From bb82d4309435f2052e98531e8d955198fb1c55ba Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Mon, 13 Oct 2025 06:09:02 -0500 Subject: wip: desktop work --- packages/desktop/src/pages/index.tsx | 313 +++++++++++++++++++++++++---------- 1 file changed, 226 insertions(+), 87 deletions(-) (limited to 'packages/desktop/src/pages') diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx index 128c6adfd..f344830a4 100644 --- a/packages/desktop/src/pages/index.tsx +++ b/packages/desktop/src/pages/index.tsx @@ -1,28 +1,30 @@ import { FileIcon, Icon, IconButton, Tooltip } from "@/ui" -import { Tabs } from "@/ui/tabs" +import * as KobalteTabs from "@kobalte/core/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 { useSync, useSDK, useLocal } from "@/context" +import type { LocalFile, TextSelection } from "@/context/local" import SessionList from "@/components/session-list" import SessionTimeline from "@/components/session-timeline" +import PromptForm, { type PromptContentPart, type PromptSubmitValue } from "@/components/prompt-form" import { createStore } from "solid-js/store" import { getDirectory, getFilename } from "@/utils" +import { Select } from "@/components/select" +import { Tabs } from "@/ui/tabs" +import { Code } from "@/components/code" export default function Page() { const local = useLocal() + const sync = useSync() + const sdk = useSDK() 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" @@ -104,95 +106,231 @@ export default function Page() { } } + const handlePromptSubmit = async (prompt: PromptSubmitValue) => { + const existingSession = local.session.active() + let session = existingSession + if (!session) { + const created = await sdk.session.create() + session = created.data ?? undefined + } + if (!session) return + local.session.setActive(session.id) + + interface SubmissionAttachment { + path: string + selection?: TextSelection + label: string + } + + const createAttachmentKey = (path: string, selection?: TextSelection) => { + if (!selection) return path + return `${path}:${selection.startLine}:${selection.startChar}:${selection.endLine}:${selection.endChar}` + } + + const formatAttachmentLabel = (path: string, selection?: TextSelection) => { + if (!selection) return getFilename(path) + return `${getFilename(path)} (${selection.startLine}-${selection.endLine})` + } + + const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path)) + + const attachments = new Map() + + const registerAttachment = (path: string, selection: TextSelection | undefined, label?: string) => { + if (!path) return + const key = createAttachmentKey(path, selection) + if (attachments.has(key)) return + attachments.set(key, { + path, + selection, + label: label ?? formatAttachmentLabel(path, selection), + }) + } + + const promptAttachments = prompt.parts.filter( + (part): part is Extract => part.kind === "attachment", + ) + + for (const part of promptAttachments) { + registerAttachment(part.path, part.selection, part.display) + } + + const activeFile = local.context.active() + if (activeFile) { + registerAttachment( + activeFile.path, + activeFile.selection, + activeFile.name ?? formatAttachmentLabel(activeFile.path, activeFile.selection), + ) + } + + for (const contextFile of local.context.all()) { + registerAttachment( + contextFile.path, + contextFile.selection, + formatAttachmentLabel(contextFile.path, contextFile.selection), + ) + } + + const attachmentParts = Array.from(attachments.values()).map((attachment) => { + const absolute = toAbsolutePath(attachment.path) + const query = attachment.selection + ? `?start=${attachment.selection.startLine}&end=${attachment.selection.endLine}` + : "" + return { + type: "file" as const, + mime: "text/plain", + url: `file://${absolute}${query}`, + filename: getFilename(attachment.path), + source: { + type: "file" as const, + text: { + value: `@${attachment.label}`, + start: 0, + end: 0, + }, + path: absolute, + }, + } + }) + + await sdk.session.prompt({ + path: { id: session.id }, + body: { + agent: local.agent.current()!.name, + model: { + modelID: local.model.current()!.id, + providerID: local.model.current()!.provider.id, + }, + parts: [ + { + type: "text", + text: prompt.text, + }, + ...attachmentParts, + ], + }, + }) + } + return (
- - - -
- - - Files - - - Changes - - -
- - - - - No changes
} - > - - - - - - - + + + {(file) => ( + + + + )} + + + + setStore("modelSelectOpen", true)} - onInputRefChange={(element: HTMLTextAreaElement | null) => { + onInputRefChange={(element: HTMLTextAreaElement | undefined) => { inputRef = element ?? undefined }} /> - - -
- }> + )} onClose={() => setStore("fileSelectOpen", false)} - onSelect={(x) => (x ? local.file.open(x, { pinned: true }) : undefined)} + onSelect={(x) => (x ? local.context.openFile(x) : undefined)} + // onSelect={(x) => (x ? local.file.open(x, { pinned: true }) : undefined)} />
-- cgit v1.2.3