diff options
Diffstat (limited to 'packages/app/src/components')
21 files changed, 200 insertions, 209 deletions
diff --git a/packages/app/src/components/dialog-connect-provider.tsx b/packages/app/src/components/dialog-connect-provider.tsx index 41225d02a..e30574379 100644 --- a/packages/app/src/components/dialog-connect-provider.tsx +++ b/packages/app/src/components/dialog-connect-provider.tsx @@ -327,7 +327,7 @@ export function DialogConnectProvider(props: { provider: string }) { if (loading()) return if (methods().length === 1) { auto = true - selectMethod(0) + void selectMethod(0) } }) @@ -373,7 +373,7 @@ export function DialogConnectProvider(props: { provider: string }) { key={(m) => m?.label} onSelect={async (selected, index) => { if (!selected) return - selectMethod(index) + void selectMethod(index) }} > {(i) => ( diff --git a/packages/app/src/components/dialog-edit-project.tsx b/packages/app/src/components/dialog-edit-project.tsx index eb962f47e..ea5d70065 100644 --- a/packages/app/src/components/dialog-edit-project.tsx +++ b/packages/app/src/components/dialog-edit-project.tsx @@ -9,7 +9,7 @@ import { createStore } from "solid-js/store" import { useGlobalSDK } from "@/context/global-sdk" import { useGlobalSync } from "@/context/global-sync" import { type LocalProject, getAvatarColors } from "@/context/layout" -import { getFilename } from "@opencode-ai/util/path" +import { getFilename } from "@opencode-ai/shared/util/path" import { Avatar } from "@opencode-ai/ui/avatar" import { useLanguage } from "@/context/language" diff --git a/packages/app/src/components/dialog-fork.tsx b/packages/app/src/components/dialog-fork.tsx index 9e1b896fa..710618c30 100644 --- a/packages/app/src/components/dialog-fork.tsx +++ b/packages/app/src/components/dialog-fork.tsx @@ -9,7 +9,7 @@ import { List } from "@opencode-ai/ui/list" import { showToast } from "@opencode-ai/ui/toast" import { extractPromptFromParts } from "@/utils/prompt" import type { TextPart as SDKTextPart } from "@opencode-ai/sdk/v2/client" -import { base64Encode } from "@opencode-ai/util/encode" +import { base64Encode } from "@opencode-ai/shared/util/encode" import { useLanguage } from "@/context/language" interface ForkableMessage { diff --git a/packages/app/src/components/dialog-select-directory.tsx b/packages/app/src/components/dialog-select-directory.tsx index 91e23f8ff..903cb1915 100644 --- a/packages/app/src/components/dialog-select-directory.tsx +++ b/packages/app/src/components/dialog-select-directory.tsx @@ -3,7 +3,7 @@ import { Dialog } from "@opencode-ai/ui/dialog" import { FileIcon } from "@opencode-ai/ui/file-icon" import { List } from "@opencode-ai/ui/list" import type { ListRef } from "@opencode-ai/ui/list" -import { getDirectory, getFilename } from "@opencode-ai/util/path" +import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" import fuzzysort from "fuzzysort" import { createMemo, createResource, createSignal } from "solid-js" import { useGlobalSDK } from "@/context/global-sdk" diff --git a/packages/app/src/components/dialog-select-file.tsx b/packages/app/src/components/dialog-select-file.tsx index e21be77fb..186906f92 100644 --- a/packages/app/src/components/dialog-select-file.tsx +++ b/packages/app/src/components/dialog-select-file.tsx @@ -4,8 +4,8 @@ import { FileIcon } from "@opencode-ai/ui/file-icon" import { Icon } from "@opencode-ai/ui/icon" import { Keybind } from "@opencode-ai/ui/keybind" import { List } from "@opencode-ai/ui/list" -import { base64Encode } from "@opencode-ai/util/encode" -import { getDirectory, getFilename } from "@opencode-ai/util/path" +import { base64Encode } from "@opencode-ai/shared/util/encode" +import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" import { useNavigate } from "@solidjs/router" import { createMemo, createSignal, Match, onCleanup, Show, Switch } from "solid-js" import { formatKeybind, useCommand, type CommandOption } from "@/context/command" @@ -348,8 +348,8 @@ export function DialogSelectFile(props: { mode?: DialogSelectFileMode; onOpenFil const open = (path: string) => { const value = file.tab(path) - tabs().open(value) - file.load(path) + void tabs().open(value) + void file.load(path) if (!view().reviewPanel.opened()) view().reviewPanel.open() layout.fileTree.setTab("all") props.onOpenFile?.(path) diff --git a/packages/app/src/components/dialog-select-server.tsx b/packages/app/src/components/dialog-select-server.tsx index ca4c42a37..dd92edec3 100644 --- a/packages/app/src/components/dialog-select-server.tsx +++ b/packages/app/src/components/dialog-select-server.tsx @@ -344,7 +344,7 @@ export function DialogSelectServer() { createEffect(() => { items() - refreshHealth() + void refreshHealth() const interval = setInterval(refreshHealth, 10_000) onCleanup(() => clearInterval(interval)) }) @@ -498,7 +498,7 @@ export function DialogSelectServer() { async function handleRemove(url: ServerConnection.Key) { server.remove(url) if ((await platform.getDefaultServer?.()) === url) { - platform.setDefaultServer?.(null) + void platform.setDefaultServer?.(null) } } @@ -536,7 +536,7 @@ export function DialogSelectServer() { items={sortedItems} key={(x) => x.http.url} onSelect={(x) => { - if (x) select(x) + if (x) void select(x) }} divider={true} class="px-5 [&_[data-slot=list-search-wrapper]]:w-full [&_[data-slot=list-scroll]]h-[300px] [&_[data-slot=list-scroll]]:overflow-y-auto [&_[data-slot=list-items]]:bg-surface-base [&_[data-slot=list-items]]:rounded-md [&_[data-slot=list-item]]:min-h-14 [&_[data-slot=list-item]]:p-3 [&_[data-slot=list-item]]:!bg-transparent" diff --git a/packages/app/src/components/file-tree.tsx b/packages/app/src/components/file-tree.tsx index 930832fb6..211ce05ef 100644 --- a/packages/app/src/components/file-tree.tsx +++ b/packages/app/src/components/file-tree.tsx @@ -14,7 +14,6 @@ import { Switch, untrack, type ComponentProps, - type JSXElement, type ParentProps, } from "solid-js" import { Dynamic } from "solid-js/web" @@ -149,7 +148,7 @@ const FileTreeNode = ( classList={{ "w-full min-w-0 h-6 flex items-center justify-start gap-x-1.5 rounded-md px-1.5 py-0 text-left hover:bg-surface-raised-base-hover active:bg-surface-base-active transition-colors cursor-pointer": true, "bg-surface-base-active": local.node.path === local.active, - ...(local.classList ?? {}), + ...local.classList, [local.class ?? ""]: !!local.class, [local.nodeClass ?? ""]: !!local.nodeClass, }} diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index eedbc91cf..156b0b3a4 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -35,7 +35,6 @@ import { useLanguage } from "@/context/language" import { usePlatform } from "@/context/platform" import { useSessionLayout } from "@/pages/session/session-layout" import { createSessionTabs } from "@/pages/session/helpers" -import { promptEnabled, promptProbe } from "@/testing/prompt" import { createTextFragment, getCursorPosition, setCursorPosition, setRangeEdge } from "./prompt-input/editor-dom" import { createPromptAttachments } from "./prompt-input/attachments" import { ACCEPTED_FILE_TYPES } from "./prompt-input/files" @@ -55,6 +54,8 @@ import { PromptImageAttachments } from "./prompt-input/image-attachments" import { PromptDragOverlay } from "./prompt-input/drag-overlay" import { promptPlaceholder } from "./prompt-input/placeholder" import { ImagePreview } from "@opencode-ai/ui/image-preview" +import { useQuery } from "@tanstack/solid-query" +import { loadAgentsQuery, loadProvidersQuery } from "@/context/global-sync/bootstrap" interface PromptInputProps { class?: string @@ -101,6 +102,7 @@ const NON_EMPTY_TEXT = /[^\s\u200B]/ export const PromptInput: Component<PromptInputProps> = (props) => { const sdk = useSDK() + const sync = useSync() const local = useLocal() const files = useFile() @@ -213,9 +215,9 @@ export const PromptInput: Component<PromptInputProps> = (props) => { if (!view().reviewPanel.opened()) view().reviewPanel.open() layout.fileTree.setTab("all") const tab = files.tab(item.path) - tabs().open(tab) + void tabs().open(tab) tabs().setActive(tab) - Promise.resolve(files.load(item.path)).finally(() => queueCommentFocus()) + void Promise.resolve(files.load(item.path)).finally(() => queueCommentFocus()) } const recent = createMemo(() => { @@ -639,7 +641,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => { const handleSlashSelect = (cmd: SlashCommand | undefined) => { if (!cmd) return - promptProbe.select(cmd.id) closePopover() const images = imageAttachments() @@ -728,21 +729,6 @@ export const PromptInput: Component<PromptInputProps> = (props) => { element?.scrollIntoView({ block: "nearest", behavior: "smooth" }) }) }) - - if (promptEnabled()) { - createEffect(() => { - promptProbe.set({ - popover: store.popover, - slash: { - active: slashActive() ?? null, - ids: slashFlat().map((cmd) => cmd.id), - }, - }) - }) - - onCleanup(() => promptProbe.clear()) - } - const selectPopoverActive = () => { if (store.popover === "at") { const items = atFlat() @@ -1156,7 +1142,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { } if (working()) { - abort() + void abort() event.preventDefault() event.stopPropagation() return @@ -1222,7 +1208,7 @@ export const PromptInput: Component<PromptInputProps> = (props) => { return } if (working()) { - abort() + void abort() event.preventDefault() } return @@ -1262,10 +1248,18 @@ export const PromptInput: Component<PromptInputProps> = (props) => { ) { return } - handleSubmit(event) + void handleSubmit(event) } } + const agentsQuery = useQuery(() => loadAgentsQuery(sdk.directory)) + const agentsLoading = () => agentsQuery.isLoading + + const globalProvidersQuery = useQuery(() => loadProvidersQuery(null)) + const providersQuery = useQuery(() => loadProvidersQuery(sdk.directory)) + + const providersLoading = () => agentsLoading() || providersQuery.isLoading || globalProvidersQuery.isLoading + return ( <div class="relative size-full _max-h-[320px] flex flex-col gap-0"> <PromptPopover @@ -1461,53 +1455,89 @@ export const PromptInput: Component<PromptInputProps> = (props) => { <span class="truncate text-13-medium text-text-strong">{language.t("prompt.mode.shell")}</span> <div class="size-4 shrink-0" /> </div> - <div class="flex items-center gap-1.5 min-w-0 flex-1"> - <div data-component="prompt-agent-control"> - <TooltipKeybind - placement="top" - gutter={4} - title={language.t("command.agent.cycle")} - keybind={command.keybind("agent.cycle")} - > - <Select - size="normal" - options={agentNames()} - current={local.agent.current()?.name ?? ""} - onSelect={(value) => { - local.agent.set(value) - restoreFocus() - }} - class="capitalize max-w-[160px] text-text-base" - valueClass="truncate text-13-regular text-text-base" - triggerStyle={control()} - triggerProps={{ "data-action": "prompt-agent" }} - variant="ghost" - /> - </TooltipKeybind> - </div> - <Show when={store.mode !== "shell"}> - <div data-component="prompt-model-control"> - <Show - when={providers.paid().length > 0} - fallback={ + <div class="flex items-center gap-1.5 min-w-0 flex-1 h-7"> + <Show when={!agentsLoading()}> + <div data-component="prompt-agent-control"> + <TooltipKeybind + placement="top" + gutter={4} + title={language.t("command.agent.cycle")} + keybind={command.keybind("agent.cycle")} + > + <Select + size="normal" + options={agentNames()} + current={local.agent.current()?.name ?? ""} + onSelect={(value) => { + local.agent.set(value) + restoreFocus() + }} + class="capitalize max-w-[160px] text-text-base" + valueClass="truncate text-13-regular text-text-base" + triggerStyle={control()} + triggerProps={{ "data-action": "prompt-agent" }} + variant="ghost" + /> + </TooltipKeybind> + </div> + </Show> + <Show when={!providersLoading()}> + <Show when={store.mode !== "shell"}> + <div data-component="prompt-model-control"> + <Show + when={providers.paid().length > 0} + fallback={ + <TooltipKeybind + placement="top" + gutter={4} + title={language.t("command.model.choose")} + keybind={command.keybind("model.choose")} + > + <Button + data-action="prompt-model" + as="div" + variant="ghost" + size="normal" + class="min-w-0 max-w-[320px] text-13-regular text-text-base group" + style={control()} + onClick={() => { + void import("@/components/dialog-select-model-unpaid").then((x) => { + dialog.show(() => <x.DialogSelectModelUnpaid model={local.model} />) + }) + }} + > + <Show when={local.model.current()?.provider?.id}> + <ProviderIcon + id={local.model.current()?.provider?.id ?? ""} + class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150" + style={{ "will-change": "opacity", transform: "translateZ(0)" }} + /> + </Show> + <span class="truncate"> + {local.model.current()?.name ?? language.t("dialog.model.select.title")} + </span> + <Icon name="chevron-down" size="small" class="shrink-0" /> + </Button> + </TooltipKeybind> + } + > <TooltipKeybind placement="top" gutter={4} title={language.t("command.model.choose")} keybind={command.keybind("model.choose")} > - <Button - data-action="prompt-model" - as="div" - variant="ghost" - size="normal" - class="min-w-0 max-w-[320px] text-13-regular text-text-base group" - style={control()} - onClick={() => { - void import("@/components/dialog-select-model-unpaid").then((x) => { - dialog.show(() => <x.DialogSelectModelUnpaid model={local.model} />) - }) + <ModelSelectorPopover + model={local.model} + triggerAs={Button} + triggerProps={{ + variant: "ghost", + size: "normal", + style: control(), + class: "min-w-0 max-w-[320px] text-13-regular text-text-base group", + "data-action": "prompt-model", }} + onClose={restoreFocus} > <Show when={local.model.current()?.provider?.id}> <ProviderIcon @@ -1520,67 +1550,35 @@ export const PromptInput: Component<PromptInputProps> = (props) => { {local.model.current()?.name ?? language.t("dialog.model.select.title")} </span> <Icon name="chevron-down" size="small" class="shrink-0" /> - </Button> + </ModelSelectorPopover> </TooltipKeybind> - } - > + </Show> + </div> + <div data-component="prompt-variant-control"> <TooltipKeybind placement="top" gutter={4} - title={language.t("command.model.choose")} - keybind={command.keybind("model.choose")} + title={language.t("command.model.variant.cycle")} + keybind={command.keybind("model.variant.cycle")} > - <ModelSelectorPopover - model={local.model} - triggerAs={Button} - triggerProps={{ - variant: "ghost", - size: "normal", - style: control(), - class: "min-w-0 max-w-[320px] text-13-regular text-text-base group", - "data-action": "prompt-model", + <Select + size="normal" + options={variants()} + current={local.model.variant.current() ?? "default"} + label={(x) => (x === "default" ? language.t("common.default") : x)} + onSelect={(value) => { + local.model.variant.set(value === "default" ? undefined : value) + restoreFocus() }} - onClose={restoreFocus} - > - <Show when={local.model.current()?.provider?.id}> - <ProviderIcon - id={local.model.current()?.provider?.id ?? ""} - class="size-4 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity duration-150" - style={{ "will-change": "opacity", transform: "translateZ(0)" }} - /> - </Show> - <span class="truncate"> - {local.model.current()?.name ?? language.t("dialog.model.select.title")} - </span> - <Icon name="chevron-down" size="small" class="shrink-0" /> - </ModelSelectorPopover> + class="capitalize max-w-[160px] text-text-base" + valueClass="truncate text-13-regular text-text-base" + triggerStyle={control()} + triggerProps={{ "data-action": "prompt-model-variant" }} + variant="ghost" + /> </TooltipKeybind> - </Show> - </div> - <div data-component="prompt-variant-control"> - <TooltipKeybind - placement="top" - gutter={4} - title={language.t("command.model.variant.cycle")} - keybind={command.keybind("model.variant.cycle")} - > - <Select - size="normal" - options={variants()} - current={local.model.variant.current() ?? "default"} - label={(x) => (x === "default" ? language.t("common.default") : x)} - onSelect={(value) => { - local.model.variant.set(value === "default" ? undefined : value) - restoreFocus() - }} - class="capitalize max-w-[160px] text-text-base" - valueClass="truncate text-13-regular text-text-base" - triggerStyle={control()} - triggerProps={{ "data-action": "prompt-model-variant" }} - variant="ghost" - /> - </TooltipKeybind> - </div> + </div> + </Show> </Show> </div> </div> diff --git a/packages/app/src/components/prompt-input/build-request-parts.ts b/packages/app/src/components/prompt-input/build-request-parts.ts index a1076e60c..c268af35e 100644 --- a/packages/app/src/components/prompt-input/build-request-parts.ts +++ b/packages/app/src/components/prompt-input/build-request-parts.ts @@ -1,4 +1,4 @@ -import { getFilename } from "@opencode-ai/util/path" +import { getFilename } from "@opencode-ai/shared/util/path" import { type AgentPartInput, type FilePartInput, type Part, type TextPartInput } from "@opencode-ai/sdk/v2/client" import type { FileSelection } from "@/context/file" import { encodeFilePath } from "@/context/file/path" diff --git a/packages/app/src/components/prompt-input/context-items.tsx b/packages/app/src/components/prompt-input/context-items.tsx index b138fe3ef..9f20f1c04 100644 --- a/packages/app/src/components/prompt-input/context-items.tsx +++ b/packages/app/src/components/prompt-input/context-items.tsx @@ -2,7 +2,7 @@ import { Component, For, Show } from "solid-js" import { FileIcon } from "@opencode-ai/ui/file-icon" import { IconButton } from "@opencode-ai/ui/icon-button" import { Tooltip } from "@opencode-ai/ui/tooltip" -import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/util/path" +import { getDirectory, getFilename, getFilenameTruncated } from "@opencode-ai/shared/util/path" import type { ContextItem } from "@/context/prompt" type PromptContextItem = ContextItem & { key: string } diff --git a/packages/app/src/components/prompt-input/slash-popover.tsx b/packages/app/src/components/prompt-input/slash-popover.tsx index 65eb01c79..0c8c95923 100644 --- a/packages/app/src/components/prompt-input/slash-popover.tsx +++ b/packages/app/src/components/prompt-input/slash-popover.tsx @@ -1,7 +1,7 @@ import { Component, For, Match, Show, Switch } from "solid-js" import { FileIcon } from "@opencode-ai/ui/file-icon" import { Icon } from "@opencode-ai/ui/icon" -import { getDirectory, getFilename } from "@opencode-ai/util/path" +import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" export type AtOption = | { type: "agent"; name: string; display: string } diff --git a/packages/app/src/components/prompt-input/submit.test.ts b/packages/app/src/components/prompt-input/submit.test.ts index 03bece2e3..cf9949723 100644 --- a/packages/app/src/components/prompt-input/submit.test.ts +++ b/packages/app/src/components/prompt-input/submit.test.ts @@ -74,7 +74,7 @@ beforeAll(async () => { showToast: () => 0, })) - mock.module("@opencode-ai/util/encode", () => ({ + mock.module("@opencode-ai/shared/util/encode", () => ({ base64Encode: (value: string) => value, })) diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts index 2a3a3d0e9..6805f619c 100644 --- a/packages/app/src/components/prompt-input/submit.ts +++ b/packages/app/src/components/prompt-input/submit.ts @@ -1,9 +1,9 @@ import type { Message, Session } from "@opencode-ai/sdk/v2/client" import { showToast } from "@opencode-ai/ui/toast" -import { base64Encode } from "@opencode-ai/util/encode" -import { Binary } from "@opencode-ai/util/binary" +import { base64Encode } from "@opencode-ai/shared/util/encode" +import { Binary } from "@opencode-ai/shared/util/binary" import { useNavigate, useParams } from "@solidjs/router" -import type { Accessor } from "solid-js" +import { batch, type Accessor } from "solid-js" import type { FileSelection } from "@/context/file" import { useGlobalSync } from "@/context/global-sync" import { useLanguage } from "@/context/language" @@ -13,7 +13,6 @@ import { usePermission } from "@/context/permission" import { type ContextItem, type ImageAttachmentPart, type Prompt, usePrompt } from "@/context/prompt" import { useSDK } from "@/context/sdk" import { useSync } from "@/context/sync" -import { promptProbe } from "@/testing/prompt" import { Identifier } from "@/utils/id" import { Worktree as WorktreeState } from "@/utils/worktree" import { buildRequestParts } from "./build-request-parts" @@ -139,13 +138,17 @@ export async function sendFollowupDraft(input: FollowupSendInput) { messageID, }) - setBusy() - add() + batch(() => { + setBusy() + add() + }) try { if (!(await wait())) { - setIdle() - remove() + batch(() => { + setIdle() + remove() + }) return false } @@ -159,8 +162,10 @@ export async function sendFollowupDraft(input: FollowupSendInput) { }) return true } catch (err) { - setIdle() - remove() + batch(() => { + setIdle() + remove() + }) throw err } } @@ -290,7 +295,7 @@ export function createPromptSubmit(input: PromptSubmitInput) { const mode = input.mode() if (text.trim().length === 0 && images.length === 0 && input.commentCount() === 0) { - if (input.working()) abort() + if (input.working()) void abort() return } @@ -307,7 +312,6 @@ export function createPromptSubmit(input: PromptSubmitInput) { input.addToHistory(currentPrompt, mode) input.resetHistoryNavigation() - promptProbe.start() const projectDirectory = sdk.directory const isNewSession = !params.id @@ -427,7 +431,6 @@ export function createPromptSubmit(input: PromptSubmitInput) { return } - promptProbe.submit({ sessionID: session.id, directory: sessionDirectory }) input.onSubmit?.() if (mode === "shell") { diff --git a/packages/app/src/components/session-context-usage.tsx b/packages/app/src/components/session-context-usage.tsx index d7c249ab0..6b7fe4ef7 100644 --- a/packages/app/src/components/session-context-usage.tsx +++ b/packages/app/src/components/session-context-usage.tsx @@ -24,7 +24,7 @@ function openSessionContext(args: { }) { if (!args.view.reviewPanel.opened()) args.view.reviewPanel.open() if (args.layout.fileTree.opened() && args.layout.fileTree.tab() !== "all") args.layout.fileTree.setTab("all") - args.tabs.open("context") + void args.tabs.open("context") args.tabs.setActive("context") } diff --git a/packages/app/src/components/session/session-context-tab.tsx b/packages/app/src/components/session/session-context-tab.tsx index 4e7dc8e78..abf4c9334 100644 --- a/packages/app/src/components/session/session-context-tab.tsx +++ b/packages/app/src/components/session/session-context-tab.tsx @@ -1,8 +1,8 @@ import { createMemo, createEffect, on, onCleanup, For, Show } from "solid-js" import type { JSX } from "solid-js" import { useSync } from "@/context/sync" -import { checksum } from "@opencode-ai/util/encode" -import { findLast } from "@opencode-ai/util/array" +import { checksum } from "@opencode-ai/shared/util/encode" +import { findLast } from "@opencode-ai/shared/util/array" import { same } from "@/utils/same" import { Icon } from "@opencode-ai/ui/icon" import { Accordion } from "@opencode-ai/ui/accordion" diff --git a/packages/app/src/components/session/session-header.tsx b/packages/app/src/components/session/session-header.tsx index 495b32340..7acfdfc37 100644 --- a/packages/app/src/components/session/session-header.tsx +++ b/packages/app/src/components/session/session-header.tsx @@ -7,8 +7,8 @@ import { Keybind } from "@opencode-ai/ui/keybind" import { Spinner } from "@opencode-ai/ui/spinner" import { showToast } from "@opencode-ai/ui/toast" import { Tooltip, TooltipKeybind } from "@opencode-ai/ui/tooltip" -import { getFilename } from "@opencode-ai/util/path" -import { createEffect, createMemo, For, onCleanup, Show } from "solid-js" +import { getFilename } from "@opencode-ai/shared/util/path" +import { createEffect, createMemo, For, Show } from "solid-js" import { createStore } from "solid-js/store" import { Portal } from "solid-js/web" import { useCommand } from "@/context/command" diff --git a/packages/app/src/components/session/session-new-view.tsx b/packages/app/src/components/session/session-new-view.tsx index e4ef36393..d2cac28fc 100644 --- a/packages/app/src/components/session/session-new-view.tsx +++ b/packages/app/src/components/session/session-new-view.tsx @@ -5,7 +5,7 @@ import { useSDK } from "@/context/sdk" import { useLanguage } from "@/context/language" import { Icon } from "@opencode-ai/ui/icon" import { Mark } from "@opencode-ai/ui/logo" -import { getDirectory, getFilename } from "@opencode-ai/util/path" +import { getDirectory, getFilename } from "@opencode-ai/shared/util/path" const MAIN_WORKTREE = "main" const CREATE_WORKTREE = "create" diff --git a/packages/app/src/components/session/session-sortable-tab.tsx b/packages/app/src/components/session/session-sortable-tab.tsx index dfda91c16..fb2275c44 100644 --- a/packages/app/src/components/session/session-sortable-tab.tsx +++ b/packages/app/src/components/session/session-sortable-tab.tsx @@ -5,7 +5,7 @@ import { FileIcon } from "@opencode-ai/ui/file-icon" import { IconButton } from "@opencode-ai/ui/icon-button" import { TooltipKeybind } from "@opencode-ai/ui/tooltip" import { Tabs } from "@opencode-ai/ui/tabs" -import { getFilename } from "@opencode-ai/util/path" +import { getFilename } from "@opencode-ai/shared/util/path" import { useFile } from "@/context/file" import { useLanguage } from "@/context/language" import { useCommand } from "@/context/command" diff --git a/packages/app/src/components/session/session-sortable-terminal-tab.tsx b/packages/app/src/components/session/session-sortable-terminal-tab.tsx index ba697f91a..2d88ed180 100644 --- a/packages/app/src/components/session/session-sortable-terminal-tab.tsx +++ b/packages/app/src/components/session/session-sortable-terminal-tab.tsx @@ -44,7 +44,7 @@ export function SortableTerminalTab(props: { terminal: LocalPTY; onClose?: () => const close = () => { const count = terminal.all().length - terminal.close(props.terminal.id) + void terminal.close(props.terminal.id) if (count === 1) { props.onClose?.() } diff --git a/packages/app/src/components/terminal.tsx b/packages/app/src/components/terminal.tsx index c8430d8bb..57e91d6d3 100644 --- a/packages/app/src/components/terminal.tsx +++ b/packages/app/src/components/terminal.tsx @@ -13,7 +13,6 @@ import { useSDK } from "@/context/sdk" import { useServer } from "@/context/server" import { monoFontFamily, useSettings } from "@/context/settings" import type { LocalPTY } from "@/context/terminal" -import { terminalAttr, terminalProbe } from "@/testing/terminal" import { disposeIfDisposable, getHoveredLinkText, setOptionIfSupported } from "@/utils/runtime-adapters" import { terminalWriter } from "@/utils/terminal-writer" @@ -178,7 +177,6 @@ export const Terminal = (props: TerminalProps) => { let container!: HTMLDivElement const [local, others] = splitProps(props, ["pty", "class", "classList", "autoFocus", "onConnect", "onConnectError"]) const id = local.pty.id - const probe = terminalProbe(id) const restore = typeof local.pty.buffer === "string" ? local.pty.buffer : "" const restoreSize = restore && @@ -193,7 +191,7 @@ export const Terminal = (props: TerminalProps) => { const scrollY = typeof local.pty.scrollY === "number" ? local.pty.scrollY : undefined let ws: WebSocket | undefined let term: Term | undefined - let ghostty: Ghostty + let _ghostty: Ghostty let serializeAddon: SerializeAddon let fitAddon: FitAddon let handleResize: () => void @@ -349,9 +347,6 @@ export const Terminal = (props: TerminalProps) => { } onMount(() => { - probe.init() - cleanups.push(() => probe.drop()) - const run = async () => { const loaded = await loadGhostty() if (disposed) return @@ -377,12 +372,10 @@ export const Terminal = (props: TerminalProps) => { cleanup() return } - ghostty = g + _ghostty = g term = t output = terminalWriter((data, done) => t.write(data, () => { - probe.render(data) - probe.settle() done?.() }), ) @@ -422,7 +415,7 @@ export const Terminal = (props: TerminalProps) => { if (local.autoFocus !== false) focusTerminal() if (typeof document !== "undefined" && document.fonts) { - document.fonts.ready.then(scheduleFit) + void document.fonts.ready.then(scheduleFit) } const onResize = t.onResize((size) => { @@ -534,7 +527,6 @@ export const Terminal = (props: TerminalProps) => { const handleOpen = () => { if (disposed) return tries = 0 - probe.connect() local.onConnect?.() scheduleSize(t.cols, t.rows) } @@ -599,13 +591,6 @@ export const Terminal = (props: TerminalProps) => { socket.addEventListener("close", handleClose) } - probe.control({ - disconnect: () => { - if (!ws) return - ws.close(4_000, "e2e") - }, - }) - open() } @@ -645,12 +630,11 @@ export const Terminal = (props: TerminalProps) => { <div ref={container} data-component="terminal" - {...{ [terminalAttr]: id }} data-prevent-autofocus tabIndex={-1} style={{ "background-color": terminalColors().background }} classList={{ - ...(local.classList ?? {}), + ...local.classList, "select-text": true, "size-full px-6 py-3 font-mono relative overflow-hidden": true, [local.class ?? ""]: !!local.class, diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx index 0a41f3119..b7edb85ed 100644 --- a/packages/app/src/components/titlebar.tsx +++ b/packages/app/src/components/titlebar.tsx @@ -1,4 +1,4 @@ -import { createEffect, createMemo, onCleanup, Show, untrack } from "solid-js" +import { createEffect, createMemo, Show, untrack } from "solid-js" import { createStore } from "solid-js/store" import { useLocation, useNavigate, useParams } from "@solidjs/router" import { IconButton } from "@opencode-ai/ui/icon-button" @@ -252,41 +252,48 @@ export function Titlebar() { </div> </div> </Show> - <Show when={hasProjects()}> - <div - class="flex items-center gap-0 transition-transform" - classList={{ - "translate-x-0": !layout.sidebar.opened(), - "-translate-x-[36px]": layout.sidebar.opened(), - "duration-180 ease-out": !layout.sidebar.opened(), - "duration-180 ease-in": layout.sidebar.opened(), - }} - > - <Tooltip placement="bottom" value={language.t("common.goBack")} openDelay={2000}> - <Button - variant="ghost" - icon="chevron-left" - class="titlebar-icon w-6 h-6 p-0 box-border" - disabled={!canBack()} - onClick={back} - aria-label={language.t("common.goBack")} - /> - </Tooltip> - <Tooltip placement="bottom" value={language.t("common.goForward")} openDelay={2000}> - <Button - variant="ghost" - icon="chevron-right" - class="titlebar-icon w-6 h-6 p-0 box-border" - disabled={!canForward()} - onClick={forward} - aria-label={language.t("common.goForward")} - /> - </Tooltip> - </div> - </Show> + <div + class="flex items-center shrink-0" + classList={{ + "translate-x-0": !layout.sidebar.opened(), + "-translate-x-[36px]": layout.sidebar.opened(), + "duration-180 ease-out": !layout.sidebar.opened(), + "duration-180 ease-in": layout.sidebar.opened(), + }} + > + <Show when={hasProjects()}> + <div class="flex items-center gap-0 transition-transform"> + <Tooltip placement="bottom" value={language.t("common.goBack")} openDelay={2000}> + <Button + variant="ghost" + icon="chevron-left" + class="titlebar-icon w-6 h-6 p-0 box-border" + disabled={!canBack()} + onClick={back} + aria-label={language.t("common.goBack")} + /> + </Tooltip> + <Tooltip placement="bottom" value={language.t("common.goForward")} openDelay={2000}> + <Button + variant="ghost" + icon="chevron-right" + class="titlebar-icon w-6 h-6 p-0 box-border" + disabled={!canForward()} + onClick={forward} + aria-label={language.t("common.goForward")} + /> + </Tooltip> + </div> + </Show> + <div id="opencode-titlebar-left" class="flex items-center gap-3 min-w-0 px-2" /> + {["beta", "dev"].includes(import.meta.env.VITE_OPENCODE_CHANNEL) && ( + <div class="bg-icon-interactive-base text-[#FFF] font-medium px-2 rounded-sm uppercase font-mono"> + {import.meta.env.VITE_OPENCODE_CHANNEL.toUpperCase()} + </div> + )} + </div> </div> </div> - <div id="opencode-titlebar-left" class="flex items-center gap-3 min-w-0 px-2" /> </div> <div class="min-w-0 flex items-center justify-center pointer-events-none"> |
