diff options
| author | Adam <[email protected]> | 2025-10-03 09:04:28 -0500 |
|---|---|---|
| committer | Adam <[email protected]> | 2025-10-03 09:04:28 -0500 |
| commit | 3fa280d21878ae391674a21758199df3d2d8c3b5 (patch) | |
| tree | f70c6ecafffeecc8e7a59dc9acef66c59a9ea54a /packages/app/src/pages | |
| parent | 1d58b5548287a3e32ffce3abdcf0f2db08fdb155 (diff) | |
| download | opencode-3fa280d21878ae391674a21758199df3d2d8c3b5.tar.gz opencode-3fa280d21878ae391674a21758199df3d2d8c3b5.zip | |
chore: app -> desktop
Diffstat (limited to 'packages/app/src/pages')
| -rw-r--r-- | packages/app/src/pages/index.tsx | 278 | ||||
| -rw-r--r-- | packages/app/src/pages/layout.tsx | 5 |
2 files changed, 0 insertions, 283 deletions
diff --git a/packages/app/src/pages/index.tsx b/packages/app/src/pages/index.tsx deleted file mode 100644 index 128c6adfd..000000000 --- a/packages/app/src/pages/index.tsx +++ /dev/null @@ -1,278 +0,0 @@ -import { FileIcon, Icon, IconButton, Tooltip } from "@/ui" -import { Tabs } from "@/ui/tabs" -import FileTree from "@/components/file-tree" -import EditorPane from "@/components/editor-pane" -import { For, Match, onCleanup, onMount, Show, Switch } from "solid-js" -import { SelectDialog } from "@/components/select-dialog" -import { useLocal } from "@/context" -import { ResizeableLayout, ResizeablePane } from "@/components/resizeable-pane" -import type { LocalFile } from "@/context/local" -import SessionList from "@/components/session-list" -import SessionTimeline from "@/components/session-timeline" -import { createStore } from "solid-js/store" -import { getDirectory, getFilename } from "@/utils" - -export default function Page() { - const local = useLocal() - const [store, setStore] = createStore({ - clickTimer: undefined as number | undefined, - modelSelectOpen: false, - fileSelectOpen: false, - }) - - const layoutKey = "workspace" - const timelinePane = "timeline" - - let inputRef: HTMLTextAreaElement | undefined = undefined - - const MOD = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control" - - onMount(() => { - document.addEventListener("keydown", handleKeyDown) - }) - - onCleanup(() => { - document.removeEventListener("keydown", handleKeyDown) - }) - - const handleKeyDown = (event: KeyboardEvent) => { - if (event.getModifierState(MOD) && event.shiftKey && event.key.toLowerCase() === "p") { - event.preventDefault() - // TODO: command palette - return - } - if (event.getModifierState(MOD) && event.key.toLowerCase() === "p") { - event.preventDefault() - setStore("fileSelectOpen", true) - return - } - - const focused = document.activeElement === inputRef - if (focused) { - if (event.key === "Escape") { - inputRef?.blur() - } - return - } - - if (local.file.active()) { - const active = local.file.active()! - if (event.key === "Enter" && active.selection) { - local.context.add({ - type: "file", - path: active.path, - selection: { ...active.selection }, - }) - return - } - - if (event.getModifierState(MOD)) { - if (event.key.toLowerCase() === "a") { - return - } - if (event.key.toLowerCase() === "c") { - return - } - } - } - - if (event.key.length === 1 && event.key !== "Unidentified") { - inputRef?.focus() - } - } - - const resetClickTimer = () => { - if (!store.clickTimer) return - clearTimeout(store.clickTimer) - setStore("clickTimer", undefined) - } - - const startClickTimer = () => { - const newClickTimer = setTimeout(() => { - setStore("clickTimer", undefined) - }, 300) - setStore("clickTimer", newClickTimer as unknown as number) - } - - const handleFileClick = async (file: LocalFile) => { - if (store.clickTimer) { - resetClickTimer() - local.file.update(file.path, { ...file, pinned: true }) - } else { - local.file.open(file.path) - startClickTimer() - } - } - - return ( - <div class="relative"> - <ResizeableLayout - id={layoutKey} - defaults={{ - explorer: { size: 24, visible: true }, - editor: { size: 56, visible: true }, - timeline: { size: 20, visible: false }, - }} - class="h-screen" - > - <ResizeablePane - id="explorer" - minSize="150px" - maxSize="300px" - class="border-r border-border-subtle/30 bg-background z-10 overflow-hidden" - > - <Tabs class="relative flex flex-col h-full" defaultValue="files"> - <div class="sticky top-0 shrink-0 flex"> - <Tabs.List class="grow w-full after:hidden"> - <Tabs.Trigger value="files" class="flex-1 justify-center text-xs"> - Files - </Tabs.Trigger> - <Tabs.Trigger value="changes" class="flex-1 justify-center text-xs"> - Changes - </Tabs.Trigger> - </Tabs.List> - </div> - <Tabs.Content value="files" class="grow min-h-0 py-2 bg-background"> - <FileTree path="" onFileClick={handleFileClick} /> - </Tabs.Content> - <Tabs.Content value="changes" class="grow min-h-0 py-2 bg-background"> - <Show - when={local.file.changes().length} - fallback={<div class="px-2 text-xs text-text-muted">No changes</div>} - > - <ul class=""> - <For each={local.file.changes()}> - {(path) => ( - <li> - <button - onClick={() => local.file.open(path, { view: "diff-unified", pinned: true })} - class="w-full flex items-center px-2 py-0.5 gap-x-2 text-text-muted grow min-w-0 cursor-pointer hover:bg-background-element" - > - <FileIcon node={{ path, type: "file" }} class="shrink-0 size-3" /> - <span class="text-xs text-text whitespace-nowrap">{getFilename(path)}</span> - <span class="text-xs text-text-muted/60 whitespace-nowrap truncate min-w-0"> - {getDirectory(path)} - </span> - </button> - </li> - )} - </For> - </ul> - </Show> - </Tabs.Content> - </Tabs> - </ResizeablePane> - <ResizeablePane id="editor" minSize={30} maxSize={80} class="bg-background"> - <EditorPane - layoutKey={layoutKey} - timelinePane={timelinePane} - onFileClick={handleFileClick} - onOpenModelSelect={() => setStore("modelSelectOpen", true)} - onInputRefChange={(element: HTMLTextAreaElement | null) => { - inputRef = element ?? undefined - }} - /> - </ResizeablePane> - <ResizeablePane - id="timeline" - minSize={20} - maxSize={40} - class="border-l border-border-subtle/30 bg-background z-10 overflow-hidden" - > - <div class="relative flex-1 min-h-0 overflow-y-auto overflow-x-hidden"> - <Show when={local.session.active()} fallback={<SessionList />}> - {(activeSession) => ( - <div class="relative"> - <div class="sticky top-0 bg-background z-50 px-2 h-8 border-b border-border-subtle/30"> - <div class="h-full flex items-center gap-2"> - <IconButton - size="xs" - variant="ghost" - onClick={() => local.session.clearActive()} - class="text-text-muted hover:text-text" - > - <Icon name="arrow-left" size={14} /> - </IconButton> - <h2 class="text-sm font-medium text-text truncate"> - {activeSession().title || "Untitled Session"} - </h2> - </div> - </div> - <SessionTimeline session={activeSession().id} /> - </div> - )} - </Show> - </div> - </ResizeablePane> - </ResizeableLayout> - <Show when={store.modelSelectOpen}> - <SelectDialog - key={(x) => `${x.provider.id}:${x.id}`} - items={local.model.list()} - current={local.model.current()} - render={(i) => ( - <div class="w-full flex items-center justify-between"> - <div class="flex items-center gap-x-2 text-text-muted grow min-w-0"> - <img src={`https://models.dev/logos/${i.provider.id}.svg`} class="size-4 invert opacity-40" /> - <span class="text-xs text-text whitespace-nowrap">{i.name}</span> - <span class="text-xs text-text-muted/80 whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0"> - {i.id} - </span> - </div> - <div class="flex items-center gap-x-1 text-text-muted/40 shrink-0"> - <Tooltip forceMount={false} value="Reasoning"> - <Icon name="brain" size={16} classList={{ "text-accent": i.reasoning }} /> - </Tooltip> - <Tooltip forceMount={false} value="Tools"> - <Icon name="hammer" size={16} classList={{ "text-secondary": i.tool_call }} /> - </Tooltip> - <Tooltip forceMount={false} value="Attachments"> - <Icon name="photo" size={16} classList={{ "text-success": i.attachment }} /> - </Tooltip> - <div class="rounded-full bg-text-muted/20 text-text-muted/80 w-9 h-4 flex items-center justify-center text-[10px]"> - {new Intl.NumberFormat("en-US", { - notation: "compact", - compactDisplay: "short", - }).format(i.limit.context)} - </div> - <Tooltip forceMount={false} value={`$${i.cost?.input}/1M input, $${i.cost?.output}/1M output`}> - <div class="rounded-full bg-success/20 text-success/80 w-9 h-4 flex items-center justify-center text-[10px]"> - <Switch fallback="FREE"> - <Match when={i.cost?.input > 10}>$$$</Match> - <Match when={i.cost?.input > 1}>$$</Match> - <Match when={i.cost?.input > 0.1}>$</Match> - </Switch> - </div> - </Tooltip> - </div> - </div> - )} - filter={["provider.name", "name", "id"]} - groupBy={(x) => x.provider.name} - onClose={() => setStore("modelSelectOpen", false)} - onSelect={(x) => local.model.set(x ? { modelID: x.id, providerID: x.provider.id } : undefined)} - /> - </Show> - <Show when={store.fileSelectOpen}> - <SelectDialog - items={local.file.search} - key={(x) => x} - render={(i) => ( - <div class="w-full flex items-center justify-between"> - <div class="flex items-center gap-x-2 text-text-muted grow min-w-0"> - <FileIcon node={{ path: i, type: "file" }} class="shrink-0 size-4" /> - <span class="text-xs text-text whitespace-nowrap">{getFilename(i)}</span> - <span class="text-xs text-text-muted/80 whitespace-nowrap overflow-hidden overflow-ellipsis truncate min-w-0"> - {getDirectory(i)} - </span> - </div> - <div class="flex items-center gap-x-1 text-text-muted/40 shrink-0"></div> - </div> - )} - onClose={() => setStore("fileSelectOpen", false)} - onSelect={(x) => (x ? local.file.open(x, { pinned: true }) : undefined)} - /> - </Show> - </div> - ) -} diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx deleted file mode 100644 index d2e33a2ca..000000000 --- a/packages/app/src/pages/layout.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { type ParentProps } from "solid-js" - -export default function Layout(props: ParentProps) { - return <main class="">{props.children}</main> -} |
