From d525fbf82940442d8d47265b7d7d0a9af8c282bc Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:55:31 -0600 Subject: feat(desktop): session router, interrupt agent, visual cleanup --- .../desktop/src/components/message-progress.tsx | 11 +- packages/desktop/src/components/prompt-input.tsx | 223 ++++-- packages/desktop/src/context/local.tsx | 176 +---- packages/desktop/src/context/session.tsx | 213 +++++ packages/desktop/src/context/sync.tsx | 14 +- packages/desktop/src/index.tsx | 10 +- packages/desktop/src/pages/index.tsx | 857 --------------------- packages/desktop/src/pages/layout.tsx | 75 ++ packages/desktop/src/pages/session-layout.tsx | 12 + packages/desktop/src/pages/session.tsx | 693 +++++++++++++++++ 10 files changed, 1183 insertions(+), 1101 deletions(-) create mode 100644 packages/desktop/src/context/session.tsx delete mode 100644 packages/desktop/src/pages/index.tsx create mode 100644 packages/desktop/src/pages/layout.tsx create mode 100644 packages/desktop/src/pages/session-layout.tsx create mode 100644 packages/desktop/src/pages/session.tsx (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/components/message-progress.tsx b/packages/desktop/src/components/message-progress.tsx index c0037f57c..a9be2ae5e 100644 --- a/packages/desktop/src/components/message-progress.tsx +++ b/packages/desktop/src/components/message-progress.tsx @@ -70,9 +70,8 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa const lastPart = createMemo(() => resolvedParts().slice(-1)?.at(0)) const rawStatus = createMemo(() => { - const defaultStatus = "Working..." const last = lastPart() - if (!last) return defaultStatus + if (!last) return undefined if (last.type === "tool") { switch (last.tool) { @@ -102,7 +101,7 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa } else if (last.type === "text") { return "Gathering thoughts..." } - return defaultStatus + return undefined }) const [status, setStatus] = createSignal(rawStatus()) @@ -111,11 +110,11 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa createEffect(() => { const newStatus = rawStatus() - if (newStatus === status()) return + if (newStatus === status() || !newStatus) return const timeSinceLastChange = Date.now() - lastStatusChange - if (timeSinceLastChange >= 1000) { + if (timeSinceLastChange >= 1500) { setStatus(newStatus) lastStatusChange = Date.now() if (statusTimeout) { @@ -145,7 +144,7 @@ export function MessageProgress(props: { assistantMessages: () => AssistantMessa {/* )} */} {/* */}
- {status()} + {status() ?? "Considering next steps..."}
0}>
void class?: string ref?: (el: HTMLDivElement) => void } export const PromptInput: Component = (props) => { + const navigate = useNavigate() + const sdk = useSDK() + const sync = useSync() const local = useLocal() + const session = useSession() let editorRef!: HTMLDivElement - const defaultParts = [{ type: "text", content: "", start: 0, end: 0 } as const] const [store, setStore] = createStore<{ - contentParts: ContentPart[] popoverIsOpen: boolean }>({ - contentParts: defaultParts, popoverIsOpen: false, }) - const isEmpty = createMemo(() => isEqual(store.contentParts, defaultParts)) + createEffect(() => { + session.id + editorRef.focus() + }) + const isFocused = createFocusSignal(() => editorRef) const handlePaste = (event: ClipboardEvent) => { @@ -71,14 +61,16 @@ export const PromptInput: Component = (props) => { } }) + const handleFileSelect = (path: string | undefined) => { + if (!path) return + addPart({ type: "file", path, content: "@" + getFilename(path), start: 0, end: 0 }) + setStore("popoverIsOpen", false) + } + const { flat, active, onInput, onKeyDown, refetch } = useFilteredList({ items: local.file.searchFilesAndDirectories, key: (x) => x, - onSelect: (path) => { - if (!path) return - addPart({ type: "file", path, content: "@" + getFilename(path), start: 0, end: 0 }) - setStore("popoverIsOpen", false) - }, + onSelect: handleFileSelect, }) createEffect(() => { @@ -88,10 +80,10 @@ export const PromptInput: Component = (props) => { createEffect( on( - () => store.contentParts, + () => session.prompt.current(), (currentParts) => { const domParts = parseFromDOM() - if (isEqual(currentParts, domParts)) return + if (isPromptEqual(currentParts, domParts)) return const selection = window.getSelection() let cursorPosition: number | null = null @@ -122,8 +114,18 @@ export const PromptInput: Component = (props) => { ), ) - const parseFromDOM = (): ContentPart[] => { - const newParts: ContentPart[] = [] + createEffect( + on( + () => session.prompt.cursor(), + (cursor) => { + if (cursor === undefined) return + queueMicrotask(() => setCursorPosition(editorRef, cursor)) + }, + ), + ) + + const parseFromDOM = (): Prompt => { + const newParts: Prompt = [] let position = 0 editorRef.childNodes.forEach((node) => { if (node.nodeType === Node.TEXT_NODE) { @@ -150,7 +152,7 @@ export const PromptInput: Component = (props) => { } } }) - if (newParts.length === 0) newParts.push(...defaultParts) + if (newParts.length === 0) newParts.push(...DEFAULT_PROMPT) return newParts } @@ -167,12 +169,15 @@ export const PromptInput: Component = (props) => { setStore("popoverIsOpen", false) } - setStore("contentParts", rawParts) + session.prompt.set(rawParts, cursorPosition) } const addPart = (part: ContentPart) => { const cursorPosition = getCursorPosition(editorRef) - const rawText = store.contentParts.map((p) => p.content).join("") + const rawText = session.prompt + .current() + .map((p) => p.content) + .join("") const textBeforeCursor = rawText.substring(0, cursorPosition) const atMatch = textBeforeCursor.match(/@(\S*)$/) @@ -198,7 +203,7 @@ export const PromptInput: Component = (props) => { parts: nextParts, inserted, cursorPositionAfter, - } = store.contentParts.reduce( + } = session.prompt.current().reduce( (acc, item) => { if (acc.inserted) { acc.parts.push({ ...item, start: acc.runningIndex, end: acc.runningIndex + item.content.length }) @@ -257,7 +262,7 @@ export const PromptInput: Component = (props) => { ) if (!inserted) { - const baseParts = store.contentParts.filter((item) => !(item.type === "text" && item.content === "")) + const baseParts = session.prompt.current().filter((item) => !(item.type === "text" && item.content === "")) const runningIndex = baseParts.reduce((sum, p) => sum + p.content.length, 0) const appendedAcc = { parts: [...baseParts] as ContentPart[], runningIndex } if (part.type === "text") { @@ -270,20 +275,27 @@ export const PromptInput: Component = (props) => { end: appendedAcc.runningIndex + part.content.length, }) } - const next = appendedAcc.parts.length > 0 ? appendedAcc.parts : defaultParts - setStore("contentParts", next) - setStore("popoverIsOpen", false) + const next = appendedAcc.parts.length > 0 ? appendedAcc.parts : DEFAULT_PROMPT const nextCursor = rawText.length + part.content.length + session.prompt.set(next, nextCursor) + setStore("popoverIsOpen", false) queueMicrotask(() => setCursorPosition(editorRef, nextCursor)) return } - setStore("contentParts", nextParts) + session.prompt.set(nextParts, cursorPositionAfter) setStore("popoverIsOpen", false) queueMicrotask(() => setCursorPosition(editorRef, cursorPositionAfter)) } + const abort = () => + sdk.client.session.abort({ + path: { + id: session.id!, + }, + }) + const handleKeyDown = (event: KeyboardEvent) => { if (store.popoverIsOpen && (event.key === "ArrowUp" || event.key === "ArrowDown" || event.key === "Enter")) { onKeyDown(event) @@ -293,14 +305,101 @@ export const PromptInput: Component = (props) => { if (event.key === "Enter" && !event.shiftKey) { handleSubmit(event) } + if (event.key === "Escape") { + if (store.popoverIsOpen) { + setStore("popoverIsOpen", false) + } else if (session.working()) { + abort() + } + } } - const handleSubmit = (event: Event) => { + const handleSubmit = async (event: Event) => { event.preventDefault() - if (store.contentParts.length > 0) { - props.onSubmit([...store.contentParts]) - setStore("contentParts", defaultParts) + const text = session.prompt + .current() + .map((part) => part.content) + .join("") + if (text.trim().length === 0) { + if (session.working()) abort() + return } + + let existing = session.info() + if (!existing) { + const created = await sdk.client.session.create() + existing = created.data ?? undefined + } + if (!existing) return + + navigate(`/session/${existing.id}`) + if (!session.id) { + // session.layout.setOpenedTabs( + // session.layout.copyTabs("", session.id) + } + session.layout.setActiveTab(undefined) + const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path)) + + const attachments = session.prompt.current().filter((part) => part.type === "file") + + // 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 = attachments.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.content, + start: attachment.start, + end: attachment.end, + }, + path: absolute, + }, + } + }) + + session.prompt.set(DEFAULT_PROMPT, 0) + + await sdk.client.session.prompt({ + path: { id: existing.id }, + body: { + agent: local.agent.current()!.name, + model: { + modelID: local.model.current()!.id, + providerID: local.model.current()!.provider.id, + }, + parts: [ + { + type: "text", + text, + }, + ...attachmentParts, + ], + }, + }) } return ( @@ -310,11 +409,12 @@ export const PromptInput: Component = (props) => { 0} fallback={
No matching files
}> {(i) => ( -
handleFileSelect(i)} >
@@ -326,7 +426,7 @@ export const PromptInput: Component = (props) => {
-
+ )}
@@ -354,7 +454,7 @@ export const PromptInput: Component = (props) => { "[&>[data-type=file]]:text-icon-info-active": true, }} /> - +
Plan and build anything
@@ -419,29 +519,18 @@ export const PromptInput: Component = (props) => { )} - + ) } -function isEqual(arrA: ContentPart[], arrB: ContentPart[]): boolean { - if (arrA.length !== arrB.length) return false - for (let i = 0; i < arrA.length; i++) { - const partA = arrA[i] - const partB = arrB[i] - if (partA.type !== partB.type) return false - if (partA.type === "text" && partA.content !== (partB as TextPart).content) { - return false - } - if (partA.type === "file" && partA.path !== (partB as FileAttachmentPart).path) { - return false - } - } - return true -} - function getCursorPosition(parent: HTMLElement): number { const selection = window.getSelection() if (!selection || selection.rangeCount === 0) return 0 diff --git a/packages/desktop/src/context/local.tsx b/packages/desktop/src/context/local.tsx index 8fc05c452..cef6c5555 100644 --- a/packages/desktop/src/context/local.tsx +++ b/packages/desktop/src/context/local.tsx @@ -195,18 +195,10 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ const file = (() => { const [store, setStore] = createStore<{ node: Record - // opened: string[] - // active?: string }>({ node: Object.fromEntries(sync.data.node.map((x) => [x.path, x])), - // opened: [], }) - // const active = createMemo(() => { - // if (!store.active) return undefined - // return store.node[store.active] - // }) - // const opened = createMemo(() => store.opened.map((x) => store.node[x])) const changeset = createMemo(() => new Set(sync.data.changes.map((f) => f.path))) const changes = createMemo(() => Array.from(changeset()).sort((a, b) => a.localeCompare(b))) @@ -247,18 +239,18 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ return false } - const resetNode = (path: string) => { - setStore("node", path, { - loaded: undefined, - pinned: undefined, - content: undefined, - selection: undefined, - scrollTop: undefined, - folded: undefined, - view: undefined, - selectedChange: undefined, - }) - } + // const resetNode = (path: string) => { + // setStore("node", path, { + // loaded: undefined, + // pinned: undefined, + // content: undefined, + // selection: undefined, + // scrollTop: undefined, + // folded: undefined, + // view: undefined, + // selectedChange: undefined, + // }) + // } const relative = (path: string) => path.replace(sync.data.path.directory + "/", "") @@ -333,31 +325,21 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ sdk.event.listen((e) => { const event = e.details switch (event.type) { - case "message.part.updated": - const part = event.properties.part - if (part.type === "tool" && part.state.status === "completed") { - switch (part.tool) { - case "read": - break - case "edit": - // load(part.state.input["filePath"] as string) - break - default: - break - } - } - break case "file.watcher.updated": - // setTimeout(sync.load.changes, 1000) - // const relativePath = relative(event.properties.file) - // if (relativePath.startsWith(".git/")) return - // load(relativePath) + const relativePath = relative(event.properties.file) + if (relativePath.startsWith(".git/")) return + load(relativePath) break } }) return { - node: (path: string) => store.node[path], + node: async (path: string) => { + if (!store.node[path]) { + await init(path) + } + return store.node[path] + }, update: (path: string, node: LocalFile) => setStore("node", path, reconcile(node)), open, load, @@ -417,121 +399,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ searchFiles, searchFilesAndDirectories, relative, - // active, - // opened, - // close(path: string) { - // setStore("opened", (opened) => opened.filter((x) => x !== path)) - // if (store.active === path) { - // const index = store.opened.findIndex((f) => f === path) - // const previous = store.opened[Math.max(0, index - 1)] - // setStore("active", previous) - // } - // resetNode(path) - // }, - // move(path: string, to: number) { - // const index = store.opened.findIndex((f) => f === path) - // if (index === -1) return - // setStore( - // "opened", - // produce((opened) => { - // opened.splice(to, 0, opened.splice(index, 1)[0]) - // }), - // ) - // setStore("node", path, "pinned", true) - // }, - } - })() - - const session = (() => { - const [store, setStore] = createStore<{ - active?: string - tabs: Record< - string, - { - active?: string - opened: string[] - } - > - }>({ - tabs: { - "": { - opened: [], - }, - }, - }) - - const active = createMemo(() => { - if (!store.active) return undefined - return sync.session.get(store.active) - }) - - createEffect(() => { - if (!store.active) return - sync.session.sync(store.active) - - if (!store.tabs[store.active]) { - setStore("tabs", store.active, { - opened: [], - }) - } - }) - - const tabs = createMemo(() => store.tabs[store.active ?? ""]) - - return { - active, - setActive(sessionId: string | undefined) { - setStore("active", sessionId) - }, - clearActive() { - setStore("active", undefined) - }, - tabs, - copyTabs(from: string, to: string) { - setStore("tabs", to, { - opened: store.tabs[from]?.opened ?? [], - }) - }, - setActiveTab(tab: string | undefined) { - setStore("tabs", store.active ?? "", "active", tab) - }, - async open(tab: string) { - if (tab !== "chat") { - await file.open(tab) - } - if (!tabs()?.opened?.includes(tab)) { - setStore("tabs", store.active ?? "", "opened", [...(tabs()?.opened ?? []), tab]) - } - setStore("tabs", store.active ?? "", "active", tab) - }, - close(tab: string) { - batch(() => { - if (!tabs()) return - setStore("tabs", store.active ?? "", { - active: tabs()!.active, - opened: tabs()!.opened.filter((x) => x !== tab), - }) - if (tabs()!.active === tab) { - const index = tabs()!.opened.findIndex((f) => f === tab) - const previous = tabs()!.opened[Math.max(0, index - 1)] - setStore("tabs", store.active ?? "", "active", previous) - } - }) - }, - move(tab: string, to: number) { - if (!tabs()) return - const index = tabs()!.opened.findIndex((f) => f === tab) - if (index === -1) return - setStore( - "tabs", - store.active ?? "", - "opened", - produce((opened) => { - opened.splice(to, 0, opened.splice(index, 1)[0]) - }), - ) - // setStore("node", path, "pinned", true) - }, } })() @@ -593,7 +460,6 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ model, agent, file, - session, context, } return result diff --git a/packages/desktop/src/context/session.tsx b/packages/desktop/src/context/session.tsx new file mode 100644 index 000000000..77ab3bc25 --- /dev/null +++ b/packages/desktop/src/context/session.tsx @@ -0,0 +1,213 @@ +import { createStore, produce } from "solid-js/store" +import { createSimpleContext } from "./helper" +import { batch, createEffect, createMemo } from "solid-js" +import { useSync } from "./sync" +import { makePersisted } from "@solid-primitives/storage" +import { TextSelection, useLocal } from "./local" +import { pipe, sumBy } from "remeda" +import { AssistantMessage } from "@opencode-ai/sdk" + +export const { use: useSession, provider: SessionProvider } = createSimpleContext({ + name: "Session", + init: (props: { sessionId?: string }) => { + const sync = useSync() + const local = useLocal() + + const [store, setStore] = makePersisted( + createStore<{ + prompt: Prompt + cursorPosition?: number + messageId?: string + tabs: { + active?: string + opened: string[] + } + }>({ + prompt: [{ type: "text", content: "", start: 0, end: 0 }], + tabs: { + opened: [], + }, + }), + { + name: props.sessionId ?? "new-session", + }, + ) + + createEffect(() => { + if (!props.sessionId) return + sync.session.sync(props.sessionId) + }) + + const info = createMemo(() => (props.sessionId ? sync.session.get(props.sessionId) : undefined)) + const messages = createMemo(() => (props.sessionId ? (sync.data.message[props.sessionId] ?? []) : [])) + const userMessages = createMemo(() => + messages() + .filter((m) => m.role === "user") + .sort((a, b) => b.id.localeCompare(a.id)), + ) + const lastUserMessage = createMemo(() => { + return userMessages()?.at(0) + }) + const activeMessage = createMemo(() => { + if (!store.messageId) return lastUserMessage() + return userMessages()?.find((m) => m.id === store.messageId) + }) + const working = createMemo(() => { + if (!props.sessionId) return false + const last = lastUserMessage() + if (!last) return false + const assistantMessages = sync.data.message[props.sessionId]?.filter( + (m) => m.role === "assistant" && m.parentID == last?.id, + ) as AssistantMessage[] + const error = assistantMessages?.find((m) => m?.error)?.error + return !last?.summary?.body && !error + }) + + const cost = createMemo(() => { + const total = pipe( + messages(), + sumBy((x) => (x.role === "assistant" ? x.cost : 0)), + ) + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(total) + }) + + const last = createMemo( + () => messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage, + ) + const model = createMemo(() => + last() ? sync.data.provider.find((x) => x.id === last().providerID)?.models[last().modelID] : undefined, + ) + + const tokens = createMemo(() => { + if (!last()) return + const tokens = last().tokens + return tokens.input + tokens.output + tokens.reasoning + tokens.cache.read + tokens.cache.write + }) + + const context = createMemo(() => { + const total = tokens() + const limit = model()?.limit.context + if (!total || !limit) return 0 + return Math.round((total / limit) * 100) + }) + + return { + id: props.sessionId, + info, + working, + prompt: { + current: createMemo(() => store.prompt), + cursor: createMemo(() => store.cursorPosition), + dirty: createMemo(() => !isPromptEqual(store.prompt, DEFAULT_PROMPT)), + set(prompt: Prompt, cursorPosition?: number) { + batch(() => { + setStore("prompt", prompt) + if (cursorPosition !== undefined) setStore("cursorPosition", cursorPosition) + }) + }, + }, + messages: { + all: messages, + user: userMessages, + last: lastUserMessage, + active: activeMessage, + setActive(id: string | undefined) { + setStore("messageId", id) + }, + }, + usage: { + tokens, + cost, + context, + }, + layout: { + tabs: store.tabs, + setActiveTab(tab: string | undefined) { + setStore("tabs", "active", tab) + }, + setOpenedTabs(tabs: string[]) { + setStore("tabs", "opened", tabs) + }, + async openTab(tab: string) { + if (tab === "chat") { + setStore("tabs", "active", undefined) + return + } + if (tab.startsWith("file://")) { + await local.file.open(tab.replace("file://", "")) + } + if (!store.tabs.opened.includes(tab)) { + setStore("tabs", "opened", [...store.tabs.opened, tab]) + } + setStore("tabs", "active", tab) + }, + closeTab(tab: string) { + batch(() => { + setStore( + "tabs", + "opened", + store.tabs.opened.filter((x) => x !== tab), + ) + if (store.tabs.active === tab) { + const index = store.tabs.opened.findIndex((f) => f === tab) + const previous = store.tabs.opened[Math.max(0, index - 1)] + setStore("tabs", "active", previous) + } + }) + }, + moveTab(tab: string, to: number) { + const index = store.tabs.opened.findIndex((f) => f === tab) + if (index === -1) return + setStore( + "tabs", + "opened", + produce((opened) => { + opened.splice(to, 0, opened.splice(index, 1)[0]) + }), + ) + // setStore("node", path, "pinned", true) + }, + }, + } + }, +}) + +interface PartBase { + content: string + start: number + end: number +} + +export interface TextPart extends PartBase { + type: "text" +} + +export interface FileAttachmentPart extends PartBase { + type: "file" + path: string + selection?: TextSelection +} + +export type ContentPart = TextPart | FileAttachmentPart +export type Prompt = ContentPart[] + +export const DEFAULT_PROMPT: Prompt = [{ type: "text", content: "", start: 0, end: 0 }] + +export function isPromptEqual(promptA: Prompt, promptB: Prompt): boolean { + if (promptA.length !== promptB.length) return false + for (let i = 0; i < promptA.length; i++) { + const partA = promptA[i] + const partB = promptB[i] + if (partA.type !== partB.type) return false + if (partA.type === "text" && partA.content !== (partB as TextPart).content) { + return false + } + if (partA.type === "file" && partA.path !== (partB as FileAttachmentPart).path) { + return false + } + } + return true +} diff --git a/packages/desktop/src/context/sync.tsx b/packages/desktop/src/context/sync.tsx index 06fc05677..c60206b0b 100644 --- a/packages/desktop/src/context/sync.tsx +++ b/packages/desktop/src/context/sync.tsx @@ -1,16 +1,4 @@ -import type { - Message, - Agent, - Provider, - Session, - Part, - Config, - Path, - File, - FileNode, - Project, - Command, -} from "@opencode-ai/sdk" +import type { Message, Agent, Provider, Session, Part, Config, Path, File, FileNode, Project } from "@opencode-ai/sdk" import { createStore, produce, reconcile } from "solid-js/store" import { createMemo } from "solid-js" import { Binary } from "@/utils/binary" diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index 9fe5da2f6..de9994af6 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -7,7 +7,9 @@ import { Fonts, ShikiProvider, MarkedProvider } from "@opencode-ai/ui" import { SDKProvider } from "./context/sdk" import { SyncProvider } from "./context/sync" import { LocalProvider } from "./context/local" -import Home from "@/pages" +import Layout from "@/pages/layout" +import SessionLayout from "@/pages/session-layout" +import Session from "@/pages/session" const host = import.meta.env.VITE_OPENCODE_SERVER_HOST ?? "127.0.0.1" const port = import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096" @@ -32,8 +34,10 @@ render( - - + + + + diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx deleted file mode 100644 index 5f04c3dbe..000000000 --- a/packages/desktop/src/pages/index.tsx +++ /dev/null @@ -1,857 +0,0 @@ -import { - Button, - List, - SelectDialog, - Tooltip, - IconButton, - Tabs, - Icon, - Accordion, - Diff, - Collapsible, - DiffChanges, - Message, - Typewriter, - Card, - Code, -} from "@opencode-ai/ui" -import { FileIcon } from "@/ui" -import FileTree from "@/components/file-tree" -import { MessageProgress } from "@/components/message-progress" -import { For, onCleanup, onMount, Show, Match, Switch, createSignal, createEffect, createMemo } from "solid-js" -import { useLocal, type LocalFile } from "@/context/local" -import { createStore } from "solid-js/store" -import { getDirectory, getFilename } from "@/utils" -import { ContentPart, PromptInput } from "@/components/prompt-input" -import { DateTime } from "luxon" -import { - DragDropProvider, - DragDropSensors, - DragOverlay, - SortableProvider, - closestCenter, - createSortable, - useDragDropContext, -} from "@thisbeyond/solid-dnd" -import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd" -import type { JSX } from "solid-js" -import { useSync } from "@/context/sync" -import { useSDK } from "@/context/sdk" -import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk" -import { Markdown } from "@opencode-ai/ui" -import { Spinner } from "@/components/spinner" - -export default function Page() { - const local = useLocal() - const sync = useSync() - const sdk = useSDK() - const [store, setStore] = createStore({ - clickTimer: undefined as number | undefined, - fileSelectOpen: false, - }) - let inputRef!: HTMLDivElement - let messageScrollElement!: HTMLDivElement - const [activeItem, setActiveItem] = createSignal(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() - 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" && !(event.ctrlKey || event.metaKey)) { - 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() - } - } - - // const navigateChange = (dir: 1 | -1) => { - // const active = local.file.active() - // if (!active) return - // const current = local.file.changeIndex(active.path) - // const next = current === undefined ? (dir === 1 ? 0 : -1) : current + dir - // local.file.setChangeIndex(active.path, next) - // } - - const handleTabChange = (path: string) => { - local.session.setActiveTab(path) - if (path === "chat") return - local.session.open(path) - } - - const handleTabClose = (file: LocalFile) => { - local.session.close(file.path) - } - - const handleDragStart = (event: unknown) => { - const id = getDraggableId(event) - if (!id) return - setActiveItem(id) - } - - const handleDragOver = (event: DragEvent) => { - const { draggable, droppable } = event - if (draggable && droppable) { - const currentFiles = local.session.tabs()?.opened.map((file) => file) - const fromIndex = currentFiles?.indexOf(draggable.id.toString()) - const toIndex = currentFiles?.indexOf(droppable.id.toString()) - if (fromIndex !== toIndex && toIndex !== undefined) { - local.session.move(draggable.id.toString(), toIndex) - } - } - } - - const handleDragEnd = () => { - setActiveItem(undefined) - } - - // const scrollDiffItem = (element: HTMLElement) => { - // element.scrollIntoView({ block: "start", behavior: "instant" }) - // } - - const handleDiffTriggerClick = (event: MouseEvent) => { - // disabling scroll to diff for now - return - // const target = event.currentTarget as HTMLElement - // queueMicrotask(() => { - // if (target.getAttribute("aria-expanded") !== "true") return - // const item = target.closest('[data-slot="accordion-item"]') as HTMLElement | null - // if (!item) return - // scrollDiffItem(item) - // }) - } - - const handlePromptSubmit = async (parts: ContentPart[]) => { - const existingSession = local.session.active() - let session = existingSession - if (!session) { - const created = await sdk.client.session.create() - session = created.data ?? undefined - } - if (!session) return - - local.session.setActive(session.id) - if (!existingSession) { - local.session.copyTabs("", session.id) - } - local.session.setActiveTab(undefined) - const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path)) - - const text = parts.map((part) => part.content).join("") - const attachments = parts.filter((part) => part.type === "file") - - // 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 = attachments.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.content, - start: attachment.start, - end: attachment.end, - }, - path: absolute, - }, - } - }) - - await sdk.client.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, - }, - ...attachmentParts, - ], - }, - }) - } - - const handleNewSession = () => { - local.session.setActive(undefined) - inputRef?.focus() - } - - const TabVisual = (props: { file: LocalFile }): JSX.Element => { - return ( -
- - - {props.file.name} - - -
- ) - } - - const SortableTab = (props: { - file: LocalFile - onTabClick: (file: LocalFile) => void - onTabClose: (file: LocalFile) => void - }): JSX.Element => { - const sortable = createSortable(props.file.path) - - return ( - // @ts-ignore -
- -
- props.onTabClick(props.file)} - > - - props.onTabClose(props.file)} - /> - -
-
-
- ) - } - - const ConstrainDragYAxis = (): JSX.Element => { - const context = useDragDropContext() - if (!context) return <> - const [, { onDragStart, onDragEnd, addTransformer, removeTransformer }] = context - const transformer: Transformer = { - id: "constrain-y-axis", - order: 100, - callback: (transform) => ({ ...transform, y: 0 }), - } - onDragStart((event) => { - const id = getDraggableId(event) - if (!id) return - addTransformer("draggables", id, transformer) - }) - onDragEnd((event) => { - const id = getDraggableId(event) - if (!id) return - removeTransformer("draggables", id, transformer.id) - }) - return <> - } - - const getDraggableId = (event: unknown): string | undefined => { - if (typeof event !== "object" || event === null) return undefined - if (!("draggable" in event)) return undefined - const draggable = (event as { draggable?: { id?: unknown } }).draggable - if (!draggable) return undefined - return typeof draggable.id === "string" ? draggable.id : undefined - } - - return ( -
- -
-
-
- {getFilename(sync.data.path.directory)} -
-
- - x.id} - current={local.session.active()} - onSelect={(s) => local.session.setActive(s?.id)} - onHover={(s) => (!!s ? sync.session.sync(s?.id) : undefined)} - > - {(session) => { - const diffs = createMemo(() => session.summary?.diffs ?? []) - const filesChanged = createMemo(() => diffs().length) - const updated = DateTime.fromMillis(session.time.updated) - return ( - -
-
- - {session.title} - - - {Math.abs(updated.diffNow().as("seconds")) < 60 - ? "Now" - : updated - .toRelative({ style: "short", unit: ["days", "hours", "minutes"] }) - ?.replace(" ago", "") - ?.replace(/ days?/, "d") - ?.replace(" min.", "m") - ?.replace(" hr.", "h")} - -
-
- {`${filesChanged() || "No"} file${filesChanged() !== 1 ? "s" : ""} changed`} - -
-
-
- ) - }} -
-
-
-
- - - - -
- - -
Chat
- {/* */} - {/* */} - {/*
{local.session.context() ?? 0}%
*/} - {/*
*/} -
- {/* Review */} - - - {(file) => ( - - )} - - -
- setStore("fileSelectOpen", true)} - /> -
-
-
- -
- -
New session
-
- -
- {getDirectory(sync.data.path.directory)} - {getFilename(sync.data.path.directory)} -
-
-
- -
- Last modified  - - {DateTime.fromMillis(sync.data.project.time.created).toRelative()} - -
-
-
- } - > - {(session) => { - const [store, setStore] = createStore<{ - messageId?: string - }>() - - const messages = createMemo(() => sync.data.message[session().id] ?? []) - const userMessages = createMemo(() => - messages() - .filter((m) => m.role === "user") - .sort((a, b) => b.id.localeCompare(a.id)), - ) - const lastUserMessage = createMemo(() => { - return userMessages()?.at(0) - }) - const activeMessage = createMemo(() => { - if (!store.messageId) return lastUserMessage() - return userMessages()?.find((m) => m.id === store.messageId) - }) - - return ( -
-
- 1}> - - -
- - {(message) => { - const isActive = createMemo(() => activeMessage()?.id === message.id) - const [titled, setTitled] = createSignal(!!message.summary?.title) - const assistantMessages = createMemo(() => { - return sync.data.message[session().id]?.filter( - (m) => m.role === "assistant" && m.parentID == message.id, - ) as AssistantMessageType[] - }) - const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) - const [completed, setCompleted] = createSignal(!!message.summary?.body || !!error()) - const [expanded, setExpanded] = createSignal(false) - const parts = createMemo(() => sync.data.part[message.id]) - const title = createMemo(() => message.summary?.title) - const summary = createMemo(() => message.summary?.body) - const diffs = createMemo(() => message.summary?.diffs ?? []) - const hasToolPart = createMemo(() => - assistantMessages() - ?.flatMap((m) => sync.data.part[m.id]) - .some((p) => p?.type === "tool"), - ) - const working = createMemo(() => !summary() && !error()) - - // allowing time for the animations to finish - createEffect(() => { - title() - setTimeout(() => setTitled(!!title()), 10_000) - }) - createEffect(() => { - const complete = !!summary() || !!error() - setTimeout(() => setCompleted(complete), 1200) - }) - - return ( - -
- {/* Title */} -
-
- - } - > -

- {title()} -

-
-
-
-
- -
- {/* Summary */} - -
-
-

- - Summary - Response - -

- - {(summary) => ( - *]:fade-up-text": !diffs().length }} - text={summary()} - /> - )} - -
- - - {(diff) => ( - - - -
-
- -
- - - {getDirectory(diff.file)}‎ - - - - {getFilename(diff.file)} - -
-
-
- - -
-
-
-
- - - -
- )} -
-
-
-
- - - {error()?.data?.message as string} - - - {/* Response */} -
- - - - - - - -
-
- - Hide details - Show details - -
- -
-
- -
- - {(assistantMessage) => { - const parts = createMemo( - () => sync.data.part[assistantMessage.id], - ) - return - }} - - - - {error()?.data?.message as string} - - -
-
-
-
-
-
-
-
- ) - }} -
-
-
-
- ) - }} - -
- - {/* */} - - {(file) => ( - - {(() => { - { - /* const view = local.file.view(file) */ - } - { - /* const showRaw = view === "raw" || !file.content?.diff */ - } - { - /* const code = showRaw ? (file.content?.content ?? "") : (file.content?.diff ?? "") */ - } - const node = local.file.node(file) - return ( - - ) - })()} - - )} - - - - {(() => { - const id = activeItem() - if (!id) return null - const draggedFile = local.file.node(id) - if (!draggedFile) return null - return ( -
- -
- ) - })()} -
- -
- { - inputRef = el - }} - onSubmit={handlePromptSubmit} - /> -
- - } - > -
    - - {(path) => ( -
  • - -
  • - )} -
    -
- -
- - - - x} - onOpenChange={(open) => setStore("fileSelectOpen", open)} - onSelect={(x) => (x ? local.session.open(x) : undefined)} - > - {(i) => ( -
-
- -
- - {getDirectory(i)} - - {getFilename(i)} -
-
-
-
- )} -
-
- - ) -} diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx new file mode 100644 index 000000000..afec9ee3c --- /dev/null +++ b/packages/desktop/src/pages/layout.tsx @@ -0,0 +1,75 @@ +import { Button, Tooltip, DiffChanges } from "@opencode-ai/ui" +import { createMemo, ParentProps } from "solid-js" +import { getFilename } from "@/utils" +import { DateTime } from "luxon" +import { useSync } from "@/context/sync" +import { VList } from "virtua/solid" +import { A, useParams } from "@solidjs/router" + +export default function Layout(props: ParentProps) { + const params = useParams() + const sync = useSync() + return ( +
+ +
+
+
+ {getFilename(sync.data.path.directory)} +
+ +
+
{props.children}
+
+
+ ) +} diff --git a/packages/desktop/src/pages/session-layout.tsx b/packages/desktop/src/pages/session-layout.tsx new file mode 100644 index 000000000..9a24608f0 --- /dev/null +++ b/packages/desktop/src/pages/session-layout.tsx @@ -0,0 +1,12 @@ +import { Show, type ParentProps } from "solid-js" +import { SessionProvider } from "@/context/session" +import { useParams } from "@solidjs/router" + +export default function Layout(props: ParentProps) { + const params = useParams() + return ( + + {props.children} + + ) +} diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx new file mode 100644 index 000000000..9c633f4f7 --- /dev/null +++ b/packages/desktop/src/pages/session.tsx @@ -0,0 +1,693 @@ +import { + SelectDialog, + IconButton, + Tabs, + Icon, + Accordion, + Diff, + Collapsible, + DiffChanges, + Message, + Typewriter, + Card, + Code, + Tooltip, + ProgressCircle, +} from "@opencode-ai/ui" +import { FileIcon } from "@/ui" +import { MessageProgress } from "@/components/message-progress" +import { + For, + onCleanup, + onMount, + Show, + Match, + Switch, + createSignal, + createEffect, + createMemo, + createResource, +} from "solid-js" +import { useLocal, type LocalFile } from "@/context/local" +import { createStore } from "solid-js/store" +import { getDirectory, getFilename } from "@/utils" +import { PromptInput } from "@/components/prompt-input" +import { DateTime } from "luxon" +import { + DragDropProvider, + DragDropSensors, + DragOverlay, + SortableProvider, + closestCenter, + createSortable, + useDragDropContext, +} from "@thisbeyond/solid-dnd" +import type { DragEvent, Transformer } from "@thisbeyond/solid-dnd" +import type { JSX } from "solid-js" +import { useSync } from "@/context/sync" +import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk" +import { Markdown } from "@opencode-ai/ui" +import { Spinner } from "@/components/spinner" +import { useSession } from "@/context/session" + +export default function Page() { + const local = useLocal() + const sync = useSync() + const session = useSession() + const [store, setStore] = createStore({ + clickTimer: undefined as number | undefined, + fileSelectOpen: false, + activeDraggable: undefined as string | undefined, + }) + let inputRef!: HTMLDivElement + let messageScrollElement!: HTMLDivElement + + 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() + 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" && !(event.ctrlKey || event.metaKey)) { + 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 handleTabClick = async (tab: string) => { + if (store.clickTimer) { + resetClickTimer() + // local.file.update(file.path, { ...file, pinned: true }) + } else { + if (tab.startsWith("file://")) { + local.file.open(tab.replace("file://", "")) + } + startClickTimer() + } + } + + const handleDragStart = (event: unknown) => { + const id = getDraggableId(event) + if (!id) return + setStore("activeDraggable", id) + } + + const handleDragOver = (event: DragEvent) => { + const { draggable, droppable } = event + if (draggable && droppable) { + const currentTabs = session.layout.tabs.opened + const fromIndex = currentTabs?.indexOf(draggable.id.toString()) + const toIndex = currentTabs?.indexOf(droppable.id.toString()) + if (fromIndex !== toIndex && toIndex !== undefined) { + session.layout.moveTab(draggable.id.toString(), toIndex) + } + } + } + + const handleDragEnd = () => { + setStore("activeDraggable", undefined) + } + + const FileVisual = (props: { file: LocalFile }): JSX.Element => { + return ( +
+ + + {props.file.name} + + +
+ ) + } + + const SortableTab = (props: { + tab: string + onTabClick: (tab: string) => void + onTabClose: (tab: string) => void + }): JSX.Element => { + const sortable = createSortable(props.tab) + + const [file] = createResource( + () => props.tab, + async (tab) => { + if (tab.startsWith("file://")) { + return local.file.node(tab.replace("file://", "")) + } + return undefined + }, + ) + + return ( + // @ts-ignore +
+
+ props.onTabClick(props.tab)}> + + {(f) => } + + props.onTabClose(props.tab)} + /> + +
+
+ ) + } + + const ConstrainDragYAxis = (): JSX.Element => { + const context = useDragDropContext() + if (!context) return <> + const [, { onDragStart, onDragEnd, addTransformer, removeTransformer }] = context + const transformer: Transformer = { + id: "constrain-y-axis", + order: 100, + callback: (transform) => ({ ...transform, y: 0 }), + } + onDragStart((event) => { + const id = getDraggableId(event) + if (!id) return + addTransformer("draggables", id, transformer) + }) + onDragEnd((event) => { + const id = getDraggableId(event) + if (!id) return + removeTransformer("draggables", id, transformer.id) + }) + return <> + } + + const getDraggableId = (event: unknown): string | undefined => { + if (typeof event !== "object" || event === null) return undefined + if (!("draggable" in event)) return undefined + const draggable = (event as { draggable?: { id?: unknown } }).draggable + if (!draggable) return undefined + return typeof draggable.id === "string" ? draggable.id : undefined + } + + return ( +
+ + + + +
+ + +
Chat
+ + +
{session.usage.context() ?? 0}%
+
+
+ {/* Review */} + + + {(tab) => } + + +
+ setStore("fileSelectOpen", true)} + /> +
+
+
+ +
+ +
New session
+
+ +
+ {getDirectory(sync.data.path.directory)} + {getFilename(sync.data.path.directory)} +
+
+
+ +
+ Last modified  + + {DateTime.fromMillis(sync.data.project.time.created).toRelative()} + +
+
+
+ } + > + {(_) => { + return ( +
+
+ 1}> + + +
+ + {(message) => { + const isActive = createMemo(() => session.messages.active()?.id === message.id) + const [titled, setTitled] = createSignal(!!message.summary?.title) + const assistantMessages = createMemo(() => { + if (!session.id) return [] + return sync.data.message[session.id]?.filter( + (m) => m.role === "assistant" && m.parentID == message.id, + ) as AssistantMessageType[] + }) + const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) + const [completed, setCompleted] = createSignal(!!message.summary?.body || !!error()) + const [detailsExpanded, setDetailsExpanded] = createSignal(false) + const parts = createMemo(() => sync.data.part[message.id]) + const hasToolPart = createMemo(() => + assistantMessages() + ?.flatMap((m) => sync.data.part[m.id]) + .some((p) => p?.type === "tool"), + ) + const working = createMemo(() => !message.summary?.body && !error()) + + // allowing time for the animations to finish + createEffect(() => { + const title = message.summary?.title + setTimeout(() => setTitled(!!title), 10_000) + }) + createEffect(() => { + const summary = message.summary?.body + const complete = !!summary || !!error() + setTimeout(() => setCompleted(complete), 1200) + }) + + return ( + +
+ {/* Title */} +
+
+ + } + > +

+ {message.summary?.title} +

+
+
+
+
+ +
+ {/* Summary */} + +
+
+

+ + Summary + Response + +

+ + {(summary) => ( + *]:fade-up-text": !message.summary?.diffs?.length, + }} + text={summary()} + /> + )} + +
+ + + {(diff) => ( + + + +
+
+ +
+ + + {getDirectory(diff.file)}‎ + + + + {getFilename(diff.file)} + +
+
+
+ + +
+
+
+
+ + + +
+ )} +
+
+
+
+ + + {error()?.data?.message as string} + + + {/* Response */} +
+ + + + + + + +
+
+ + Hide details + Show details + +
+ +
+
+ +
+ + {(assistantMessage) => { + const parts = createMemo(() => sync.data.part[assistantMessage.id]) + return + }} + + + + {error()?.data?.message as string} + + +
+
+
+
+
+
+
+
+ ) + }} +
+
+
+
+ ) + }} + +
+ + {/* */} + + {(tab) => { + const [file] = createResource( + () => tab, + async (tab) => { + if (tab.startsWith("file://")) { + return local.file.node(tab.replace("file://", "")) + } + return undefined + }, + ) + return ( + + + + {(f) => ( + + )} + + + + ) + }} + + + + + {(draggedFile) => { + const [file] = createResource( + () => draggedFile(), + async (tab) => { + if (tab.startsWith("file://")) { + return local.file.node(tab.replace("file://", "")) + } + return undefined + }, + ) + return ( +
+ {(f) => } +
+ ) + }} +
+
+ +
+ { + inputRef = el + }} + /> +
+ + }> +
    + + {(path) => ( +
  • + +
  • + )} +
    +
+
+ + + x} + onOpenChange={(open) => setStore("fileSelectOpen", open)} + onSelect={(x) => (x ? session.layout.openTab("file://" + x) : undefined)} + > + {(i) => ( +
+
+ +
+ + {getDirectory(i)} + + {getFilename(i)} +
+
+
+
+ )} +
+
+ + ) +} -- cgit v1.2.3 From 674febcf6057ecea206406ffb4b90edd19872529 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:59:10 -0600 Subject: fix(desktop): type issue --- packages/desktop/src/components/code.tsx | 12 ++++++------ packages/desktop/src/components/file-tree.tsx | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/components/code.tsx b/packages/desktop/src/components/code.tsx index 325f7b635..bbf7e28aa 100644 --- a/packages/desktop/src/components/code.tsx +++ b/packages/desktop/src/components/code.tsx @@ -99,13 +99,13 @@ export function Code(props: Props) { }) } - const onSelectionChange = () => { + const onSelectionChange = async () => { if (!container) return if (isProgrammaticSelection) return // if (ctx.file.active()?.path !== local.path) return const d = getSelectionInContainer(container) if (!d) return - const p = ctx.file.node(local.path)?.selection + const p = (await ctx.file.node(local.path))?.selection if (p && p.startLine === d.sl && p.endLine === d.el && p.startChar === d.sch && p.endChar === d.ech) return ctx.file.select(local.path, { startLine: d.sl, startChar: d.sch, endLine: d.el, endChar: d.ech }) } @@ -144,21 +144,21 @@ export function Code(props: Props) { }) // Restore scroll position from store when content is ready - createEffect(() => { + createEffect(async () => { const content = html() if (!container || !content) return - const top = ctx.file.node(local.path)?.scrollTop + const top = (await ctx.file.node(local.path))?.scrollTop if (top !== undefined && container.scrollTop !== top) container.scrollTop = top }) // Sync selection from store -> DOM - createEffect(() => { + createEffect(async () => { const content = html() if (!container || !content) return // if (ctx.file.active()?.path !== local.path) return const codeEl = container.querySelector("code") as HTMLElement | undefined if (!codeEl) return - const target = ctx.file.node(local.path)?.selection + const target = (await ctx.file.node(local.path))?.selection const current = getSelectionInContainer(container) const sel = window.getSelection() if (!sel) return diff --git a/packages/desktop/src/components/file-tree.tsx b/packages/desktop/src/components/file-tree.tsx index a5d19f51e..1347ecae6 100644 --- a/packages/desktop/src/components/file-tree.tsx +++ b/packages/desktop/src/components/file-tree.tsx @@ -77,7 +77,7 @@ export default function FileTree(props: { (open ? local.file.expand(node.path) : local.file.collapse(node.path))} > @@ -85,7 +85,7 @@ export default function FileTree(props: { -- cgit v1.2.3 From 3660e2c481f21988fd54bb395ea4d75d118a7ae0 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Wed, 5 Nov 2025 12:09:22 -0600 Subject: fix(desktop): local dev url --- packages/desktop/src/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index de9994af6..9d402138d 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -16,7 +16,9 @@ const port = import.meta.env.VITE_OPENCODE_SERVER_PORT ?? "4096" const url = new URLSearchParams(document.location.search).get("url") || - (location.hostname.includes("opencode.ai") ? `http://${host}:${port}` : "/") + (location.hostname.includes("opencode.ai") || location.hostname.includes("localhost") + ? `http://${host}:${port}` + : "/") const root = document.getElementById("root") if (import.meta.env.DEV && !(root instanceof HTMLElement)) { -- cgit v1.2.3 From c59ec71918ecf2796a12e2eefdf09176f536c0a2 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:00:17 -0600 Subject: fix(desktop): max height on message diffs, session stats --- packages/desktop/src/pages/layout.tsx | 8 +++----- packages/desktop/src/pages/session.tsx | 6 +++--- packages/ui/src/components/diff-changes.tsx | 18 ++++++++++-------- packages/ui/src/components/message-part.tsx | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index afec9ee3c..22f0a50de 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -1,5 +1,5 @@ import { Button, Tooltip, DiffChanges } from "@opencode-ai/ui" -import { createMemo, ParentProps } from "solid-js" +import { createMemo, ParentProps, Show } from "solid-js" import { getFilename } from "@/utils" import { DateTime } from "luxon" import { useSync } from "@/context/sync" @@ -25,8 +25,6 @@ export default function Layout(props: ParentProps) { {(session) => { - const diffs = createMemo(() => session.summary?.diffs ?? []) - const filesChanged = createMemo(() => diffs().length) const updated = createMemo(() => DateTime.fromMillis(session.time.updated)) return (
- {`${filesChanged() || "No"} file${filesChanged() !== 1 ? "s" : ""} changed`} - + {`${session.summary?.files || "No"} file${session.summary?.files !== 1 ? "s" : ""} changed`} + {(summary) => }
diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 9c633f4f7..36eed718a 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -362,7 +362,7 @@ export default function Page() { - +
- +
- + props.variant ?? "default" const additions = createMemo(() => - Array.isArray(props.diff) - ? props.diff.reduce((acc, diff) => acc + (diff.additions ?? 0), 0) - : props.diff.additions, + Array.isArray(props.changes) + ? props.changes.reduce((acc, diff) => acc + (diff.additions ?? 0), 0) + : props.changes.additions, ) const deletions = createMemo(() => - Array.isArray(props.diff) - ? props.diff.reduce((acc, diff) => acc + (diff.deletions ?? 0), 0) - : props.diff.deletions, + Array.isArray(props.changes) + ? props.changes.reduce((acc, diff) => acc + (diff.deletions ?? 0), 0) + : props.changes.deletions, ) const total = createMemo(() => (additions() ?? 0) + (deletions() ?? 0)) diff --git a/packages/ui/src/components/message-part.tsx b/packages/ui/src/components/message-part.tsx index 01a58025a..38870b5a3 100644 --- a/packages/ui/src/components/message-part.tsx +++ b/packages/ui/src/components/message-part.tsx @@ -364,7 +364,7 @@ ToolRegistry.register({
- +
-- cgit v1.2.3 From e006e3355cff3de25e023edcee0b59985e7db66b Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:32:05 -0600 Subject: feat(desktop): incrementally load sessions in side nav --- packages/desktop/src/context/sync.tsx | 22 +++++--- packages/desktop/src/pages/layout.tsx | 95 ++++++++++++++++++++--------------- 2 files changed, 70 insertions(+), 47 deletions(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/context/sync.tsx b/packages/desktop/src/context/sync.tsx index c60206b0b..1e960397b 100644 --- a/packages/desktop/src/context/sync.tsx +++ b/packages/desktop/src/context/sync.tsx @@ -16,6 +16,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ config: Config path: Path session: Session[] + limit: number + more: boolean message: { [sessionID: string]: Message[] } @@ -32,6 +34,8 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ agent: [], provider: [], session: [], + limit: 10, + more: false, message: {}, part: {}, node: [], @@ -106,12 +110,14 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ path: () => sdk.client.path.get().then((x) => setStore("path", x.data!)), agent: () => sdk.client.app.agents().then((x) => setStore("agent", x.data ?? [])), session: () => - sdk.client.session.list().then((x) => - setStore( - "session", - (x.data ?? []).slice().sort((a, b) => a.id.localeCompare(b.id)), - ), - ), + sdk.client.session.list().then((x) => { + const sessions = (x.data ?? []) + .slice() + .sort((a, b) => a.id.localeCompare(b.id)) + .slice(0, store.limit) + setStore("session", sessions) + setStore("more", sessions.length === store.limit) + }), config: () => sdk.client.config.get().then((x) => setStore("config", x.data!)), changes: () => sdk.client.file.status().then((x) => setStore("changes", x.data!)), node: () => sdk.client.file.list({ query: { path: "/" } }).then((x) => setStore("node", x.data!)), @@ -184,6 +190,10 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }), ) }, + fetch: async (count = 10) => { + setStore("limit", (x) => x + count) + await load.session() + }, }, load, absolute, diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index 22f0a50de..1d9669604 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -1,5 +1,5 @@ import { Button, Tooltip, DiffChanges } from "@opencode-ai/ui" -import { createMemo, ParentProps, Show } from "solid-js" +import { createMemo, For, ParentProps, Show } from "solid-js" import { getFilename } from "@/utils" import { DateTime } from "luxon" import { useSync } from "@/context/sync" @@ -9,61 +9,74 @@ import { A, useParams } from "@solidjs/router" export default function Layout(props: ParentProps) { const params = useParams() const sync = useSync() + return (
-
+
{getFilename(sync.data.path.directory)}
-
+
- - {(session) => { - const updated = createMemo(() => DateTime.fromMillis(session.time.updated)) - return ( - - -
+
+ + + ) + }} + + + + + +
{props.children}
-- cgit v1.2.3 From ab345cf0dac4378163292a5fb99b102bb2922ee1 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 6 Nov 2025 06:05:08 -0600 Subject: feat(desktop): better tooltips --- packages/desktop/src/components/prompt-input.tsx | 36 ++++++++++--- packages/ui/src/components/icon.tsx | 1 + packages/ui/src/components/tooltip.css | 69 ++++++++++++------------ packages/ui/src/components/tooltip.tsx | 8 +-- 4 files changed, 68 insertions(+), 46 deletions(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/components/prompt-input.tsx b/packages/desktop/src/components/prompt-input.tsx index 7dc67f6ae..cad1f2098 100644 --- a/packages/desktop/src/components/prompt-input.tsx +++ b/packages/desktop/src/components/prompt-input.tsx @@ -1,6 +1,6 @@ -import { Button, Icon, IconButton, Select, SelectDialog } from "@opencode-ai/ui" +import { Button, Icon, IconButton, Select, SelectDialog, Tooltip } from "@opencode-ai/ui" import { useFilteredList } from "@opencode-ai/ui/hooks" -import { createEffect, on, Component, Show, For, onMount, onCleanup } from "solid-js" +import { createEffect, on, Component, Show, For, onMount, onCleanup, Switch, Match } from "solid-js" import { createStore } from "solid-js/store" import { FileIcon } from "@/ui" import { getDirectory, getFilename } from "@/utils" @@ -519,12 +519,32 @@ export const PromptInput: Component = (props) => { )}
- + + +
+ Stop + ESC +
+
+ +
+ Send + +
+
+ + } + > + +
diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 617997201..2e96b9d85 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -151,6 +151,7 @@ const newIcons = { "square-arrow-top-right": ``, "circle-ban-sign": ``, stop: ``, + enter: ``, } export interface IconProps extends ComponentProps<"svg"> { diff --git a/packages/ui/src/components/tooltip.css b/packages/ui/src/components/tooltip.css index 0577365d6..92825aca1 100644 --- a/packages/ui/src/components/tooltip.css +++ b/packages/ui/src/components/tooltip.css @@ -6,54 +6,55 @@ [data-component="tooltip"] { z-index: 1000; max-width: 320px; - border-radius: 12px; + border-radius: 6px; background-color: var(--surface-float-base); - color: var(--white); - padding: 2px 12px 2px 12px; + color: rgba(253, 252, 252, 0.94); + padding: 2px 8px; + border: 0.5px solid rgba(253, 252, 252, 0.2); box-shadow: var(--shadow-md); pointer-events: none !important; - transition: all 150ms ease-out; - transform: translate3d(0, 0, 0); - transform-origin: var(--kb-tooltip-content-transform-origin); + /* transition: all 150ms ease-out; */ + /* transform: translate3d(0, 0, 0); */ + /* transform-origin: var(--kb-tooltip-content-transform-origin); */ - /* text-14-regular */ + /* text-12-medium */ font-family: var(--font-family-sans); - font-size: var(--font-size-base); + font-size: var(--font-size-small); font-style: normal; - font-weight: var(--font-weight-regular); - line-height: var(--line-height-large); /* 171.429% */ + font-weight: var(--font-weight-medium); + line-height: var(--line-height-large); /* 166.667% */ letter-spacing: var(--letter-spacing-normal); &[data-expanded] { opacity: 1; - transform: translate3d(0, 0, 0); + /* transform: translate3d(0, 0, 0); */ } &[data-closed] { opacity: 0; } - &[data-placement="top"] { - &[data-closed] { - transform: translate3d(0, 4px, 0); - } - } - - &[data-placement="bottom"] { - &[data-closed] { - transform: translate3d(0, -4px, 0); - } - } - - &[data-placement="left"] { - &[data-closed] { - transform: translate3d(4px, 0, 0); - } - } - - &[data-placement="right"] { - &[data-closed] { - transform: translate3d(-4px, 0, 0); - } - } + /* &[data-placement="top"] { */ + /* &[data-closed] { */ + /* transform: translate3d(0, 4px, 0); */ + /* } */ + /* } */ + /**/ + /* &[data-placement="bottom"] { */ + /* &[data-closed] { */ + /* transform: translate3d(0, -4px, 0); */ + /* } */ + /* } */ + /**/ + /* &[data-placement="left"] { */ + /* &[data-closed] { */ + /* transform: translate3d(4px, 0, 0); */ + /* } */ + /* } */ + /**/ + /* &[data-placement="right"] { */ + /* &[data-closed] { */ + /* transform: translate3d(-4px, 0, 0); */ + /* } */ + /* } */ } diff --git a/packages/ui/src/components/tooltip.tsx b/packages/ui/src/components/tooltip.tsx index ff13c8d61..c3d1947d3 100644 --- a/packages/ui/src/components/tooltip.tsx +++ b/packages/ui/src/components/tooltip.tsx @@ -1,9 +1,9 @@ import { Tooltip as KobalteTooltip } from "@kobalte/core/tooltip" -import { children, createEffect, createSignal, splitProps } from "solid-js" +import { children, createEffect, createSignal, splitProps, type JSX } from "solid-js" import type { ComponentProps } from "solid-js" export interface TooltipProps extends ComponentProps { - value: string | (() => string) + value: JSX.Element class?: string } @@ -29,13 +29,13 @@ export function Tooltip(props: TooltipProps) { }) return ( - + {c()} - {typeof others.value === "function" ? others.value() : others.value} + {others.value} {/* */} -- cgit v1.2.3 From 146bae82cb584c253a42ed4565d423d9cded93a6 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 6 Nov 2025 06:24:39 -0600 Subject: fix(desktop): button styles --- packages/desktop/src/pages/layout.tsx | 9 +++------ packages/ui/src/components/button.css | 8 +++----- 2 files changed, 6 insertions(+), 11 deletions(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index 1d9669604..85f55e8bd 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -3,7 +3,6 @@ import { createMemo, For, ParentProps, Show } from "solid-js" import { getFilename } from "@/utils" import { DateTime } from "luxon" import { useSync } from "@/context/sync" -import { VList } from "virtua/solid" import { A, useParams } from "@solidjs/router" export default function Layout(props: ParentProps) { @@ -19,11 +18,9 @@ export default function Layout(props: ParentProps) { {getFilename(sync.data.path.directory)}
- - - +
diff --git a/packages/desktop/src/context/local.tsx b/packages/desktop/src/context/local.tsx index cef6c5555..9dacc7100 100644 --- a/packages/desktop/src/context/local.tsx +++ b/packages/desktop/src/context/local.tsx @@ -5,6 +5,7 @@ import type { FileContent, FileNode, Model, Provider, File as FileStatus } from import { createSimpleContext } from "./helper" import { useSDK } from "./sdk" import { useSync } from "./sync" +import { makePersisted } from "@solid-primitives/storage" export type LocalFile = FileNode & Partial<{ @@ -456,11 +457,45 @@ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({ } })() + const layout = (() => { + const [store, setStore] = makePersisted( + createStore({ + sidebar: { + opened: true, + width: 240, + }, + }), + { + name: "layout", + }, + ) + + return { + sidebar: { + opened: createMemo(() => store.sidebar.opened), + open() { + setStore("sidebar", "opened", true) + }, + close() { + setStore("sidebar", "opened", false) + }, + toggle() { + setStore("sidebar", "opened", (x) => !x) + }, + width: createMemo(() => store.sidebar.width), + resize(width: number) { + setStore("sidebar", "width", width) + }, + }, + } + })() + const result = { model, agent, file, context, + layout, } return result }, diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index 85f55e8bd..e1560accb 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -1,27 +1,42 @@ -import { Button, Tooltip, DiffChanges } from "@opencode-ai/ui" +import { Button, Tooltip, DiffChanges, IconButton } from "@opencode-ai/ui" import { createMemo, For, ParentProps, Show } from "solid-js" -import { getFilename } from "@/utils" import { DateTime } from "luxon" import { useSync } from "@/context/sync" import { A, useParams } from "@solidjs/router" +import { useLocal } from "@/context/local" export default function Layout(props: ParentProps) { const params = useParams() const sync = useSync() + const local = useLocal() return (
-
-
- {getFilename(sync.data.path.directory)} -
-
- -
+
+
+
+ + + +
+
+ + + + +
+ +
+ + + + +
{props.children}
diff --git a/packages/ui/src/components/button.css b/packages/ui/src/components/button.css index f5a08067a..f76d7465b 100644 --- a/packages/ui/src/components/button.css +++ b/packages/ui/src/components/button.css @@ -7,6 +7,7 @@ border-radius: 6px; text-decoration: none; user-select: none; + cursor: default; outline: none; &[data-variant="primary"] { @@ -93,11 +94,12 @@ gap: 8px; + /* text-12-medium */ font-family: var(--font-family-sans); - font-size: var(--font-size-base); + font-size: var(--font-size-small); font-style: normal; font-weight: var(--font-weight-medium); - line-height: var(--line-height-large); /* 171.429% */ + line-height: var(--line-height-large); /* 166.667% */ letter-spacing: var(--letter-spacing-normal); } diff --git a/packages/ui/src/components/icon-button.css b/packages/ui/src/components/icon-button.css index 6fe95fccf..a491074fe 100644 --- a/packages/ui/src/components/icon-button.css +++ b/packages/ui/src/components/icon-button.css @@ -2,20 +2,11 @@ display: inline-flex; align-items: center; justify-content: center; - border-radius: 100%; + border-radius: 6px; text-decoration: none; user-select: none; aspect-ratio: 1; - - &:disabled { - background-color: var(--icon-strong-disabled); - color: var(--icon-invert-base); - cursor: not-allowed; - } - - &:focus { - outline: none; - } + flex-shrink: 0; &[data-variant="primary"] { background-color: var(--icon-strong-base); @@ -51,45 +42,62 @@ } &[data-variant="secondary"] { + border: transparent; background-color: var(--button-secondary-base); color: var(--text-strong); + box-shadow: var(--shadow-xs-border); &:hover:not(:disabled) { - background-color: var(--surface-hover); + background-color: var(--button-secondary-hover); } &:active:not(:disabled) { - background-color: var(--surface-active); + background-color: var(--button-secondary-base); } &:focus:not(:disabled) { - background-color: var(--surface-focus); + background-color: var(--button-secondary-base); + } + &:focus-visible:not(:active) { + background-color: var(--button-secondary-base); + box-shadow: var(--shadow-xs-border-focus); + } + &:focus-visible:active { + box-shadow: none; + } + + [data-slot="icon"] { + color: var(--icon-strong-base); } } &[data-variant="ghost"] { background-color: transparent; + /* color: var(--icon-base); */ [data-slot="icon"] { - color: var(--icon-weak-base); + color: var(--icon-base); + } - &:hover:not(:disabled) { - color: var(--icon-weak-hover); + &:hover:not(:disabled) { + background-color: var(--surface-base-hover); + + [data-slot="icon"] { + color: var(--icon-hover); } - &:active:not(:disabled) { - color: var(--icon-string-active); + } + &:active:not(:disabled) { + [data-slot="icon"] { + color: var(--icon-active); } } - - /* color: var(--text-strong); */ - /**/ - /* &:hover:not(:disabled) { */ - /* background-color: var(--surface-hover); */ - /* } */ - /* &:active:not(:disabled) { */ - /* background-color: var(--surface-active); */ - /* } */ - /* &:focus:not(:disabled) { */ - /* background-color: var(--surface-focus); */ - /* } */ + &:selected:not(:disabled) { + background-color: var(--surface-base-active); + [data-slot="icon"] { + color: var(--icon-selected); + } + } + &:focus:not(:disabled) { + background-color: var(--surface-focus); + } } &[data-size="normal"] { @@ -103,9 +111,14 @@ &[data-size="large"] { height: 32px; - padding: 0 8px 0 6px; + /* padding: 0 8px 0 6px; */ gap: 8px; + [data-slot="icon"] { + height: 16px; + width: 16px; + } + /* text-12-medium */ font-family: var(--font-family-sans); font-size: var(--font-size-small); @@ -114,4 +127,14 @@ line-height: var(--line-height-large); /* 166.667% */ letter-spacing: var(--letter-spacing-normal); } + + &:disabled { + background-color: var(--icon-strong-disabled); + color: var(--icon-invert-base); + cursor: not-allowed; + } + + &:focus { + outline: none; + } } diff --git a/packages/ui/src/components/icon-button.tsx b/packages/ui/src/components/icon-button.tsx index abc82609b..fccdebd04 100644 --- a/packages/ui/src/components/icon-button.tsx +++ b/packages/ui/src/components/icon-button.tsx @@ -2,7 +2,7 @@ import { Button as Kobalte } from "@kobalte/core/button" import { type ComponentProps, splitProps } from "solid-js" import { Icon, IconProps } from "./icon" -export interface IconButtonProps { +export interface IconButtonProps extends ComponentProps { icon: IconProps["name"] size?: "normal" | "large" iconSize?: IconProps["size"] @@ -22,7 +22,11 @@ export function IconButton(props: ComponentProps<"button"> & IconButtonProps) { [split.class ?? ""]: !!split.class, }} > - + ) } diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 2e96b9d85..082bbea90 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -152,6 +152,8 @@ const newIcons = { "circle-ban-sign": ``, stop: ``, enter: ``, + "layout-left": ``, + "speech-bubble": ``, } export interface IconProps extends ComponentProps<"svg"> { diff --git a/packages/ui/src/components/tabs.css b/packages/ui/src/components/tabs.css index 1d786fb4a..67f289283 100644 --- a/packages/ui/src/components/tabs.css +++ b/packages/ui/src/components/tabs.css @@ -7,7 +7,7 @@ overflow: clip; [data-slot="list"] { - height: 40px; + height: 48px; width: 100%; position: relative; display: flex; @@ -39,7 +39,7 @@ [data-slot="trigger"] { position: relative; height: 100%; - padding: 8px 24px; + padding: 14px 24px; display: flex; align-items: center; color: var(--text-base); diff --git a/packages/web/astro.config.mjs b/packages/web/astro.config.mjs index 24987ca35..d67bebe0a 100644 --- a/packages/web/astro.config.mjs +++ b/packages/web/astro.config.mjs @@ -110,6 +110,7 @@ export default defineConfig({ ], redirects: { "/discord": "https://discord.gg/opencode", + "/desktop-feedback": "https://discord.gg/h5TNnkFVNy", }, }) -- cgit v1.2.3 From 492bf51a0dfa4fd278337b6e75da807048d44ff1 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:56:46 -0600 Subject: fix(desktop): sidebar collapsed width --- packages/desktop/src/pages/layout.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/pages/layout.tsx b/packages/desktop/src/pages/layout.tsx index e1560accb..c5957a2d2 100644 --- a/packages/desktop/src/pages/layout.tsx +++ b/packages/desktop/src/pages/layout.tsx @@ -16,7 +16,7 @@ export default function Layout(props: ParentProps) {
Date: Thu, 6 Nov 2025 13:03:02 -0500 Subject: big format --- AGENTS.md | 46 +-- github/sst-env.d.ts | 2 +- ...80b3b2480f80fa29b850af461dce619c0b2f-audit.json | 28 +- packages/console/app/src/component/dropdown.css | 2 +- packages/console/app/src/component/faq.tsx | 5 +- packages/console/app/src/component/icon.tsx | 133 +++++-- packages/console/app/src/component/modal.css | 2 +- packages/console/app/src/routes/auth/authorize.ts | 5 +- packages/console/app/src/routes/brand/index.css | 11 +- packages/console/app/src/routes/user-menu.css | 2 +- .../console/app/src/routes/workspace-picker.css | 2 +- .../console/app/src/routes/workspace-picker.tsx | 11 +- packages/console/app/src/routes/workspace.css | 2 +- .../[id]/billing/billing-section.module.css | 2 +- .../[id]/billing/monthly-limit-section.module.css | 2 +- .../workspace/[id]/billing/payment-section.tsx | 5 +- .../[id]/billing/reload-section.module.css | 1 - .../workspace/[id]/keys/key-section.module.css | 9 +- .../src/routes/workspace/[id]/keys/key-section.tsx | 10 +- .../workspace/[id]/members/member-section.tsx | 7 +- .../workspace/[id]/members/role-dropdown.css | 2 +- .../workspace/[id]/new-user-section.module.css | 2 +- .../src/routes/workspace/[id]/new-user-section.tsx | 15 +- .../workspace/[id]/provider-section.module.css | 3 +- .../src/routes/workspace/[id]/provider-section.tsx | 20 +- .../[id]/settings/settings-section.module.css | 6 +- packages/console/app/src/routes/zen/index.tsx | 132 +++++-- .../app/src/routes/zen/util/provider/anthropic.ts | 61 +++- .../routes/zen/util/provider/openai-compatible.ts | 35 +- .../app/src/routes/zen/util/provider/openai.ts | 79 ++++- packages/console/app/src/style/token/font.css | 3 +- packages/console/app/sst-env.d.ts | 2 +- .../core/migrations/meta/0018_snapshot.json | 57 +-- .../core/migrations/meta/0019_snapshot.json | 57 +-- .../core/migrations/meta/0020_snapshot.json | 57 +-- .../core/migrations/meta/0021_snapshot.json | 57 +-- .../core/migrations/meta/0022_snapshot.json | 62 +--- .../core/migrations/meta/0023_snapshot.json | 70 +--- .../core/migrations/meta/0024_snapshot.json | 70 +--- .../core/migrations/meta/0025_snapshot.json | 70 +--- .../core/migrations/meta/0026_snapshot.json | 65 +--- .../core/migrations/meta/0027_snapshot.json | 65 +--- .../core/migrations/meta/0028_snapshot.json | 65 +--- .../core/migrations/meta/0029_snapshot.json | 65 +--- .../core/migrations/meta/0030_snapshot.json | 75 +--- .../core/migrations/meta/0031_snapshot.json | 85 +---- .../core/migrations/meta/0032_snapshot.json | 85 +---- .../core/migrations/meta/0033_snapshot.json | 85 +---- .../core/migrations/meta/0034_snapshot.json | 90 ++--- .../core/migrations/meta/0035_snapshot.json | 90 ++--- .../core/migrations/meta/0036_snapshot.json | 98 ++---- .../core/migrations/meta/0037_snapshot.json | 98 ++---- .../console/core/migrations/meta/_journal.json | 2 +- packages/console/core/src/aws.ts | 53 +-- packages/console/core/src/drizzle/index.ts | 10 +- packages/console/core/src/key.ts | 10 +- packages/console/core/src/provider.ts | 11 +- packages/console/core/src/schema/auth.sql.ts | 9 +- packages/console/core/src/schema/model.sql.ts | 5 +- packages/console/core/src/schema/provider.sql.ts | 5 +- packages/console/core/src/schema/user.sql.ts | 10 +- packages/console/core/src/user.ts | 28 +- packages/console/core/sst-env.d.ts | 174 +++++----- packages/console/function/sst-env.d.ts | 174 +++++----- .../console/mail/emails/templates/InviteEmail.tsx | 25 +- packages/console/mail/sst-env.d.ts | 2 +- packages/console/resource/sst-env.d.ts | 174 +++++----- packages/desktop/src/sst-env.d.ts | 6 +- packages/desktop/sst-env.d.ts | 2 +- packages/function/src/api.ts | 6 +- packages/function/sst-env.d.ts | 174 +++++----- packages/opencode/script/postinstall.mjs | 7 +- packages/opencode/script/schema.ts | 15 +- packages/opencode/src/agent/agent.ts | 18 +- packages/opencode/src/bus/index.ts | 10 +- packages/opencode/src/cli/cmd/auth.ts | 383 +++++++++++---------- packages/opencode/src/cli/cmd/debug/lsp.ts | 6 +- packages/opencode/src/cli/cmd/debug/ripgrep.ts | 7 +- packages/opencode/src/cli/cmd/debug/snapshot.ts | 3 +- packages/opencode/src/cli/cmd/generate.ts | 2 +- packages/opencode/src/cli/cmd/github.ts | 60 +++- packages/opencode/src/cli/cmd/run.ts | 18 +- .../src/cli/cmd/tui/component/dialog-model.tsx | 6 +- .../cli/cmd/tui/component/dialog-session-list.tsx | 4 +- .../opencode/src/cli/cmd/tui/component/logo.tsx | 14 +- .../opencode/src/cli/cmd/tui/context/route.tsx | 8 +- packages/opencode/src/cli/cmd/tui/context/sync.tsx | 1 + .../src/cli/cmd/tui/context/theme/nightowl.json | 2 +- .../cli/cmd/tui/routes/session/dialog-message.tsx | 4 +- .../cli/cmd/tui/routes/session/dialog-timeline.tsx | 12 +- .../src/cli/cmd/tui/routes/session/index.tsx | 5 +- packages/opencode/src/cli/cmd/tui/spawn.ts | 7 +- .../opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx | 4 +- packages/opencode/src/cli/cmd/tui/util/editor.ts | 5 +- packages/opencode/src/cli/cmd/upgrade.ts | 4 +- packages/opencode/src/config/config.ts | 5 +- packages/opencode/src/file/fzf.ts | 4 +- packages/opencode/src/file/ripgrep.ts | 11 +- packages/opencode/src/file/time.ts | 5 +- packages/opencode/src/file/watcher.ts | 6 +- packages/opencode/src/id/id.ts | 6 +- packages/opencode/src/lsp/client.ts | 25 +- packages/opencode/src/patch/index.ts | 211 +++++++----- packages/opencode/src/provider/transform.ts | 18 +- packages/opencode/src/server/server.ts | 2 +- packages/opencode/src/session/compaction.ts | 34 +- packages/opencode/src/session/lock.ts | 5 +- packages/opencode/src/session/message-v2.ts | 35 +- packages/opencode/src/session/message.ts | 23 +- packages/opencode/src/session/prompt.ts | 3 - packages/opencode/src/session/revert.ts | 4 +- packages/opencode/src/session/system.ts | 9 +- packages/opencode/src/session/todo.ts | 4 +- packages/opencode/src/share/share.ts | 9 +- packages/opencode/src/snapshot/index.ts | 25 +- packages/opencode/src/tool/edit.ts | 30 +- packages/opencode/src/tool/grep.ts | 10 +- packages/opencode/src/tool/ls.ts | 9 +- packages/opencode/src/tool/lsp-diagnostics.ts | 4 +- packages/opencode/src/tool/multiedit.ts | 9 +- packages/opencode/src/tool/read.ts | 12 +- packages/opencode/src/tool/registry.ts | 7 +- packages/opencode/src/tool/task.ts | 12 +- packages/opencode/src/tool/webfetch.ts | 10 +- packages/opencode/src/tool/write.ts | 8 +- packages/opencode/src/util/binary.ts | 6 +- packages/opencode/src/util/defer.ts | 4 +- packages/opencode/src/util/eventloop.ts | 10 +- packages/opencode/src/util/lock.ts | 7 +- packages/opencode/src/util/rpc.ts | 5 +- packages/opencode/src/util/wildcard.ts | 17 +- packages/opencode/sst-env.d.ts | 2 +- packages/opencode/test/patch/patch.test.ts | 139 ++++---- packages/opencode/test/session/retry.test.ts | 4 +- packages/opencode/test/util/wildcard.test.ts | 13 +- packages/plugin/package.json | 2 +- packages/plugin/src/index.ts | 5 +- packages/plugin/sst-env.d.ts | 2 +- packages/script/sst-env.d.ts | 2 +- packages/sdk/go/.github/workflows/ci.yml | 14 +- packages/sdk/go/.release-please-manifest.json | 2 +- packages/sdk/go/CHANGELOG.md | 147 ++++---- packages/sdk/go/release-please-config.json | 7 +- packages/sdk/js/package.json | 2 +- packages/sdk/js/src/server.ts | 16 +- packages/sdk/js/sst-env.d.ts | 2 +- packages/sdk/python/README.md | 18 +- packages/sdk/python/docs/generation.md | 3 + packages/sdk/python/docs/index.md | 1 + packages/sdk/python/docs/installation.md | 5 + packages/sdk/python/docs/publishing.md | 3 + packages/sdk/python/docs/testing.md | 1 + packages/sdk/python/mkdocs.yml | 2 +- packages/slack/src/index.ts | 15 +- packages/slack/sst-env.d.ts | 2 +- packages/ui/src/assets/favicon/site.webmanifest | 2 +- packages/ui/src/components/checkbox.tsx | 13 +- packages/ui/src/components/dialog.tsx | 6 +- packages/ui/src/styles/base.css | 4 +- packages/ui/src/styles/tailwind/colors.css | 2 +- packages/ui/src/styles/theme.css | 17 +- packages/ui/sst-env.d.ts | 2 +- packages/ui/tsconfig.json | 11 +- packages/web/config.mjs | 3 +- packages/web/src/components/Share.tsx | 46 ++- packages/web/src/components/icons/index.tsx | 9 +- packages/web/src/components/share/common.tsx | 7 +- packages/web/src/components/share/content-code.tsx | 6 +- packages/web/src/components/share/content-diff.tsx | 20 +- packages/web/src/components/share/copy-button.tsx | 6 +- packages/web/src/components/share/part.tsx | 115 +++++-- packages/web/src/content/docs/1-0.mdx | 3 +- packages/web/src/content/docs/index.mdx | 24 +- packages/web/src/content/docs/permissions.mdx | 1 - packages/web/src/content/docs/rules.mdx | 6 +- packages/web/src/content/docs/troubleshooting.mdx | 2 +- packages/web/src/styles/custom.css | 29 +- packages/web/sst-env.d.ts | 2 +- script/format.ts | 2 +- script/stats.ts | 14 +- sdks/vscode/src/extension.ts | 135 ++++---- sdks/vscode/sst-env.d.ts | 2 +- sst-env.d.ts | 150 ++++---- 183 files changed, 2691 insertions(+), 2559 deletions(-) (limited to 'packages/desktop/src') diff --git a/AGENTS.md b/AGENTS.md index 9c6111b08..22b305dac 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -21,27 +21,27 @@ json { - "recipient_name": "multi_tool_use.parallel", - "parameters": { - "tool_uses": [ - { - "recipient_name": "functions.read", - "parameters": { - "filePath": "path/to/file.tsx" - } - }, - { - "recipient_name": "functions.read", - "parameters": { - "filePath": "path/to/file.ts" - } - }, - { - "recipient_name": "functions.read", - "parameters": { - "filePath": "path/to/file.md" - } - } - ] - } +"recipient_name": "multi_tool_use.parallel", +"parameters": { +"tool_uses": [ +{ +"recipient_name": "functions.read", +"parameters": { +"filePath": "path/to/file.tsx" +} +}, +{ +"recipient_name": "functions.read", +"parameters": { +"filePath": "path/to/file.ts" +} +}, +{ +"recipient_name": "functions.read", +"parameters": { +"filePath": "path/to/file.md" +} +} +] +} } diff --git a/github/sst-env.d.ts b/github/sst-env.d.ts index f742a1200..6b69016e7 100644 --- a/github/sst-env.d.ts +++ b/github/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json b/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json index 7c57ef350..41cb01a2b 100644 --- a/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json +++ b/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json @@ -1,15 +1,15 @@ { - "keep": { - "days": true, - "amount": 14 - }, - "auditLog": "/home/thdxr/dev/projects/sst/opencode/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json", - "files": [ - { - "date": 1759827172859, - "name": "/home/thdxr/dev/projects/sst/opencode/logs/mcp-puppeteer-2025-10-07.log", - "hash": "a3d98b26edd793411b968a0d24cfeee8332138e282023c3b83ec169d55c67f16" - } - ], - "hashType": "sha256" -} \ No newline at end of file + "keep": { + "days": true, + "amount": 14 + }, + "auditLog": "/home/thdxr/dev/projects/sst/opencode/logs/.2c5480b3b2480f80fa29b850af461dce619c0b2f-audit.json", + "files": [ + { + "date": 1759827172859, + "name": "/home/thdxr/dev/projects/sst/opencode/logs/mcp-puppeteer-2025-10-07.log", + "hash": "a3d98b26edd793411b968a0d24cfeee8332138e282023c3b83ec169d55c67f16" + } + ], + "hashType": "sha256" +} diff --git a/packages/console/app/src/component/dropdown.css b/packages/console/app/src/component/dropdown.css index 982367c6b..242940e6a 100644 --- a/packages/console/app/src/component/dropdown.css +++ b/packages/console/app/src/component/dropdown.css @@ -77,4 +77,4 @@ background-color: var(--color-accent-alpha); } } -} \ No newline at end of file +} diff --git a/packages/console/app/src/component/faq.tsx b/packages/console/app/src/component/faq.tsx index 753a0dce4..47dca9513 100644 --- a/packages/console/app/src/component/faq.tsx +++ b/packages/console/app/src/component/faq.tsx @@ -13,7 +13,10 @@ export function Faq(props: ParentProps & { question: string }) { fill="currentColor" xmlns="http://www.w3.org/2000/svg" > - + ) { - - + + - + ) { fill-opacity="0.2" /> - - + + @@ -40,9 +61,21 @@ export function IconLogo(props: JSX.SvgSVGAttributes) { - - - + + + @@ -53,16 +86,40 @@ export function IconLogo(props: JSX.SvgSVGAttributes) { - - + + - - + + - - + + ) @@ -70,7 +127,14 @@ export function IconLogo(props: JSX.SvgSVGAttributes) { export function IconCopy(props: JSX.SvgSVGAttributes) { return ( - + ) { export function IconCheck(props: JSX.SvgSVGAttributes) { return ( - - + + ) } @@ -113,7 +189,14 @@ export function IconStripe(props: JSX.SvgSVGAttributes) { export function IconChevron(props: JSX.SvgSVGAttributes) { return ( - + ) { export function IconWorkspaceLogo(props: JSX.SvgSVGAttributes) { return ( - + ) @@ -144,7 +234,10 @@ export function IconOpenAI(props: JSX.SvgSVGAttributes) { export function IconAnthropic(props: JSX.SvgSVGAttributes) { return ( - + {(workspace) => ( - handleSelectWorkspace(workspace.id)}> + handleSelectWorkspace(workspace.id)} + > {workspace.name || workspace.slug} )} @@ -95,7 +98,11 @@ export function WorkspacePicker() { - setStore("showForm", false)} title="Create New Workspace"> + setStore("showForm", false)} + title="Create New Workspace" + >
{key.email} - + {key.timeUsed ? formatDateForTable(key.timeUsed) : "-"} diff --git a/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx b/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx index 5aa1b969e..4b2a12fdc 100644 --- a/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/members/member-section.tsx @@ -85,7 +85,12 @@ const updateMember = action(async (form: FormData) => { ) }, "member.update") -function MemberRow(props: { member: any; workspaceID: string; actorID: string; actorRole: string }) { +function MemberRow(props: { + member: any + workspaceID: string + actorID: string + actorRole: string +}) { const submission = useSubmission(updateMember) const isCurrentUser = () => props.actorID === props.member.id const isAdmin = () => props.actorRole === "admin" diff --git a/packages/console/app/src/routes/workspace/[id]/members/role-dropdown.css b/packages/console/app/src/routes/workspace/[id]/members/role-dropdown.css index 29f55a977..7a64fd9c7 100644 --- a/packages/console/app/src/routes/workspace/[id]/members/role-dropdown.css +++ b/packages/console/app/src/routes/workspace/[id]/members/role-dropdown.css @@ -69,4 +69,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/console/app/src/routes/workspace/[id]/new-user-section.module.css b/packages/console/app/src/routes/workspace/[id]/new-user-section.module.css index aaad823ab..bb58df79b 100644 --- a/packages/console/app/src/routes/workspace/[id]/new-user-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/new-user-section.module.css @@ -140,4 +140,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx b/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx index 65edc6847..7b949c661 100644 --- a/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/new-user-section.tsx @@ -43,15 +43,24 @@ export function NewUserSection() {

Tested & Verified Models

-

We've benchmarked and tested models specifically for coding agents to ensure the best performance.

+

+ We've benchmarked and tested models specifically for coding agents to ensure the best + performance. +

Highest Quality

-

Access models configured for optimal performance - no downgrades or routing to cheaper providers.

+

+ Access models configured for optimal performance - no downgrades or routing to cheaper + providers. +

No Lock-in

-

Use Zen with any coding agent, and continue using other providers with opencode whenever you want.

+

+ Use Zen with any coding agent, and continue using other providers with opencode + whenever you want. +

diff --git a/packages/console/app/src/routes/workspace/[id]/provider-section.module.css b/packages/console/app/src/routes/workspace/[id]/provider-section.module.css index 1a450d3dc..1dc7085b7 100644 --- a/packages/console/app/src/routes/workspace/[id]/provider-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/provider-section.module.css @@ -128,7 +128,6 @@ } @media (max-width: 40rem) { - th, td { padding: var(--space-2) var(--space-3); @@ -136,4 +135,4 @@ } } } -} \ No newline at end of file +} diff --git a/packages/console/app/src/routes/workspace/[id]/provider-section.tsx b/packages/console/app/src/routes/workspace/[id]/provider-section.tsx index 6ec8477b4..67314fbdc 100644 --- a/packages/console/app/src/routes/workspace/[id]/provider-section.tsx +++ b/packages/console/app/src/routes/workspace/[id]/provider-section.tsx @@ -22,7 +22,9 @@ const removeProvider = action(async (form: FormData) => { if (!provider) return { error: "Provider is required" } const workspaceID = form.get("workspaceID")?.toString() if (!workspaceID) return { error: "Workspace ID is required" } - return json(await withActor(() => Provider.remove({ provider }), workspaceID), { revalidate: listProviders.key }) + return json(await withActor(() => Provider.remove({ provider }), workspaceID), { + revalidate: listProviders.key, + }) }, "provider.remove") const saveProvider = action(async (form: FormData) => { @@ -53,7 +55,10 @@ const listProviders = query(async (workspaceID: string) => { function ProviderRow(props: { provider: Provider }) { const params = useParams() const providers = createAsync(() => listProviders(params.id)) - const saveSubmission = useSubmission(saveProvider, ([fd]) => fd.get("provider")?.toString() === props.provider.key) + const saveSubmission = useSubmission( + saveProvider, + ([fd]) => fd.get("provider")?.toString() === props.provider.key, + ) const removeSubmission = useSubmission( removeProvider, ([fd]) => fd.get("provider")?.toString() === props.provider.key, @@ -89,9 +94,16 @@ function ProviderRow(props: { provider: Provider }) { {providerData() ? maskCredentials(providerData()!.credentials) : "-"}} + fallback={ + {providerData() ? maskCredentials(providerData()!.credentials) : "-"} + } > -
+
(input = r)} diff --git a/packages/console/app/src/routes/workspace/[id]/settings/settings-section.module.css b/packages/console/app/src/routes/workspace/[id]/settings/settings-section.module.css index 058fbe301..6764a0534 100644 --- a/packages/console/app/src/routes/workspace/[id]/settings/settings-section.module.css +++ b/packages/console/app/src/routes/workspace/[id]/settings/settings-section.module.css @@ -31,7 +31,7 @@ margin: 0; } - >button { + > button { align-self: flex-start; } } @@ -80,7 +80,7 @@ } } - >button[type="reset"] { + > button[type="reset"] { align-self: flex-start; } @@ -91,4 +91,4 @@ margin-top: calc(var(--space-1) * -1); } } -} \ No newline at end of file +} diff --git a/packages/console/app/src/routes/zen/index.tsx b/packages/console/app/src/routes/zen/index.tsx index 08e58e160..92cc0a508 100644 --- a/packages/console/app/src/routes/zen/index.tsx +++ b/packages/console/app/src/routes/zen/index.tsx @@ -28,7 +28,10 @@ export default function Home() { createAsync(() => checkLoggedIn()) return (
- + OpenCode Zen | A curated set of reliable optimized models for coding agents @@ -44,13 +47,19 @@ export default function Home() { zen logo dark Reliable optimized models for coding agents

- Zen gives you access to a curated set of AI models that OpenCode has tested and benchmarked specifically - for coding agents. No need to worry about inconsistent performance and quality, use validated models - that work. + Zen gives you access to a curated set of AI models that OpenCode has tested and + benchmarked specifically for coding agents. No need to worry about inconsistent + performance and quality, use validated models that work.

- +
- - + +
- +
- +
- + Get started with Zen - +

- Add $20 Pay as you go balance (+$1.23 card processing fee) + Add $20 Pay as you go balance{" "} + (+$1.23 card processing fee)

Use with any agent. Set monthly spend limits. Cancel any time.

-
@@ -140,8 +191,8 @@ export default function Home() {

What problem is Zen solving?

- There are so many models available, but only a few work well with coding agents. Most providers - configure them differently with varying results. + There are so many models available, but only a few work well with coding agents. + Most providers configure them differently with varying results.

We're fixing this for everyone, not just OpenCode users.

@@ -176,14 +227,15 @@ export default function Home() {
  • [2]
  • [3]
    - Auto-top up - when your balance reaches $5 we’ll automatically add $20 + Auto-top up - when your balance reaches $5 we’ll automatically + add $20
  • @@ -195,8 +247,9 @@ export default function Home() {
    [*]

    - All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data - for model training, with the following exceptions. + All Zen models are hosted in the US. Providers follow a zero-retention policy and + do not use your data for model training, with the{" "} + following exceptions.

    @@ -251,7 +304,8 @@ export default function Home() { ex-Head of Design, Laravel
    - With @OpenCode Zen I know all the models are tested and perfect for coding agents. + With @OpenCode Zen I know all the models are tested and perfect for + coding agents.
    @@ -275,38 +329,44 @@ export default function Home() {
    • - Zen is a curated set of AI models tested and benchmarked for coding agents created by the team behind - OpenCode. + Zen is a curated set of AI models tested and benchmarked for coding agents created + by the team behind OpenCode.
    • - Zen only provides models that have been specifically tested and benchmarked for coding agents. You - wouldn’t use a butter knife to cut steak, don’t use poor models for coding. + Zen only provides models that have been specifically tested and benchmarked for + coding agents. You wouldn’t use a butter knife to cut steak, don’t use poor models + for coding.
    • - Zen is not for profit. Zen passes through the costs from the model providers to you. The higher Zen’s - usage the more OpenCode can negotiate better rates and pass those to you. + Zen is not for profit. Zen passes through the costs from the model providers to + you. The higher Zen’s usage the more OpenCode can negotiate better rates and pass + those to you.
    • - Zen charges per request with zero markups, so you pay exactly what - the model provider charges. Your total cost depends on usage, and you can set monthly spend limits in - your account. To cover costs, OpenCode adds only a small payment processing fee of - $1.23 per $20 balance top-up. + Zen charges per request with zero markups, so you + pay exactly what the model provider charges. Your total cost depends on usage, and + you can set monthly spend limits in your account. To cover + costs, OpenCode adds only a small payment processing fee of $1.23 per $20 balance + top-up.
    • - All Zen models are hosted in the US. Providers follow a zero-retention policy and do not use your data - for model training, with the following exceptions. + All Zen models are hosted in the US. Providers follow a zero-retention policy and + do not use your data for model training, with the{" "} + following exceptions.
    • - Yes, you can set monthly spending limits in your account. + + Yes, you can set monthly spending limits in your account. +
    • @@ -315,8 +375,8 @@ export default function Home() {
    • - While Zen works great with OpenCode, you can use Zen with any agent. Follow the setup instructions in - your preferred coding agent. + While Zen works great with OpenCode, you can use Zen with any agent. Follow the + setup instructions in your preferred coding agent.
    diff --git a/packages/console/app/src/routes/zen/util/provider/anthropic.ts b/packages/console/app/src/routes/zen/util/provider/anthropic.ts index 603d8917b..f4e8dc44d 100644 --- a/packages/console/app/src/routes/zen/util/provider/anthropic.ts +++ b/packages/console/app/src/routes/zen/util/provider/anthropic.ts @@ -98,7 +98,10 @@ export function fromAnthropicRequest(body: any): CommonRequest { typeof (src as any).media_type === "string" && typeof (src as any).data === "string" ) - return { type: "image_url", image_url: { url: `data:${(src as any).media_type};base64,${(src as any).data}` } } + return { + type: "image_url", + image_url: { url: `data:${(src as any).media_type};base64,${(src as any).data}` }, + } return undefined } @@ -120,12 +123,15 @@ export function fromAnthropicRequest(body: any): CommonRequest { if ((p as any).type === "tool_result") { const id = (p as any).tool_use_id const content = - typeof (p as any).content === "string" ? (p as any).content : JSON.stringify((p as any).content) + typeof (p as any).content === "string" + ? (p as any).content + : JSON.stringify((p as any).content) msgs.push({ role: "tool", tool_call_id: id, content }) } } if (partsOut.length > 0) { - if (partsOut.length === 1 && partsOut[0].type === "text") msgs.push({ role: "user", content: partsOut[0].text }) + if (partsOut.length === 1 && partsOut[0].type === "text") + msgs.push({ role: "user", content: partsOut[0].text }) else msgs.push({ role: "user", content: partsOut }) } continue @@ -137,7 +143,8 @@ export function fromAnthropicRequest(body: any): CommonRequest { const tcs: any[] = [] for (const p of partsIn) { if (!p || !(p as any).type) continue - if ((p as any).type === "text" && typeof (p as any).text === "string") texts.push((p as any).text) + if ((p as any).type === "text" && typeof (p as any).text === "string") + texts.push((p as any).text) if ((p as any).type === "tool_use") { const name = (p as any).name const id = (p as any).id @@ -165,7 +172,11 @@ export function fromAnthropicRequest(body: any): CommonRequest { .filter((t: any) => t && typeof t === "object" && "input_schema" in t) .map((t: any) => ({ type: "function", - function: { name: (t as any).name, description: (t as any).description, parameters: (t as any).input_schema }, + function: { + name: (t as any).name, + description: (t as any).description, + parameters: (t as any).input_schema, + }, })) : undefined @@ -203,7 +214,9 @@ export function fromAnthropicRequest(body: any): CommonRequest { export function toAnthropicRequest(body: CommonRequest) { if (!body || typeof body !== "object") return body - const sysIn = Array.isArray(body.messages) ? body.messages.filter((m: any) => m && m.role === "system") : [] + const sysIn = Array.isArray(body.messages) + ? body.messages.filter((m: any) => m && m.role === "system") + : [] let ccCount = 0 const cc = () => { ccCount++ @@ -354,7 +367,9 @@ export function fromAnthropicResponse(resp: any): CommonResponse { const idIn = (resp as any).id const id = - typeof idIn === "string" ? idIn.replace(/^msg_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}` + typeof idIn === "string" + ? idIn.replace(/^msg_/, "chatcmpl_") + : `chatcmpl_${Math.random().toString(36).slice(2)}` const model = (resp as any).model const blocks: any[] = Array.isArray((resp as any).content) ? (resp as any).content : [] @@ -397,7 +412,9 @@ export function fromAnthropicResponse(resp: any): CommonResponse { const ct = typeof (u as any).output_tokens === "number" ? (u as any).output_tokens : undefined const total = pt != null && ct != null ? pt + ct : undefined const cached = - typeof (u as any).cache_read_input_tokens === "number" ? (u as any).cache_read_input_tokens : undefined + typeof (u as any).cache_read_input_tokens === "number" + ? (u as any).cache_read_input_tokens + : undefined const details = cached != null ? { cached_tokens: cached } : undefined return { prompt_tokens: pt, @@ -452,7 +469,12 @@ export function toAnthropicResponse(resp: CommonResponse) { } catch { input = (tc as any).function.arguments } - content.push({ type: "tool_use", id: (tc as any).id, name: (tc as any).function.name, input }) + content.push({ + type: "tool_use", + id: (tc as any).id, + name: (tc as any).function.name, + input, + }) } } } @@ -511,13 +533,22 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string { if (json.type === "content_block_start") { const cb = json.content_block if (cb?.type === "text") { - out.choices.push({ index: json.index ?? 0, delta: { role: "assistant", content: "" }, finish_reason: null }) + out.choices.push({ + index: json.index ?? 0, + delta: { role: "assistant", content: "" }, + finish_reason: null, + }) } else if (cb?.type === "tool_use") { out.choices.push({ index: json.index ?? 0, delta: { tool_calls: [ - { index: json.index ?? 0, id: cb.id, type: "function", function: { name: cb.name, arguments: "" } }, + { + index: json.index ?? 0, + id: cb.id, + type: "function", + function: { name: cb.name, arguments: "" }, + }, ], }, finish_reason: null, @@ -532,7 +563,9 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string { } else if (d?.type === "input_json_delta") { out.choices.push({ index: json.index ?? 0, - delta: { tool_calls: [{ index: json.index ?? 0, function: { arguments: d.partial_json } }] }, + delta: { + tool_calls: [{ index: json.index ?? 0, function: { arguments: d.partial_json } }], + }, finish_reason: null, }) } @@ -558,7 +591,9 @@ export function fromAnthropicChunk(chunk: string): CommonChunk | string { prompt_tokens: u.input_tokens, completion_tokens: u.output_tokens, total_tokens: (u.input_tokens || 0) + (u.output_tokens || 0), - ...(u.cache_read_input_tokens ? { prompt_tokens_details: { cached_tokens: u.cache_read_input_tokens } } : {}), + ...(u.cache_read_input_tokens + ? { prompt_tokens_details: { cached_tokens: u.cache_read_input_tokens } } + : {}), } } diff --git a/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts b/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts index daf650275..d69985728 100644 --- a/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts +++ b/packages/console/app/src/routes/zen/util/provider/openai-compatible.ts @@ -57,7 +57,8 @@ export const oaCompatHelper = { const inputTokens = usage.prompt_tokens ?? 0 const outputTokens = usage.completion_tokens ?? 0 const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens ?? undefined - const cacheReadTokens = usage.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? undefined + const cacheReadTokens = + usage.cached_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? undefined return { inputTokens: inputTokens - (cacheReadTokens ?? 0), outputTokens, @@ -79,7 +80,8 @@ export function fromOaCompatibleRequest(body: any): CommonRequest { if (!m || !m.role) continue if (m.role === "system") { - if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content }) + if (typeof m.content === "string" && m.content.length > 0) + msgsOut.push({ role: "system", content: m.content }) continue } @@ -90,10 +92,12 @@ export function fromOaCompatibleRequest(body: any): CommonRequest { const parts: any[] = [] for (const p of m.content) { if (!p || !p.type) continue - if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text }) + if (p.type === "text" && typeof p.text === "string") + parts.push({ type: "text", text: p.text }) if (p.type === "image_url") parts.push({ type: "image_url", image_url: p.image_url }) } - if (parts.length === 1 && parts[0].type === "text") msgsOut.push({ role: "user", content: parts[0].text }) + if (parts.length === 1 && parts[0].type === "text") + msgsOut.push({ role: "user", content: parts[0].text }) else if (parts.length > 0) msgsOut.push({ role: "user", content: parts }) } continue @@ -137,7 +141,8 @@ export function toOaCompatibleRequest(body: CommonRequest) { if (p.type === "image_url" && p.image_url) return { type: "image_url", image_url: p.image_url } const s = (p as any).source if (!s || typeof s !== "object") return undefined - if (s.type === "url" && typeof s.url === "string") return { type: "image_url", image_url: { url: s.url } } + if (s.type === "url" && typeof s.url === "string") + return { type: "image_url", image_url: { url: s.url } } if (s.type === "base64" && typeof s.media_type === "string" && typeof s.data === "string") return { type: "image_url", image_url: { url: `data:${s.media_type};base64,${s.data}` } } return undefined @@ -147,7 +152,8 @@ export function toOaCompatibleRequest(body: CommonRequest) { if (!m || !m.role) continue if (m.role === "system") { - if (typeof m.content === "string" && m.content.length > 0) msgsOut.push({ role: "system", content: m.content }) + if (typeof m.content === "string" && m.content.length > 0) + msgsOut.push({ role: "system", content: m.content }) continue } @@ -160,11 +166,13 @@ export function toOaCompatibleRequest(body: CommonRequest) { const parts: any[] = [] for (const p of m.content) { if (!p || !p.type) continue - if (p.type === "text" && typeof p.text === "string") parts.push({ type: "text", text: p.text }) + if (p.type === "text" && typeof p.text === "string") + parts.push({ type: "text", text: p.text }) const ip = toImg(p) if (ip) parts.push(ip) } - if (parts.length === 1 && parts[0].type === "text") msgsOut.push({ role: "user", content: parts[0].text }) + if (parts.length === 1 && parts[0].type === "text") + msgsOut.push({ role: "user", content: parts[0].text }) else if (parts.length > 0) msgsOut.push({ role: "user", content: parts }) } continue @@ -317,7 +325,9 @@ export function toOaCompatibleResponse(resp: CommonResponse) { const idIn = (resp as any).id const id = - typeof idIn === "string" ? idIn.replace(/^msg_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}` + typeof idIn === "string" + ? idIn.replace(/^msg_/, "chatcmpl_") + : `chatcmpl_${Math.random().toString(36).slice(2)}` const model = (resp as any).model const blocks: any[] = Array.isArray((resp as any).content) ? (resp as any).content : [] @@ -359,7 +369,8 @@ export function toOaCompatibleResponse(resp: CommonResponse) { const pt = typeof u.input_tokens === "number" ? u.input_tokens : undefined const ct = typeof u.output_tokens === "number" ? u.output_tokens : undefined const total = pt != null && ct != null ? pt + ct : undefined - const cached = typeof u.cache_read_input_tokens === "number" ? u.cache_read_input_tokens : undefined + const cached = + typeof u.cache_read_input_tokens === "number" ? u.cache_read_input_tokens : undefined const details = cached != null ? { cached_tokens: cached } : undefined return { prompt_tokens: pt, @@ -532,7 +543,9 @@ export function toOaCompatibleChunk(chunk: CommonChunk): string { total_tokens: chunk.usage.total_tokens, ...(chunk.usage.prompt_tokens_details?.cached_tokens ? { - prompt_tokens_details: { cached_tokens: chunk.usage.prompt_tokens_details.cached_tokens }, + prompt_tokens_details: { + cached_tokens: chunk.usage.prompt_tokens_details.cached_tokens, + }, } : {}), } diff --git a/packages/console/app/src/routes/zen/util/provider/openai.ts b/packages/console/app/src/routes/zen/util/provider/openai.ts index d17300991..fa0776b7a 100644 --- a/packages/console/app/src/routes/zen/util/provider/openai.ts +++ b/packages/console/app/src/routes/zen/util/provider/openai.ts @@ -77,13 +77,20 @@ export function fromOpenaiRequest(body: any): CommonRequest { typeof (s as any).media_type === "string" && typeof (s as any).data === "string" ) - return { type: "image_url", image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` } } + return { + type: "image_url", + image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` }, + } return undefined } const msgs: any[] = [] - const inMsgs = Array.isArray(body.input) ? body.input : Array.isArray(body.messages) ? body.messages : [] + const inMsgs = Array.isArray(body.input) + ? body.input + : Array.isArray(body.messages) + ? body.messages + : [] for (const m of inMsgs) { if (!m) continue @@ -96,7 +103,9 @@ export function fromOpenaiRequest(body: any): CommonRequest { const args = typeof a === "string" ? a : JSON.stringify(a ?? {}) msgs.push({ role: "assistant", - tool_calls: [{ id: (m as any).id, type: "function", function: { name, arguments: args } }], + tool_calls: [ + { id: (m as any).id, type: "function", function: { name, arguments: args } }, + ], }) } if ((m as any).type === "function_call_output") { @@ -113,7 +122,8 @@ export function fromOpenaiRequest(body: any): CommonRequest { if (typeof c === "string" && c.length > 0) msgs.push({ role: "system", content: c }) if (Array.isArray(c)) { const t = c.find((p: any) => p && typeof p.text === "string") - if (t && typeof t.text === "string" && t.text.length > 0) msgs.push({ role: "system", content: t.text }) + if (t && typeof t.text === "string" && t.text.length > 0) + msgs.push({ role: "system", content: t.text }) } continue } @@ -126,18 +136,24 @@ export function fromOpenaiRequest(body: any): CommonRequest { const parts: any[] = [] for (const p of c) { if (!p || !(p as any).type) continue - if (((p as any).type === "text" || (p as any).type === "input_text") && typeof (p as any).text === "string") + if ( + ((p as any).type === "text" || (p as any).type === "input_text") && + typeof (p as any).text === "string" + ) parts.push({ type: "text", text: (p as any).text }) const ip = toImg(p) if (ip) parts.push(ip) if ((p as any).type === "tool_result") { const id = (p as any).tool_call_id const content = - typeof (p as any).content === "string" ? (p as any).content : JSON.stringify((p as any).content) + typeof (p as any).content === "string" + ? (p as any).content + : JSON.stringify((p as any).content) msgs.push({ role: "tool", tool_call_id: id, content }) } } - if (parts.length === 1 && parts[0].type === "text") msgs.push({ role: "user", content: parts[0].text }) + if (parts.length === 1 && parts[0].type === "text") + msgs.push({ role: "user", content: parts[0].text }) else if (parts.length > 0) msgs.push({ role: "user", content: parts }) } continue @@ -153,7 +169,11 @@ export function fromOpenaiRequest(body: any): CommonRequest { } if ((m as any).role === "tool") { - msgs.push({ role: "tool", tool_call_id: (m as any).tool_call_id, content: (m as any).content }) + msgs.push({ + role: "tool", + tool_call_id: (m as any).tool_call_id, + content: (m as any).content, + }) continue } } @@ -210,7 +230,10 @@ export function toOpenaiRequest(body: CommonRequest) { typeof (s as any).media_type === "string" && typeof (s as any).data === "string" ) - return { type: "input_image", image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` } } + return { + type: "input_image", + image_url: { url: `data:${(s as any).media_type};base64,${(s as any).data}` }, + } return undefined } @@ -257,7 +280,10 @@ export function toOpenaiRequest(body: CommonRequest) { } if ((m as any).role === "tool") { - const out = typeof (m as any).content === "string" ? (m as any).content : JSON.stringify((m as any).content) + const out = + typeof (m as any).content === "string" + ? (m as any).content + : JSON.stringify((m as any).content) input.push({ type: "function_call_output", call_id: (m as any).tool_call_id, output: out }) continue } @@ -325,7 +351,9 @@ export function fromOpenaiResponse(resp: any): CommonResponse { const idIn = (r as any).id const id = - typeof idIn === "string" ? idIn.replace(/^resp_/, "chatcmpl_") : `chatcmpl_${Math.random().toString(36).slice(2)}` + typeof idIn === "string" + ? idIn.replace(/^resp_/, "chatcmpl_") + : `chatcmpl_${Math.random().toString(36).slice(2)}` const model = (r as any).model ?? (resp as any).model const out = Array.isArray((r as any).output) ? (r as any).output : [] @@ -452,7 +480,9 @@ export function toOpenaiResponse(resp: CommonResponse) { })() return { - id: (resp as any).id?.replace(/^chatcmpl_/, "resp_") ?? `resp_${Math.random().toString(36).slice(2)}`, + id: + (resp as any).id?.replace(/^chatcmpl_/, "resp_") ?? + `resp_${Math.random().toString(36).slice(2)}`, object: "response", model: (resp as any).model, output: outputItems, @@ -498,7 +528,9 @@ export function fromOpenaiChunk(chunk: string): CommonChunk | string { if (typeof name === "string" && name.length > 0) { out.choices.push({ index: 0, - delta: { tool_calls: [{ index: 0, id, type: "function", function: { name, arguments: "" } }] }, + delta: { + tool_calls: [{ index: 0, id, type: "function", function: { name, arguments: "" } }], + }, finish_reason: null, }) } @@ -555,7 +587,12 @@ export function toOpenaiChunk(chunk: CommonChunk): string { const model = chunk.model if (d.content) { - const data = { id, type: "response.output_text.delta", delta: d.content, response: { id, model } } + const data = { + id, + type: "response.output_text.delta", + delta: d.content, + response: { id, model }, + } return `event: response.output_text.delta\ndata: ${JSON.stringify(data)}` } @@ -565,7 +602,13 @@ export function toOpenaiChunk(chunk: CommonChunk): string { const data = { type: "response.output_item.added", output_index: 0, - item: { id: tc.id, type: "function_call", name: tc.function.name, call_id: tc.id, arguments: "" }, + item: { + id: tc.id, + type: "function_call", + name: tc.function.name, + call_id: tc.id, + arguments: "", + }, } return `event: response.output_item.added\ndata: ${JSON.stringify(data)}` } @@ -593,7 +636,11 @@ export function toOpenaiChunk(chunk: CommonChunk): string { } : undefined - const data: any = { id, type: "response.completed", response: { id, model, ...(usage ? { usage } : {}) } } + const data: any = { + id, + type: "response.completed", + response: { id, model, ...(usage ? { usage } : {}) }, + } return `event: response.completed\ndata: ${JSON.stringify(data)}` } diff --git a/packages/console/app/src/style/token/font.css b/packages/console/app/src/style/token/font.css index 67143e662..dc0d298f1 100644 --- a/packages/console/app/src/style/token/font.css +++ b/packages/console/app/src/style/token/font.css @@ -15,6 +15,7 @@ body { --font-size-9xl: 8rem; --font-mono: - "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", + "Courier New", monospace; --font-sans: var(--font-mono); } diff --git a/packages/console/app/sst-env.d.ts b/packages/console/app/sst-env.d.ts index 9b9de7327..bd5588217 100644 --- a/packages/console/app/sst-env.d.ts +++ b/packages/console/app/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/console/core/migrations/meta/0018_snapshot.json b/packages/console/core/migrations/meta/0018_snapshot.json index 398ba2619..3e3c64c73 100644 --- a/packages/console/core/migrations/meta/0018_snapshot.json +++ b/packages/console/core/migrations/meta/0018_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -599,10 +578,7 @@ "indexes": { "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true } }, @@ -610,10 +586,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -670,9 +643,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -680,9 +651,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -699,4 +668,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0019_snapshot.json b/packages/console/core/migrations/meta/0019_snapshot.json index 8f1afeb51..9a0d4d243 100644 --- a/packages/console/core/migrations/meta/0019_snapshot.json +++ b/packages/console/core/migrations/meta/0019_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -599,10 +578,7 @@ "indexes": { "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true } }, @@ -610,10 +586,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -670,9 +643,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -680,9 +651,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -699,4 +668,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0020_snapshot.json b/packages/console/core/migrations/meta/0020_snapshot.json index 662093f55..9defceb5a 100644 --- a/packages/console/core/migrations/meta/0020_snapshot.json +++ b/packages/console/core/migrations/meta/0020_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -592,10 +571,7 @@ "indexes": { "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true } }, @@ -603,10 +579,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -663,9 +636,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -673,9 +644,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -692,4 +661,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0021_snapshot.json b/packages/console/core/migrations/meta/0021_snapshot.json index b285e34fa..64d3e9d24 100644 --- a/packages/console/core/migrations/meta/0021_snapshot.json +++ b/packages/console/core/migrations/meta/0021_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -599,10 +578,7 @@ "indexes": { "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true } }, @@ -610,10 +586,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -670,9 +643,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -680,9 +651,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -699,4 +668,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0022_snapshot.json b/packages/console/core/migrations/meta/0022_snapshot.json index 9486ee345..8a1c4e7d8 100644 --- a/packages/console/core/migrations/meta/0022_snapshot.json +++ b/packages/console/core/migrations/meta/0022_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -613,18 +592,12 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true } }, @@ -632,10 +605,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -692,9 +662,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -702,9 +670,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -721,4 +687,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0023_snapshot.json b/packages/console/core/migrations/meta/0023_snapshot.json index 8a40e42a4..4f6f66283 100644 --- a/packages/console/core/migrations/meta/0023_snapshot.json +++ b/packages/console/core/migrations/meta/0023_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -613,32 +592,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -646,10 +615,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -706,9 +672,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -716,9 +680,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -735,4 +697,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0024_snapshot.json b/packages/console/core/migrations/meta/0024_snapshot.json index 4f50945d6..1ef25970a 100644 --- a/packages/console/core/migrations/meta/0024_snapshot.json +++ b/packages/console/core/migrations/meta/0024_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,17 +473,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -504,10 +486,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -599,32 +578,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -632,10 +601,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -692,9 +658,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -702,9 +666,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -721,4 +683,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0025_snapshot.json b/packages/console/core/migrations/meta/0025_snapshot.json index 4b0cef0c0..6746a6e8c 100644 --- a/packages/console/core/migrations/meta/0025_snapshot.json +++ b/packages/console/core/migrations/meta/0025_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -493,17 +480,12 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true }, "name": { "name": "name", - "columns": [ - "workspace_id", - "name" - ], + "columns": ["workspace_id", "name"], "isUnique": true } }, @@ -511,10 +493,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -606,32 +585,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -639,10 +608,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -699,9 +665,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -709,9 +673,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -728,4 +690,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0026_snapshot.json b/packages/console/core/migrations/meta/0026_snapshot.json index 543ab44c3..d3c7dc496 100644 --- a/packages/console/core/migrations/meta/0026_snapshot.json +++ b/packages/console/core/migrations/meta/0026_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -493,9 +480,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -503,10 +488,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -598,32 +580,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -631,10 +603,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -691,9 +660,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -701,9 +668,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -720,4 +685,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0027_snapshot.json b/packages/console/core/migrations/meta/0027_snapshot.json index 9b6910223..408766f71 100644 --- a/packages/console/core/migrations/meta/0027_snapshot.json +++ b/packages/console/core/migrations/meta/0027_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -479,9 +466,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -489,10 +474,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -584,32 +566,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -617,10 +589,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -677,9 +646,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -687,9 +654,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -706,4 +671,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0028_snapshot.json b/packages/console/core/migrations/meta/0028_snapshot.json index 8242ae52d..827cb53c5 100644 --- a/packages/console/core/migrations/meta/0028_snapshot.json +++ b/packages/console/core/migrations/meta/0028_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -479,9 +466,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -489,10 +474,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -584,32 +566,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -617,10 +589,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -677,9 +646,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -687,9 +654,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -706,4 +671,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0029_snapshot.json b/packages/console/core/migrations/meta/0029_snapshot.json index 959004f33..d235697cc 100644 --- a/packages/console/core/migrations/meta/0029_snapshot.json +++ b/packages/console/core/migrations/meta/0029_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -479,9 +466,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -489,10 +474,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -605,32 +587,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -638,10 +610,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -698,9 +667,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -708,9 +675,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -727,4 +692,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0030_snapshot.json b/packages/console/core/migrations/meta/0030_snapshot.json index 6a6eb38cb..66978dfa5 100644 --- a/packages/console/core/migrations/meta/0030_snapshot.json +++ b/packages/console/core/migrations/meta/0030_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -479,9 +466,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -489,10 +474,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -549,10 +531,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -560,10 +539,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -676,32 +652,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -709,10 +675,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -769,9 +732,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -779,9 +740,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -798,4 +757,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0031_snapshot.json b/packages/console/core/migrations/meta/0031_snapshot.json index ba964881d..c47165925 100644 --- a/packages/console/core/migrations/meta/0031_snapshot.json +++ b/packages/console/core/migrations/meta/0031_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -398,10 +388,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -479,9 +466,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -489,10 +474,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -549,10 +531,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -560,10 +539,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -627,10 +603,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -638,10 +611,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -754,32 +724,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -787,10 +747,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -847,9 +804,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -857,9 +812,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -876,4 +829,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0032_snapshot.json b/packages/console/core/migrations/meta/0032_snapshot.json index 344fde6fd..51e84a1d3 100644 --- a/packages/console/core/migrations/meta/0032_snapshot.json +++ b/packages/console/core/migrations/meta/0032_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -180,9 +178,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -190,10 +186,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -280,10 +273,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -405,10 +395,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -486,9 +473,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -496,10 +481,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -556,10 +538,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -567,10 +546,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -634,10 +610,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -645,10 +618,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -761,32 +731,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -794,10 +754,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -854,9 +811,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -864,9 +819,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -883,4 +836,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0033_snapshot.json b/packages/console/core/migrations/meta/0033_snapshot.json index eb682adca..76d4720e8 100644 --- a/packages/console/core/migrations/meta/0033_snapshot.json +++ b/packages/console/core/migrations/meta/0033_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -187,9 +185,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -197,10 +193,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -287,10 +280,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -412,10 +402,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -493,9 +480,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -503,10 +488,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -563,10 +545,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -574,10 +553,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -641,10 +617,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -652,10 +625,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -768,32 +738,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -801,10 +761,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -861,9 +818,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -871,9 +826,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -890,4 +843,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0034_snapshot.json b/packages/console/core/migrations/meta/0034_snapshot.json index 36acbdef4..e9c999be7 100644 --- a/packages/console/core/migrations/meta/0034_snapshot.json +++ b/packages/console/core/migrations/meta/0034_snapshot.json @@ -48,9 +48,7 @@ "indexes": { "email": { "name": "email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": true } }, @@ -117,10 +115,7 @@ "indexes": { "provider": { "name": "provider", - "columns": [ - "provider", - "subject" - ], + "columns": ["provider", "subject"], "isUnique": true } }, @@ -257,9 +252,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -267,10 +260,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -357,10 +347,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -482,10 +469,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -563,9 +547,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -573,10 +555,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -633,10 +612,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -644,10 +620,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -711,10 +684,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -722,10 +692,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -838,32 +805,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -871,10 +828,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -931,9 +885,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -941,9 +893,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -960,4 +910,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0035_snapshot.json b/packages/console/core/migrations/meta/0035_snapshot.json index 7478337b5..815d120ea 100644 --- a/packages/console/core/migrations/meta/0035_snapshot.json +++ b/packages/console/core/migrations/meta/0035_snapshot.json @@ -102,17 +102,12 @@ "indexes": { "provider": { "name": "provider", - "columns": [ - "provider", - "subject" - ], + "columns": ["provider", "subject"], "isUnique": true }, "account_id": { "name": "account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false } }, @@ -249,9 +244,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -259,10 +252,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -349,10 +339,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -474,10 +461,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -555,9 +539,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -565,10 +547,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -625,10 +604,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -636,10 +612,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -703,10 +676,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -714,10 +684,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -830,32 +797,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -863,10 +820,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -923,9 +877,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -933,9 +885,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -952,4 +902,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0036_snapshot.json b/packages/console/core/migrations/meta/0036_snapshot.json index b030e30ea..926b143eb 100644 --- a/packages/console/core/migrations/meta/0036_snapshot.json +++ b/packages/console/core/migrations/meta/0036_snapshot.json @@ -43,9 +43,7 @@ "compositePrimaryKeys": { "account_id_pk": { "name": "account_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -109,17 +107,12 @@ "indexes": { "provider": { "name": "provider", - "columns": [ - "provider", - "subject" - ], + "columns": ["provider", "subject"], "isUnique": true }, "account_id": { "name": "account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false } }, @@ -127,9 +120,7 @@ "compositePrimaryKeys": { "auth_id_pk": { "name": "auth_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -263,9 +254,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -273,10 +262,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -363,10 +349,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -488,10 +471,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -569,9 +549,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -579,10 +557,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -639,10 +614,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -650,10 +622,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -717,10 +686,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -728,10 +694,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -844,32 +807,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -877,10 +830,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -937,9 +887,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -947,9 +895,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -966,4 +912,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/0037_snapshot.json b/packages/console/core/migrations/meta/0037_snapshot.json index 690bae87a..8a80ea522 100644 --- a/packages/console/core/migrations/meta/0037_snapshot.json +++ b/packages/console/core/migrations/meta/0037_snapshot.json @@ -43,9 +43,7 @@ "compositePrimaryKeys": { "account_id_pk": { "name": "account_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -109,17 +107,12 @@ "indexes": { "provider": { "name": "provider", - "columns": [ - "provider", - "subject" - ], + "columns": ["provider", "subject"], "isUnique": true }, "account_id": { "name": "account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false } }, @@ -127,9 +120,7 @@ "compositePrimaryKeys": { "auth_id_pk": { "name": "auth_id_pk", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -277,9 +268,7 @@ "indexes": { "global_customer_id": { "name": "global_customer_id", - "columns": [ - "customer_id" - ], + "columns": ["customer_id"], "isUnique": true } }, @@ -287,10 +276,7 @@ "compositePrimaryKeys": { "billing_workspace_id_id_pk": { "name": "billing_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -377,10 +363,7 @@ "compositePrimaryKeys": { "payment_workspace_id_id_pk": { "name": "payment_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -502,10 +485,7 @@ "compositePrimaryKeys": { "usage_workspace_id_id_pk": { "name": "usage_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -583,9 +563,7 @@ "indexes": { "global_key": { "name": "global_key", - "columns": [ - "key" - ], + "columns": ["key"], "isUnique": true } }, @@ -593,10 +571,7 @@ "compositePrimaryKeys": { "key_workspace_id_id_pk": { "name": "key_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -653,10 +628,7 @@ "indexes": { "model_workspace_model": { "name": "model_workspace_model", - "columns": [ - "workspace_id", - "model" - ], + "columns": ["workspace_id", "model"], "isUnique": true } }, @@ -664,10 +636,7 @@ "compositePrimaryKeys": { "model_workspace_id_id_pk": { "name": "model_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -731,10 +700,7 @@ "indexes": { "workspace_provider": { "name": "workspace_provider", - "columns": [ - "workspace_id", - "provider" - ], + "columns": ["workspace_id", "provider"], "isUnique": true } }, @@ -742,10 +708,7 @@ "compositePrimaryKeys": { "provider_workspace_id_id_pk": { "name": "provider_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -858,32 +821,22 @@ "indexes": { "user_account_id": { "name": "user_account_id", - "columns": [ - "workspace_id", - "account_id" - ], + "columns": ["workspace_id", "account_id"], "isUnique": true }, "user_email": { "name": "user_email", - "columns": [ - "workspace_id", - "email" - ], + "columns": ["workspace_id", "email"], "isUnique": true }, "global_account_id": { "name": "global_account_id", - "columns": [ - "account_id" - ], + "columns": ["account_id"], "isUnique": false }, "global_email": { "name": "global_email", - "columns": [ - "email" - ], + "columns": ["email"], "isUnique": false } }, @@ -891,10 +844,7 @@ "compositePrimaryKeys": { "user_workspace_id_id_pk": { "name": "user_workspace_id_id_pk", - "columns": [ - "workspace_id", - "id" - ] + "columns": ["workspace_id", "id"] } }, "uniqueConstraints": {}, @@ -951,9 +901,7 @@ "indexes": { "slug": { "name": "slug", - "columns": [ - "slug" - ], + "columns": ["slug"], "isUnique": true } }, @@ -961,9 +909,7 @@ "compositePrimaryKeys": { "workspace_id": { "name": "workspace_id", - "columns": [ - "id" - ] + "columns": ["id"] } }, "uniqueConstraints": {}, @@ -980,4 +926,4 @@ "tables": {}, "indexes": {} } -} \ No newline at end of file +} diff --git a/packages/console/core/migrations/meta/_journal.json b/packages/console/core/migrations/meta/_journal.json index f2c6c6fc5..250fe59b3 100644 --- a/packages/console/core/migrations/meta/_journal.json +++ b/packages/console/core/migrations/meta/_journal.json @@ -269,4 +269,4 @@ "breakpoints": true } ] -} \ No newline at end of file +} diff --git a/packages/console/core/src/aws.ts b/packages/console/core/src/aws.ts index e87ada6ef..ce4a20f44 100644 --- a/packages/console/core/src/aws.ts +++ b/packages/console/core/src/aws.ts @@ -24,37 +24,40 @@ export namespace AWS { body: z.string(), }), async (input) => { - const res = await createClient().fetch("https://email.us-east-1.amazonaws.com/v2/email/outbound-emails", { - method: "POST", - headers: { - "X-Amz-Target": "SES.SendEmail", - "Content-Type": "application/json", - }, - body: JSON.stringify({ - FromEmailAddress: `OpenCode Zen `, - Destination: { - ToAddresses: [input.to], + const res = await createClient().fetch( + "https://email.us-east-1.amazonaws.com/v2/email/outbound-emails", + { + method: "POST", + headers: { + "X-Amz-Target": "SES.SendEmail", + "Content-Type": "application/json", }, - Content: { - Simple: { - Subject: { - Charset: "UTF-8", - Data: input.subject, - }, - Body: { - Text: { + body: JSON.stringify({ + FromEmailAddress: `OpenCode Zen `, + Destination: { + ToAddresses: [input.to], + }, + Content: { + Simple: { + Subject: { Charset: "UTF-8", - Data: input.body, + Data: input.subject, }, - Html: { - Charset: "UTF-8", - Data: input.body, + Body: { + Text: { + Charset: "UTF-8", + Data: input.body, + }, + Html: { + Charset: "UTF-8", + Data: input.body, + }, }, }, }, - }, - }), - }) + }), + }, + ) if (!res.ok) { throw new Error(`Failed to send email: ${res.statusText}`) } diff --git a/packages/console/core/src/drizzle/index.ts b/packages/console/core/src/drizzle/index.ts index f0f065de4..8b37b1f9c 100644 --- a/packages/console/core/src/drizzle/index.ts +++ b/packages/console/core/src/drizzle/index.ts @@ -5,7 +5,10 @@ import { Client } from "@planetscale/database" import { MySqlTransaction, type MySqlTransactionConfig } from "drizzle-orm/mysql-core" import type { ExtractTablesWithRelations } from "drizzle-orm" -import type { PlanetScalePreparedQueryHKT, PlanetscaleQueryResultHKT } from "drizzle-orm/planetscale-serverless" +import type { + PlanetScalePreparedQueryHKT, + PlanetscaleQueryResultHKT, +} from "drizzle-orm/planetscale-serverless" import { Context } from "../context" import { memo } from "../util/memo" @@ -67,7 +70,10 @@ export namespace Database { } } - export async function transaction(callback: (tx: TxOrDb) => Promise, config?: MySqlTransactionConfig) { + export async function transaction( + callback: (tx: TxOrDb) => Promise, + config?: MySqlTransactionConfig, + ) { try { const { tx } = TransactionContext.use() return callback(tx) diff --git a/packages/console/core/src/key.ts b/packages/console/core/src/key.ts index 688f19b3d..6396fd0b8 100644 --- a/packages/console/core/src/key.ts +++ b/packages/console/core/src/key.ts @@ -20,8 +20,14 @@ export namespace Key { email: AuthTable.subject, }) .from(KeyTable) - .innerJoin(UserTable, and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID))) - .innerJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) + .innerJoin( + UserTable, + and(eq(KeyTable.userID, UserTable.id), eq(KeyTable.workspaceID, UserTable.workspaceID)), + ) + .innerJoin( + AuthTable, + and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), + ) .where( and( ...[ diff --git a/packages/console/core/src/provider.ts b/packages/console/core/src/provider.ts index cf2040b59..0af642f71 100644 --- a/packages/console/core/src/provider.ts +++ b/packages/console/core/src/provider.ts @@ -11,7 +11,9 @@ export namespace Provider { tx .select() .from(ProviderTable) - .where(and(eq(ProviderTable.workspaceID, Actor.workspace()), isNull(ProviderTable.timeDeleted))), + .where( + and(eq(ProviderTable.workspaceID, Actor.workspace()), isNull(ProviderTable.timeDeleted)), + ), ), ) @@ -50,7 +52,12 @@ export namespace Provider { return Database.transaction((tx) => tx .delete(ProviderTable) - .where(and(eq(ProviderTable.provider, provider), eq(ProviderTable.workspaceID, Actor.workspace()))), + .where( + and( + eq(ProviderTable.provider, provider), + eq(ProviderTable.workspaceID, Actor.workspace()), + ), + ), ) }, ) diff --git a/packages/console/core/src/schema/auth.sql.ts b/packages/console/core/src/schema/auth.sql.ts index 27c926d6f..d55e605aa 100644 --- a/packages/console/core/src/schema/auth.sql.ts +++ b/packages/console/core/src/schema/auth.sql.ts @@ -1,4 +1,11 @@ -import { index, mysqlEnum, mysqlTable, primaryKey, uniqueIndex, varchar } from "drizzle-orm/mysql-core" +import { + index, + mysqlEnum, + mysqlTable, + primaryKey, + uniqueIndex, + varchar, +} from "drizzle-orm/mysql-core" import { id, timestamps, ulid } from "../drizzle/types" export const AuthProvider = ["email", "github", "google"] as const diff --git a/packages/console/core/src/schema/model.sql.ts b/packages/console/core/src/schema/model.sql.ts index 1c032aad2..343b0c4f3 100644 --- a/packages/console/core/src/schema/model.sql.ts +++ b/packages/console/core/src/schema/model.sql.ts @@ -9,5 +9,8 @@ export const ModelTable = mysqlTable( ...timestamps, model: varchar("model", { length: 64 }).notNull(), }, - (table) => [...workspaceIndexes(table), uniqueIndex("model_workspace_model").on(table.workspaceID, table.model)], + (table) => [ + ...workspaceIndexes(table), + uniqueIndex("model_workspace_model").on(table.workspaceID, table.model), + ], ) diff --git a/packages/console/core/src/schema/provider.sql.ts b/packages/console/core/src/schema/provider.sql.ts index 11be5b4d7..04d11e2e5 100644 --- a/packages/console/core/src/schema/provider.sql.ts +++ b/packages/console/core/src/schema/provider.sql.ts @@ -10,5 +10,8 @@ export const ProviderTable = mysqlTable( provider: varchar("provider", { length: 64 }).notNull(), credentials: text("credentials").notNull(), }, - (table) => [...workspaceIndexes(table), uniqueIndex("workspace_provider").on(table.workspaceID, table.provider)], + (table) => [ + ...workspaceIndexes(table), + uniqueIndex("workspace_provider").on(table.workspaceID, table.provider), + ], ) diff --git a/packages/console/core/src/schema/user.sql.ts b/packages/console/core/src/schema/user.sql.ts index 7fd7f5e1e..ce5b6c53e 100644 --- a/packages/console/core/src/schema/user.sql.ts +++ b/packages/console/core/src/schema/user.sql.ts @@ -1,4 +1,12 @@ -import { mysqlTable, uniqueIndex, varchar, int, mysqlEnum, index, bigint } from "drizzle-orm/mysql-core" +import { + mysqlTable, + uniqueIndex, + varchar, + int, + mysqlEnum, + index, + bigint, +} from "drizzle-orm/mysql-core" import { timestamps, ulid, utc, workspaceColumns } from "../drizzle/types" import { workspaceIndexes } from "./workspace.sql" diff --git a/packages/console/core/src/user.ts b/packages/console/core/src/user.ts index 8b7a96f44..cbb1ac827 100644 --- a/packages/console/core/src/user.ts +++ b/packages/console/core/src/user.ts @@ -26,7 +26,10 @@ export namespace User { authEmail: AuthTable.subject, }) .from(UserTable) - .leftJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) + .leftJoin( + AuthTable, + and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), + ) .where(and(eq(UserTable.workspaceID, Actor.workspace()), isNull(UserTable.timeDeleted))), ), ) @@ -36,7 +39,13 @@ export namespace User { tx .select() .from(UserTable) - .where(and(eq(UserTable.workspaceID, Actor.workspace()), eq(UserTable.id, id), isNull(UserTable.timeDeleted))) + .where( + and( + eq(UserTable.workspaceID, Actor.workspace()), + eq(UserTable.id, id), + isNull(UserTable.timeDeleted), + ), + ) .then((rows) => rows[0]), ), ) @@ -48,7 +57,10 @@ export namespace User { email: AuthTable.subject, }) .from(UserTable) - .leftJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) + .leftJoin( + AuthTable, + and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), + ) .where(and(eq(UserTable.workspaceID, Actor.workspace()), eq(UserTable.id, id))) .then((rows) => rows[0]?.email), ), @@ -130,10 +142,16 @@ export namespace User { workspaceName: WorkspaceTable.name, }) .from(UserTable) - .innerJoin(AuthTable, and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email"))) + .innerJoin( + AuthTable, + and(eq(UserTable.accountID, AuthTable.accountID), eq(AuthTable.provider, "email")), + ) .innerJoin(WorkspaceTable, eq(WorkspaceTable.id, workspaceID)) .where( - and(eq(UserTable.workspaceID, workspaceID), eq(UserTable.id, Actor.assert("user").properties.userID)), + and( + eq(UserTable.workspaceID, workspaceID), + eq(UserTable.id, Actor.assert("user").properties.userID), + ), ) .then((rows) => rows[0]), ) diff --git a/packages/console/core/sst-env.d.ts b/packages/console/core/sst-env.d.ts index 01407434e..ba4c5a623 100644 --- a/packages/console/core/sst-env.d.ts +++ b/packages/console/core/sst-env.d.ts @@ -6,99 +6,99 @@ import "sst" declare module "sst" { export interface Resource { - "ADMIN_SECRET": { - "type": "sst.sst.Secret" - "value": string - } - "AUTH_API_URL": { - "type": "sst.sst.Linkable" - "value": string - } - "AWS_SES_ACCESS_KEY_ID": { - "type": "sst.sst.Secret" - "value": string - } - "AWS_SES_SECRET_ACCESS_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "Console": { - "type": "sst.cloudflare.SolidStart" - "url": string - } - "Database": { - "database": string - "host": string - "password": string - "port": number - "type": "sst.sst.Linkable" - "username": string - } - "Desktop": { - "type": "sst.cloudflare.StaticSite" - "url": string - } - "EMAILOCTOPUS_API_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_APP_ID": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_APP_PRIVATE_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_CLIENT_ID_CONSOLE": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_CLIENT_SECRET_CONSOLE": { - "type": "sst.sst.Secret" - "value": string - } - "GOOGLE_CLIENT_ID": { - "type": "sst.sst.Secret" - "value": string - } - "HONEYCOMB_API_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "STRIPE_SECRET_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "STRIPE_WEBHOOK_SECRET": { - "type": "sst.sst.Linkable" - "value": string - } - "Web": { - "type": "sst.cloudflare.Astro" - "url": string - } - "ZEN_MODELS1": { - "type": "sst.sst.Secret" - "value": string - } - "ZEN_MODELS2": { - "type": "sst.sst.Secret" - "value": string + ADMIN_SECRET: { + type: "sst.sst.Secret" + value: string + } + AUTH_API_URL: { + type: "sst.sst.Linkable" + value: string + } + AWS_SES_ACCESS_KEY_ID: { + type: "sst.sst.Secret" + value: string + } + AWS_SES_SECRET_ACCESS_KEY: { + type: "sst.sst.Secret" + value: string + } + Console: { + type: "sst.cloudflare.SolidStart" + url: string + } + Database: { + database: string + host: string + password: string + port: number + type: "sst.sst.Linkable" + username: string + } + Desktop: { + type: "sst.cloudflare.StaticSite" + url: string + } + EMAILOCTOPUS_API_KEY: { + type: "sst.sst.Secret" + value: string + } + GITHUB_APP_ID: { + type: "sst.sst.Secret" + value: string + } + GITHUB_APP_PRIVATE_KEY: { + type: "sst.sst.Secret" + value: string + } + GITHUB_CLIENT_ID_CONSOLE: { + type: "sst.sst.Secret" + value: string + } + GITHUB_CLIENT_SECRET_CONSOLE: { + type: "sst.sst.Secret" + value: string + } + GOOGLE_CLIENT_ID: { + type: "sst.sst.Secret" + value: string + } + HONEYCOMB_API_KEY: { + type: "sst.sst.Secret" + value: string + } + STRIPE_SECRET_KEY: { + type: "sst.sst.Secret" + value: string + } + STRIPE_WEBHOOK_SECRET: { + type: "sst.sst.Linkable" + value: string + } + Web: { + type: "sst.cloudflare.Astro" + url: string + } + ZEN_MODELS1: { + type: "sst.sst.Secret" + value: string + } + ZEN_MODELS2: { + type: "sst.sst.Secret" + value: string } } } -// cloudflare -import * as cloudflare from "@cloudflare/workers-types"; +// cloudflare +import * as cloudflare from "@cloudflare/workers-types" declare module "sst" { export interface Resource { - "Api": cloudflare.Service - "AuthApi": cloudflare.Service - "AuthStorage": cloudflare.KVNamespace - "Bucket": cloudflare.R2Bucket - "LogProcessor": cloudflare.Service + Api: cloudflare.Service + AuthApi: cloudflare.Service + AuthStorage: cloudflare.KVNamespace + Bucket: cloudflare.R2Bucket + LogProcessor: cloudflare.Service } } import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/console/function/sst-env.d.ts b/packages/console/function/sst-env.d.ts index 01407434e..ba4c5a623 100644 --- a/packages/console/function/sst-env.d.ts +++ b/packages/console/function/sst-env.d.ts @@ -6,99 +6,99 @@ import "sst" declare module "sst" { export interface Resource { - "ADMIN_SECRET": { - "type": "sst.sst.Secret" - "value": string - } - "AUTH_API_URL": { - "type": "sst.sst.Linkable" - "value": string - } - "AWS_SES_ACCESS_KEY_ID": { - "type": "sst.sst.Secret" - "value": string - } - "AWS_SES_SECRET_ACCESS_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "Console": { - "type": "sst.cloudflare.SolidStart" - "url": string - } - "Database": { - "database": string - "host": string - "password": string - "port": number - "type": "sst.sst.Linkable" - "username": string - } - "Desktop": { - "type": "sst.cloudflare.StaticSite" - "url": string - } - "EMAILOCTOPUS_API_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_APP_ID": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_APP_PRIVATE_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_CLIENT_ID_CONSOLE": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_CLIENT_SECRET_CONSOLE": { - "type": "sst.sst.Secret" - "value": string - } - "GOOGLE_CLIENT_ID": { - "type": "sst.sst.Secret" - "value": string - } - "HONEYCOMB_API_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "STRIPE_SECRET_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "STRIPE_WEBHOOK_SECRET": { - "type": "sst.sst.Linkable" - "value": string - } - "Web": { - "type": "sst.cloudflare.Astro" - "url": string - } - "ZEN_MODELS1": { - "type": "sst.sst.Secret" - "value": string - } - "ZEN_MODELS2": { - "type": "sst.sst.Secret" - "value": string + ADMIN_SECRET: { + type: "sst.sst.Secret" + value: string + } + AUTH_API_URL: { + type: "sst.sst.Linkable" + value: string + } + AWS_SES_ACCESS_KEY_ID: { + type: "sst.sst.Secret" + value: string + } + AWS_SES_SECRET_ACCESS_KEY: { + type: "sst.sst.Secret" + value: string + } + Console: { + type: "sst.cloudflare.SolidStart" + url: string + } + Database: { + database: string + host: string + password: string + port: number + type: "sst.sst.Linkable" + username: string + } + Desktop: { + type: "sst.cloudflare.StaticSite" + url: string + } + EMAILOCTOPUS_API_KEY: { + type: "sst.sst.Secret" + value: string + } + GITHUB_APP_ID: { + type: "sst.sst.Secret" + value: string + } + GITHUB_APP_PRIVATE_KEY: { + type: "sst.sst.Secret" + value: string + } + GITHUB_CLIENT_ID_CONSOLE: { + type: "sst.sst.Secret" + value: string + } + GITHUB_CLIENT_SECRET_CONSOLE: { + type: "sst.sst.Secret" + value: string + } + GOOGLE_CLIENT_ID: { + type: "sst.sst.Secret" + value: string + } + HONEYCOMB_API_KEY: { + type: "sst.sst.Secret" + value: string + } + STRIPE_SECRET_KEY: { + type: "sst.sst.Secret" + value: string + } + STRIPE_WEBHOOK_SECRET: { + type: "sst.sst.Linkable" + value: string + } + Web: { + type: "sst.cloudflare.Astro" + url: string + } + ZEN_MODELS1: { + type: "sst.sst.Secret" + value: string + } + ZEN_MODELS2: { + type: "sst.sst.Secret" + value: string } } } -// cloudflare -import * as cloudflare from "@cloudflare/workers-types"; +// cloudflare +import * as cloudflare from "@cloudflare/workers-types" declare module "sst" { export interface Resource { - "Api": cloudflare.Service - "AuthApi": cloudflare.Service - "AuthStorage": cloudflare.KVNamespace - "Bucket": cloudflare.R2Bucket - "LogProcessor": cloudflare.Service + Api: cloudflare.Service + AuthApi: cloudflare.Service + AuthStorage: cloudflare.KVNamespace + Bucket: cloudflare.R2Bucket + LogProcessor: cloudflare.Service } } import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/console/mail/emails/templates/InviteEmail.tsx b/packages/console/mail/emails/templates/InviteEmail.tsx index baf0d383f..e94eb564c 100644 --- a/packages/console/mail/emails/templates/InviteEmail.tsx +++ b/packages/console/mail/emails/templates/InviteEmail.tsx @@ -1,6 +1,18 @@ // @ts-nocheck import React from "react" -import { Img, Row, Html, Link, Body, Head, Button, Column, Preview, Section, Container } from "@jsx-email/all" +import { + Img, + Row, + Html, + Link, + Body, + Head, + Button, + Column, + Preview, + Section, + Container, +} from "@jsx-email/all" import { Text, Fonts, Title, A, Span } from "../components" import { unit, @@ -52,8 +64,8 @@ export const InviteEmail = ({
    Join your team's OpenCode workspace - You have been invited by {inviter} to join the{" "} - {workspaceName} workspace on OpenCode. + You have been invited by {inviter} to join + the {workspaceName} workspace on OpenCode.
    @@ -61,7 +73,12 @@ export const InviteEmail = ({ diff --git a/packages/console/mail/sst-env.d.ts b/packages/console/mail/sst-env.d.ts index 9b9de7327..bd5588217 100644 --- a/packages/console/mail/sst-env.d.ts +++ b/packages/console/mail/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/console/resource/sst-env.d.ts b/packages/console/resource/sst-env.d.ts index 01407434e..ba4c5a623 100644 --- a/packages/console/resource/sst-env.d.ts +++ b/packages/console/resource/sst-env.d.ts @@ -6,99 +6,99 @@ import "sst" declare module "sst" { export interface Resource { - "ADMIN_SECRET": { - "type": "sst.sst.Secret" - "value": string - } - "AUTH_API_URL": { - "type": "sst.sst.Linkable" - "value": string - } - "AWS_SES_ACCESS_KEY_ID": { - "type": "sst.sst.Secret" - "value": string - } - "AWS_SES_SECRET_ACCESS_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "Console": { - "type": "sst.cloudflare.SolidStart" - "url": string - } - "Database": { - "database": string - "host": string - "password": string - "port": number - "type": "sst.sst.Linkable" - "username": string - } - "Desktop": { - "type": "sst.cloudflare.StaticSite" - "url": string - } - "EMAILOCTOPUS_API_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_APP_ID": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_APP_PRIVATE_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_CLIENT_ID_CONSOLE": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_CLIENT_SECRET_CONSOLE": { - "type": "sst.sst.Secret" - "value": string - } - "GOOGLE_CLIENT_ID": { - "type": "sst.sst.Secret" - "value": string - } - "HONEYCOMB_API_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "STRIPE_SECRET_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "STRIPE_WEBHOOK_SECRET": { - "type": "sst.sst.Linkable" - "value": string - } - "Web": { - "type": "sst.cloudflare.Astro" - "url": string - } - "ZEN_MODELS1": { - "type": "sst.sst.Secret" - "value": string - } - "ZEN_MODELS2": { - "type": "sst.sst.Secret" - "value": string + ADMIN_SECRET: { + type: "sst.sst.Secret" + value: string + } + AUTH_API_URL: { + type: "sst.sst.Linkable" + value: string + } + AWS_SES_ACCESS_KEY_ID: { + type: "sst.sst.Secret" + value: string + } + AWS_SES_SECRET_ACCESS_KEY: { + type: "sst.sst.Secret" + value: string + } + Console: { + type: "sst.cloudflare.SolidStart" + url: string + } + Database: { + database: string + host: string + password: string + port: number + type: "sst.sst.Linkable" + username: string + } + Desktop: { + type: "sst.cloudflare.StaticSite" + url: string + } + EMAILOCTOPUS_API_KEY: { + type: "sst.sst.Secret" + value: string + } + GITHUB_APP_ID: { + type: "sst.sst.Secret" + value: string + } + GITHUB_APP_PRIVATE_KEY: { + type: "sst.sst.Secret" + value: string + } + GITHUB_CLIENT_ID_CONSOLE: { + type: "sst.sst.Secret" + value: string + } + GITHUB_CLIENT_SECRET_CONSOLE: { + type: "sst.sst.Secret" + value: string + } + GOOGLE_CLIENT_ID: { + type: "sst.sst.Secret" + value: string + } + HONEYCOMB_API_KEY: { + type: "sst.sst.Secret" + value: string + } + STRIPE_SECRET_KEY: { + type: "sst.sst.Secret" + value: string + } + STRIPE_WEBHOOK_SECRET: { + type: "sst.sst.Linkable" + value: string + } + Web: { + type: "sst.cloudflare.Astro" + url: string + } + ZEN_MODELS1: { + type: "sst.sst.Secret" + value: string + } + ZEN_MODELS2: { + type: "sst.sst.Secret" + value: string } } } -// cloudflare -import * as cloudflare from "@cloudflare/workers-types"; +// cloudflare +import * as cloudflare from "@cloudflare/workers-types" declare module "sst" { export interface Resource { - "Api": cloudflare.Service - "AuthApi": cloudflare.Service - "AuthStorage": cloudflare.KVNamespace - "Bucket": cloudflare.R2Bucket - "LogProcessor": cloudflare.Service + Api: cloudflare.Service + AuthApi: cloudflare.Service + AuthStorage: cloudflare.KVNamespace + Bucket: cloudflare.R2Bucket + LogProcessor: cloudflare.Service } } import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/desktop/src/sst-env.d.ts b/packages/desktop/src/sst-env.d.ts index 47a8fbec7..1b1683a1e 100644 --- a/packages/desktop/src/sst-env.d.ts +++ b/packages/desktop/src/sst-env.d.ts @@ -2,9 +2,7 @@ /* tslint:disable */ /* eslint-disable */ /// -interface ImportMetaEnv { - -} +interface ImportMetaEnv {} interface ImportMeta { readonly env: ImportMetaEnv -} \ No newline at end of file +} diff --git a/packages/desktop/sst-env.d.ts b/packages/desktop/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/desktop/sst-env.d.ts +++ b/packages/desktop/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/function/src/api.ts b/packages/function/src/api.ts index 6f00dae9a..3475f5d72 100644 --- a/packages/function/src/api.ts +++ b/packages/function/src/api.ts @@ -268,7 +268,11 @@ export default new Hono<{ Bindings: Env }>() // Verify permissions const userClient = new Octokit({ auth: token }) const { data: repoData } = await userClient.repos.get({ owner, repo }) - if (!repoData.permissions.admin && !repoData.permissions.push && !repoData.permissions.maintain) + if ( + !repoData.permissions.admin && + !repoData.permissions.push && + !repoData.permissions.maintain + ) throw new Error("User does not have write permissions") // Get installation token diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts index 01407434e..ba4c5a623 100644 --- a/packages/function/sst-env.d.ts +++ b/packages/function/sst-env.d.ts @@ -6,99 +6,99 @@ import "sst" declare module "sst" { export interface Resource { - "ADMIN_SECRET": { - "type": "sst.sst.Secret" - "value": string - } - "AUTH_API_URL": { - "type": "sst.sst.Linkable" - "value": string - } - "AWS_SES_ACCESS_KEY_ID": { - "type": "sst.sst.Secret" - "value": string - } - "AWS_SES_SECRET_ACCESS_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "Console": { - "type": "sst.cloudflare.SolidStart" - "url": string - } - "Database": { - "database": string - "host": string - "password": string - "port": number - "type": "sst.sst.Linkable" - "username": string - } - "Desktop": { - "type": "sst.cloudflare.StaticSite" - "url": string - } - "EMAILOCTOPUS_API_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_APP_ID": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_APP_PRIVATE_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_CLIENT_ID_CONSOLE": { - "type": "sst.sst.Secret" - "value": string - } - "GITHUB_CLIENT_SECRET_CONSOLE": { - "type": "sst.sst.Secret" - "value": string - } - "GOOGLE_CLIENT_ID": { - "type": "sst.sst.Secret" - "value": string - } - "HONEYCOMB_API_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "STRIPE_SECRET_KEY": { - "type": "sst.sst.Secret" - "value": string - } - "STRIPE_WEBHOOK_SECRET": { - "type": "sst.sst.Linkable" - "value": string - } - "Web": { - "type": "sst.cloudflare.Astro" - "url": string - } - "ZEN_MODELS1": { - "type": "sst.sst.Secret" - "value": string - } - "ZEN_MODELS2": { - "type": "sst.sst.Secret" - "value": string + ADMIN_SECRET: { + type: "sst.sst.Secret" + value: string + } + AUTH_API_URL: { + type: "sst.sst.Linkable" + value: string + } + AWS_SES_ACCESS_KEY_ID: { + type: "sst.sst.Secret" + value: string + } + AWS_SES_SECRET_ACCESS_KEY: { + type: "sst.sst.Secret" + value: string + } + Console: { + type: "sst.cloudflare.SolidStart" + url: string + } + Database: { + database: string + host: string + password: string + port: number + type: "sst.sst.Linkable" + username: string + } + Desktop: { + type: "sst.cloudflare.StaticSite" + url: string + } + EMAILOCTOPUS_API_KEY: { + type: "sst.sst.Secret" + value: string + } + GITHUB_APP_ID: { + type: "sst.sst.Secret" + value: string + } + GITHUB_APP_PRIVATE_KEY: { + type: "sst.sst.Secret" + value: string + } + GITHUB_CLIENT_ID_CONSOLE: { + type: "sst.sst.Secret" + value: string + } + GITHUB_CLIENT_SECRET_CONSOLE: { + type: "sst.sst.Secret" + value: string + } + GOOGLE_CLIENT_ID: { + type: "sst.sst.Secret" + value: string + } + HONEYCOMB_API_KEY: { + type: "sst.sst.Secret" + value: string + } + STRIPE_SECRET_KEY: { + type: "sst.sst.Secret" + value: string + } + STRIPE_WEBHOOK_SECRET: { + type: "sst.sst.Linkable" + value: string + } + Web: { + type: "sst.cloudflare.Astro" + url: string + } + ZEN_MODELS1: { + type: "sst.sst.Secret" + value: string + } + ZEN_MODELS2: { + type: "sst.sst.Secret" + value: string } } } -// cloudflare -import * as cloudflare from "@cloudflare/workers-types"; +// cloudflare +import * as cloudflare from "@cloudflare/workers-types" declare module "sst" { export interface Resource { - "Api": cloudflare.Service - "AuthApi": cloudflare.Service - "AuthStorage": cloudflare.KVNamespace - "Bucket": cloudflare.R2Bucket - "LogProcessor": cloudflare.Service + Api: cloudflare.Service + AuthApi: cloudflare.Service + AuthStorage: cloudflare.KVNamespace + Bucket: cloudflare.R2Bucket + LogProcessor: cloudflare.Service } } import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/opencode/script/postinstall.mjs b/packages/opencode/script/postinstall.mjs index b875d158f..41865273d 100644 --- a/packages/opencode/script/postinstall.mjs +++ b/packages/opencode/script/postinstall.mjs @@ -77,7 +77,8 @@ async function regenerateWindowsCmdWrappers() { // npm_config_global is string | undefined // if it exists, the value is true - const isGlobal = process.env.npm_config_global === "true" || pkgPath.includes(path.join("npm", "node_modules")) + const isGlobal = + process.env.npm_config_global === "true" || pkgPath.includes(path.join("npm", "node_modules")) // The npm rebuild command does 2 things - Execute lifecycle scripts and rebuild bin links // We want to skip lifecycle scripts to avoid infinite loops, so we use --ignore-scripts @@ -93,7 +94,9 @@ async function regenerateWindowsCmdWrappers() { console.log("Successfully rebuilt npm bin links") } catch (error) { console.error("Error rebuilding npm links:", error.message) - console.error("npm rebuild failed. You may need to manually run: npm rebuild opencode-ai --ignore-scripts") + console.error( + "npm rebuild failed. You may need to manually run: npm rebuild opencode-ai --ignore-scripts", + ) } } diff --git a/packages/opencode/script/schema.ts b/packages/opencode/script/schema.ts index 585701c95..48bf65442 100755 --- a/packages/opencode/script/schema.ts +++ b/packages/opencode/script/schema.ts @@ -19,12 +19,23 @@ const result = z.toJSONSchema(Config.Info, { const schema = ctx.jsonSchema // Preserve strictness: set additionalProperties: false for objects - if (schema && typeof schema === "object" && schema.type === "object" && schema.additionalProperties === undefined) { + if ( + schema && + typeof schema === "object" && + schema.type === "object" && + schema.additionalProperties === undefined + ) { schema.additionalProperties = false } // Add examples and default descriptions for string fields with defaults - if (schema && typeof schema === "object" && "type" in schema && schema.type === "string" && schema?.default) { + if ( + schema && + typeof schema === "object" && + "type" in schema && + schema.type === "string" && + schema?.default + ) { if (!schema.examples) { schema.examples = [schema.default] } diff --git a/packages/opencode/src/agent/agent.ts b/packages/opencode/src/agent/agent.ts index a6933708b..16f401629 100644 --- a/packages/opencode/src/agent/agent.ts +++ b/packages/opencode/src/agent/agent.ts @@ -143,7 +143,18 @@ export namespace Agent { tools: {}, builtIn: false, } - const { name, model, prompt, tools, description, temperature, top_p, mode, permission, ...extra } = value + const { + name, + model, + prompt, + tools, + description, + temperature, + top_p, + mode, + permission, + ...extra + } = value item.options = { ...item.options, ...extra, @@ -212,7 +223,10 @@ export namespace Agent { } } -function mergeAgentPermissions(basePermission: any, overridePermission: any): Agent.Info["permission"] { +function mergeAgentPermissions( + basePermission: any, + overridePermission: any, +): Agent.Info["permission"] { if (typeof basePermission.bash === "string") { basePermission.bash = { "*": basePermission.bash, diff --git a/packages/opencode/src/bus/index.ts b/packages/opencode/src/bus/index.ts index f4dd3ed2c..c424eb879 100644 --- a/packages/opencode/src/bus/index.ts +++ b/packages/opencode/src/bus/index.ts @@ -19,7 +19,10 @@ export namespace Bus { const registry = new Map() - export function event(type: Type, properties: Properties) { + export function event( + type: Type, + properties: Properties, + ) { const result = { type, properties, @@ -70,7 +73,10 @@ export namespace Bus { export function subscribe( def: Definition, - callback: (event: { type: Definition["type"]; properties: z.infer }) => void, + callback: (event: { + type: Definition["type"] + properties: z.infer + }) => void, ) { return raw(def.type, callback) } diff --git a/packages/opencode/src/cli/cmd/auth.ts b/packages/opencode/src/cli/cmd/auth.ts index aa833e977..b4c47f0a4 100644 --- a/packages/opencode/src/cli/cmd/auth.ts +++ b/packages/opencode/src/cli/cmd/auth.ts @@ -14,7 +14,11 @@ export const AuthCommand = cmd({ command: "auth", describe: "manage credentials", builder: (yargs) => - yargs.command(AuthLoginCommand).command(AuthLogoutCommand).command(AuthListCommand).demandCommand(), + yargs + .command(AuthLoginCommand) + .command(AuthLogoutCommand) + .command(AuthListCommand) + .demandCommand(), async handler() {}, }) @@ -60,7 +64,9 @@ export const AuthListCommand = cmd({ prompts.log.info(`${provider} ${UI.Style.TEXT_DIM}${envVar}`) } - prompts.outro(`${activeEnvVars.length} environment variable` + (activeEnvVars.length === 1 ? "" : "s")) + prompts.outro( + `${activeEnvVars.length} environment variable` + (activeEnvVars.length === 1 ? "" : "s"), + ) } }, }) @@ -80,7 +86,9 @@ export const AuthLoginCommand = cmd({ UI.empty() prompts.intro("Add credential") if (args.url) { - const wellknown = await fetch(`${args.url}/.well-known/opencode`).then((x) => x.json() as any) + const wellknown = await fetch(`${args.url}/.well-known/opencode`).then( + (x) => x.json() as any, + ) prompts.log.info(`Running \`${wellknown.auth.command.join(" ")}\``) const proc = Bun.spawn({ cmd: wellknown.auth.command, @@ -102,223 +110,224 @@ export const AuthLoginCommand = cmd({ prompts.outro("Done") return } - await ModelsDev.refresh().catch(() => {}) - const providers = await ModelsDev.get() - const priority: Record = { - opencode: 0, - anthropic: 1, - "github-copilot": 2, - openai: 3, - google: 4, - openrouter: 5, - vercel: 6, - } - let provider = await prompts.autocomplete({ - message: "Select provider", - maxItems: 8, - options: [ - ...pipe( - providers, - values(), - sortBy( - (x) => priority[x.id] ?? 99, - (x) => x.name ?? x.id, + await ModelsDev.refresh().catch(() => {}) + const providers = await ModelsDev.get() + const priority: Record = { + opencode: 0, + anthropic: 1, + "github-copilot": 2, + openai: 3, + google: 4, + openrouter: 5, + vercel: 6, + } + let provider = await prompts.autocomplete({ + message: "Select provider", + maxItems: 8, + options: [ + ...pipe( + providers, + values(), + sortBy( + (x) => priority[x.id] ?? 99, + (x) => x.name ?? x.id, + ), + map((x) => ({ + label: x.name, + value: x.id, + hint: priority[x.id] <= 1 ? "recommended" : undefined, + })), ), - map((x) => ({ - label: x.name, - value: x.id, - hint: priority[x.id] <= 1 ? "recommended" : undefined, - })), - ), - { - value: "other", - label: "Other", - }, - ], - }) + { + value: "other", + label: "Other", + }, + ], + }) - if (prompts.isCancel(provider)) throw new UI.CancelledError() + if (prompts.isCancel(provider)) throw new UI.CancelledError() - const plugin = await Plugin.list().then((x) => x.find((x) => x.auth?.provider === provider)) - if (plugin && plugin.auth) { - let index = 0 - if (plugin.auth.methods.length > 1) { - const method = await prompts.select({ - message: "Login method", - options: [ - ...plugin.auth.methods.map((x, index) => ({ - label: x.label, - value: index.toString(), - })), - ], - }) - if (prompts.isCancel(method)) throw new UI.CancelledError() - index = parseInt(method) - } - const method = plugin.auth.methods[index] + const plugin = await Plugin.list().then((x) => x.find((x) => x.auth?.provider === provider)) + if (plugin && plugin.auth) { + let index = 0 + if (plugin.auth.methods.length > 1) { + const method = await prompts.select({ + message: "Login method", + options: [ + ...plugin.auth.methods.map((x, index) => ({ + label: x.label, + value: index.toString(), + })), + ], + }) + if (prompts.isCancel(method)) throw new UI.CancelledError() + index = parseInt(method) + } + const method = plugin.auth.methods[index] - // Handle prompts for all auth types - await new Promise((resolve) => setTimeout(resolve, 10)) - const inputs: Record = {} - if (method.prompts) { - for (const prompt of method.prompts) { - if (prompt.condition && !prompt.condition(inputs)) { - continue - } - if (prompt.type === "select") { - const value = await prompts.select({ - message: prompt.message, - options: prompt.options, - }) - if (prompts.isCancel(value)) throw new UI.CancelledError() - inputs[prompt.key] = value - } else { - const value = await prompts.text({ - message: prompt.message, - placeholder: prompt.placeholder, - validate: prompt.validate ? (v) => prompt.validate!(v ?? "") : undefined, - }) - if (prompts.isCancel(value)) throw new UI.CancelledError() - inputs[prompt.key] = value + // Handle prompts for all auth types + await new Promise((resolve) => setTimeout(resolve, 10)) + const inputs: Record = {} + if (method.prompts) { + for (const prompt of method.prompts) { + if (prompt.condition && !prompt.condition(inputs)) { + continue + } + if (prompt.type === "select") { + const value = await prompts.select({ + message: prompt.message, + options: prompt.options, + }) + if (prompts.isCancel(value)) throw new UI.CancelledError() + inputs[prompt.key] = value + } else { + const value = await prompts.text({ + message: prompt.message, + placeholder: prompt.placeholder, + validate: prompt.validate ? (v) => prompt.validate!(v ?? "") : undefined, + }) + if (prompts.isCancel(value)) throw new UI.CancelledError() + inputs[prompt.key] = value + } } } - } - if (method.type === "oauth") { - const authorize = await method.authorize(inputs) - - if (authorize.url) { - prompts.log.info("Go to: " + authorize.url) - } + if (method.type === "oauth") { + const authorize = await method.authorize(inputs) - if (authorize.method === "auto") { - if (authorize.instructions) { - prompts.log.info(authorize.instructions) + if (authorize.url) { + prompts.log.info("Go to: " + authorize.url) } - const spinner = prompts.spinner() - spinner.start("Waiting for authorization...") - const result = await authorize.callback() - if (result.type === "failed") { - spinner.stop("Failed to authorize", 1) + + if (authorize.method === "auto") { + if (authorize.instructions) { + prompts.log.info(authorize.instructions) + } + const spinner = prompts.spinner() + spinner.start("Waiting for authorization...") + const result = await authorize.callback() + if (result.type === "failed") { + spinner.stop("Failed to authorize", 1) + } + if (result.type === "success") { + const saveProvider = result.provider ?? provider + if ("refresh" in result) { + const { type: _, provider: __, refresh, access, expires, ...extraFields } = result + await Auth.set(saveProvider, { + type: "oauth", + refresh, + access, + expires, + ...extraFields, + }) + } + if ("key" in result) { + await Auth.set(saveProvider, { + type: "api", + key: result.key, + }) + } + spinner.stop("Login successful") + } } - if (result.type === "success") { - const saveProvider = result.provider ?? provider - if ("refresh" in result) { - const { type: _, provider: __, refresh, access, expires, ...extraFields } = result - await Auth.set(saveProvider, { - type: "oauth", - refresh, - access, - expires, - ...extraFields, - }) + + if (authorize.method === "code") { + const code = await prompts.text({ + message: "Paste the authorization code here: ", + validate: (x) => (x && x.length > 0 ? undefined : "Required"), + }) + if (prompts.isCancel(code)) throw new UI.CancelledError() + const result = await authorize.callback(code) + if (result.type === "failed") { + prompts.log.error("Failed to authorize") } - if ("key" in result) { - await Auth.set(saveProvider, { - type: "api", - key: result.key, - }) + if (result.type === "success") { + const saveProvider = result.provider ?? provider + if ("refresh" in result) { + const { type: _, provider: __, refresh, access, expires, ...extraFields } = result + await Auth.set(saveProvider, { + type: "oauth", + refresh, + access, + expires, + ...extraFields, + }) + } + if ("key" in result) { + await Auth.set(saveProvider, { + type: "api", + key: result.key, + }) + } + prompts.log.success("Login successful") } - spinner.stop("Login successful") } + + prompts.outro("Done") + return } - if (authorize.method === "code") { - const code = await prompts.text({ - message: "Paste the authorization code here: ", - validate: (x) => (x && x.length > 0 ? undefined : "Required"), - }) - if (prompts.isCancel(code)) throw new UI.CancelledError() - const result = await authorize.callback(code) - if (result.type === "failed") { - prompts.log.error("Failed to authorize") - } - if (result.type === "success") { - const saveProvider = result.provider ?? provider - if ("refresh" in result) { - const { type: _, provider: __, refresh, access, expires, ...extraFields } = result - await Auth.set(saveProvider, { - type: "oauth", - refresh, - access, - expires, - ...extraFields, - }) + if (method.type === "api") { + if (method.authorize) { + const result = await method.authorize(inputs) + if (result.type === "failed") { + prompts.log.error("Failed to authorize") } - if ("key" in result) { + if (result.type === "success") { + const saveProvider = result.provider ?? provider await Auth.set(saveProvider, { type: "api", key: result.key, }) + prompts.log.success("Login successful") } - prompts.log.success("Login successful") + prompts.outro("Done") + return } } + } + if (provider === "other") { + provider = await prompts.text({ + message: "Enter provider id", + validate: (x) => + x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only", + }) + if (prompts.isCancel(provider)) throw new UI.CancelledError() + provider = provider.replace(/^@ai-sdk\//, "") + if (prompts.isCancel(provider)) throw new UI.CancelledError() + prompts.log.warn( + `This only stores a credential for ${provider} - you will need configure it in opencode.json, check the docs for examples.`, + ) + } + + if (provider === "amazon-bedrock") { + prompts.log.info( + "Amazon bedrock can be configured with standard AWS environment variables like AWS_BEARER_TOKEN_BEDROCK, AWS_PROFILE or AWS_ACCESS_KEY_ID", + ) prompts.outro("Done") return } - if (method.type === "api") { - if (method.authorize) { - const result = await method.authorize(inputs) - if (result.type === "failed") { - prompts.log.error("Failed to authorize") - } - if (result.type === "success") { - const saveProvider = result.provider ?? provider - await Auth.set(saveProvider, { - type: "api", - key: result.key, - }) - prompts.log.success("Login successful") - } - prompts.outro("Done") - return - } + if (provider === "opencode") { + prompts.log.info("Create an api key at https://opencode.ai/auth") + } + + if (provider === "vercel") { + prompts.log.info("You can create an api key at https://vercel.link/ai-gateway-token") } - } - if (provider === "other") { - provider = await prompts.text({ - message: "Enter provider id", - validate: (x) => (x && x.match(/^[0-9a-z-]+$/) ? undefined : "a-z, 0-9 and hyphens only"), + const key = await prompts.password({ + message: "Enter your API key", + validate: (x) => (x && x.length > 0 ? undefined : "Required"), + }) + if (prompts.isCancel(key)) throw new UI.CancelledError() + await Auth.set(provider, { + type: "api", + key, }) - if (prompts.isCancel(provider)) throw new UI.CancelledError() - provider = provider.replace(/^@ai-sdk\//, "") - if (prompts.isCancel(provider)) throw new UI.CancelledError() - prompts.log.warn( - `This only stores a credential for ${provider} - you will need configure it in opencode.json, check the docs for examples.`, - ) - } - if (provider === "amazon-bedrock") { - prompts.log.info( - "Amazon bedrock can be configured with standard AWS environment variables like AWS_BEARER_TOKEN_BEDROCK, AWS_PROFILE or AWS_ACCESS_KEY_ID", - ) prompts.outro("Done") - return - } - - if (provider === "opencode") { - prompts.log.info("Create an api key at https://opencode.ai/auth") - } - - if (provider === "vercel") { - prompts.log.info("You can create an api key at https://vercel.link/ai-gateway-token") - } - - const key = await prompts.password({ - message: "Enter your API key", - validate: (x) => (x && x.length > 0 ? undefined : "Required"), - }) - if (prompts.isCancel(key)) throw new UI.CancelledError() - await Auth.set(provider, { - type: "api", - key, - }) - - prompts.outro("Done") }, }) }, diff --git a/packages/opencode/src/cli/cmd/debug/lsp.ts b/packages/opencode/src/cli/cmd/debug/lsp.ts index 2f5977195..8492395d1 100644 --- a/packages/opencode/src/cli/cmd/debug/lsp.ts +++ b/packages/opencode/src/cli/cmd/debug/lsp.ts @@ -7,7 +7,11 @@ import { EOL } from "os" export const LSPCommand = cmd({ command: "lsp", builder: (yargs) => - yargs.command(DiagnosticsCommand).command(SymbolsCommand).command(DocumentSymbolsCommand).demandCommand(), + yargs + .command(DiagnosticsCommand) + .command(SymbolsCommand) + .command(DocumentSymbolsCommand) + .demandCommand(), async handler() {}, }) diff --git a/packages/opencode/src/cli/cmd/debug/ripgrep.ts b/packages/opencode/src/cli/cmd/debug/ripgrep.ts index 66cfba20d..7c1d0d96a 100644 --- a/packages/opencode/src/cli/cmd/debug/ripgrep.ts +++ b/packages/opencode/src/cli/cmd/debug/ripgrep.ts @@ -6,7 +6,8 @@ import { cmd } from "../cmd" export const RipgrepCommand = cmd({ command: "rg", - builder: (yargs) => yargs.command(TreeCommand).command(FilesCommand).command(SearchCommand).demandCommand(), + builder: (yargs) => + yargs.command(TreeCommand).command(FilesCommand).command(SearchCommand).demandCommand(), async handler() {}, }) @@ -18,7 +19,9 @@ const TreeCommand = cmd({ }), async handler(args) { await bootstrap(process.cwd(), async () => { - process.stdout.write(await Ripgrep.tree({ cwd: Instance.directory, limit: args.limit }) + EOL) + process.stdout.write( + (await Ripgrep.tree({ cwd: Instance.directory, limit: args.limit })) + EOL, + ) }) }, }) diff --git a/packages/opencode/src/cli/cmd/debug/snapshot.ts b/packages/opencode/src/cli/cmd/debug/snapshot.ts index 1849fe270..b114122b7 100644 --- a/packages/opencode/src/cli/cmd/debug/snapshot.ts +++ b/packages/opencode/src/cli/cmd/debug/snapshot.ts @@ -4,7 +4,8 @@ import { cmd } from "../cmd" export const SnapshotCommand = cmd({ command: "snapshot", - builder: (yargs) => yargs.command(TrackCommand).command(PatchCommand).command(DiffCommand).demandCommand(), + builder: (yargs) => + yargs.command(TrackCommand).command(PatchCommand).command(DiffCommand).demandCommand(), async handler() {}, }) diff --git a/packages/opencode/src/cli/cmd/generate.ts b/packages/opencode/src/cli/cmd/generate.ts index 0cefb2533..c29a22a82 100644 --- a/packages/opencode/src/cli/cmd/generate.ts +++ b/packages/opencode/src/cli/cmd/generate.ts @@ -6,7 +6,7 @@ export const GenerateCommand = { handler: async () => { const specs = await Server.openapi() const json = JSON.stringify(specs, null, 2) - + // Wait for stdout to finish writing before process.exit() is called await new Promise((resolve, reject) => { process.stdout.write(json, (err) => { diff --git a/packages/opencode/src/cli/cmd/github.ts b/packages/opencode/src/cli/cmd/github.ts index cd3ceb94b..6fbeee2ea 100644 --- a/packages/opencode/src/cli/cmd/github.ts +++ b/packages/opencode/src/cli/cmd/github.ts @@ -189,7 +189,9 @@ export const GithubInstallCommand = cmd({ async function getAppInfo() { const project = Instance.project if (project.vcs !== "git") { - prompts.log.error(`Could not find git repository. Please run this command from a git repository.`) + prompts.log.error( + `Could not find git repository. Please run this command from a git repository.`, + ) throw new UI.CancelledError() } @@ -202,9 +204,13 @@ export const GithubInstallCommand = cmd({ // ie. git@github.com:sst/opencode // ie. ssh://git@github.com/sst/opencode.git // ie. ssh://git@github.com/sst/opencode - const parsed = info.match(/^(?:(?:https?|ssh):\/\/)?(?:git@)?github\.com[:/]([^/]+)\/([^/.]+?)(?:\.git)?$/) + const parsed = info.match( + /^(?:(?:https?|ssh):\/\/)?(?:git@)?github\.com[:/]([^/]+)\/([^/.]+?)(?:\.git)?$/, + ) if (!parsed) { - prompts.log.error(`Could not find git repository. Please run this command from a git repository.`) + prompts.log.error( + `Could not find git repository. Please run this command from a git repository.`, + ) throw new UI.CancelledError() } const [, owner, repo] = parsed @@ -445,7 +451,9 @@ export const GithubRunCommand = cmd({ const summary = await summarize(response) await pushToLocalBranch(summary) } - const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`)) + const hasShared = prData.comments.nodes.some((c) => + c.body.includes(`${shareBaseUrl}/s/${shareId}`), + ) await updateComment(`${response}${footer({ image: !hasShared })}`) } // Fork PR @@ -457,7 +465,9 @@ export const GithubRunCommand = cmd({ const summary = await summarize(response) await pushToForkBranch(summary, prData) } - const hasShared = prData.comments.nodes.some((c) => c.body.includes(`${shareBaseUrl}/s/${shareId}`)) + const hasShared = prData.comments.nodes.some((c) => + c.body.includes(`${shareBaseUrl}/s/${shareId}`), + ) await updateComment(`${response}${footer({ image: !hasShared })}`) } } @@ -547,8 +557,12 @@ export const GithubRunCommand = cmd({ // ie. Image // ie. [api.json](https://github.com/user-attachments/files/21433810/api.json) // ie. ![Image](https://github.com/user-attachments/assets/xxxx) - const mdMatches = prompt.matchAll(/!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi) - const tagMatches = prompt.matchAll(//gi) + const mdMatches = prompt.matchAll( + /!?\[.*?\]\((https:\/\/github\.com\/user-attachments\/[^)]+)\)/gi, + ) + const tagMatches = prompt.matchAll( + //gi, + ) const matches = [...mdMatches, ...tagMatches].sort((a, b) => a.index - b.index) console.log("Images", JSON.stringify(matches, null, 2)) @@ -573,7 +587,10 @@ export const GithubRunCommand = cmd({ // Replace img tag with file path, ie. @image.png const replacement = `@${filename}` - prompt = prompt.slice(0, start + offset) + replacement + prompt.slice(start + offset + tag.length) + prompt = + prompt.slice(0, start + offset) + + replacement + + prompt.slice(start + offset + tag.length) offset += replacement.length - tag.length const contentType = res.headers.get("content-type") @@ -856,7 +873,8 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` throw new Error(`Failed to check permissions for user ${actor}: ${error}`) } - if (!["admin", "write"].includes(permission)) throw new Error(`User ${actor} does not have write permissions`) + if (!["admin", "write"].includes(permission)) + throw new Error(`User ${actor} does not have write permissions`) } async function createComment() { @@ -904,7 +922,9 @@ Co-authored-by: ${actor} <${actor}@users.noreply.github.com>"` return `${titleAlt}\n` })() - const shareUrl = shareId ? `[opencode session](${shareBaseUrl}/s/${shareId})  |  ` : "" + const shareUrl = shareId + ? `[opencode session](${shareBaseUrl}/s/${shareId})  |  ` + : "" return `\n\n${image}${shareUrl}[github run](${runUrl})` } @@ -1080,9 +1100,13 @@ query($owner: String!, $repo: String!, $number: Int!) { }) .map((c) => `- ${c.author.login} at ${c.createdAt}: ${c.body}`) - const files = (pr.files.nodes || []).map((f) => `- ${f.path} (${f.changeType}) +${f.additions}/-${f.deletions}`) + const files = (pr.files.nodes || []).map( + (f) => `- ${f.path} (${f.changeType}) +${f.additions}/-${f.deletions}`, + ) const reviewData = (pr.reviews.nodes || []).map((r) => { - const comments = (r.comments.nodes || []).map((c) => ` - ${c.path}:${c.line ?? "?"}: ${c.body}`) + const comments = (r.comments.nodes || []).map( + (c) => ` - ${c.path}:${c.line ?? "?"}: ${c.body}`, + ) return [ `- ${r.author.login} at ${r.submittedAt}:`, ` - Review body: ${r.body}`, @@ -1104,9 +1128,15 @@ query($owner: String!, $repo: String!, $number: Int!) { `Deletions: ${pr.deletions}`, `Total Commits: ${pr.commits.totalCount}`, `Changed Files: ${pr.files.nodes.length} files`, - ...(comments.length > 0 ? ["", ...comments, ""] : []), - ...(files.length > 0 ? ["", ...files, ""] : []), - ...(reviewData.length > 0 ? ["", ...reviewData, ""] : []), + ...(comments.length > 0 + ? ["", ...comments, ""] + : []), + ...(files.length > 0 + ? ["", ...files, ""] + : []), + ...(reviewData.length > 0 + ? ["", ...reviewData, ""] + : []), "", ].join("\n") } diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index 39b0a55fd..9add4bff7 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -137,7 +137,9 @@ export const RunCommand = cmd({ const outputJsonEvent = (type: string, data: any) => { if (args.format === "json") { - process.stdout.write(JSON.stringify({ type, timestamp: Date.now(), sessionID, ...data }) + EOL) + process.stdout.write( + JSON.stringify({ type, timestamp: Date.now(), sessionID, ...data }) + EOL, + ) return true } return false @@ -157,7 +159,9 @@ export const RunCommand = cmd({ const [tool, color] = TOOL[part.tool] ?? [part.tool, UI.Style.TEXT_INFO_BOLD] const title = part.state.title || - (Object.keys(part.state.input).length > 0 ? JSON.stringify(part.state.input) : "Unknown") + (Object.keys(part.state.input).length > 0 + ? JSON.stringify(part.state.input) + : "Unknown") printEvent(color, tool, title) if (part.tool === "bash" && part.state.output?.trim()) { UI.println() @@ -280,7 +284,10 @@ export const RunCommand = cmd({ } const cfgResult = await sdk.config.get() - if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) { + if ( + cfgResult.data && + (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share) + ) { const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => { if (error instanceof Error && error.message.includes("disabled")) { UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message) @@ -333,7 +340,10 @@ export const RunCommand = cmd({ } const cfgResult = await sdk.config.get() - if (cfgResult.data && (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share)) { + if ( + cfgResult.data && + (cfgResult.data.share === "auto" || Flag.OPENCODE_AUTO_SHARE || args.share) + ) { const shareResult = await sdk.session.share({ path: { id: sessionID } }).catch((error) => { if (error instanceof Error && error.message.includes("disabled")) { UI.println(UI.Style.TEXT_DANGER_BOLD + "! " + error.message) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx index 04f2f6523..154995997 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-model.tsx @@ -52,7 +52,11 @@ export function DialogModel() { description: provider.name, category: provider.name, })), - filter((x) => Boolean(ref()?.filter) || !local.model.recent().find((y) => isDeepEqual(y, x.value))), + filter( + (x) => + Boolean(ref()?.filter) || + !local.model.recent().find((y) => isDeepEqual(y, x.value)), + ), ), ), ), diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx index 95792ad83..dc770ce28 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-session-list.tsx @@ -20,8 +20,8 @@ export function DialogSessionList() { const deleteKeybind = "ctrl+d" - const currentSessionID = createMemo(() => - route.data.type === "session" ? route.data.sessionID : undefined + const currentSessionID = createMemo(() => + route.data.type === "session" ? route.data.sessionID : undefined, ) const options = createMemo(() => { diff --git a/packages/opencode/src/cli/cmd/tui/component/logo.tsx b/packages/opencode/src/cli/cmd/tui/component/logo.tsx index 59db5fe7d..7cac51ecc 100644 --- a/packages/opencode/src/cli/cmd/tui/component/logo.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/logo.tsx @@ -3,9 +3,19 @@ import { TextAttributes } from "@opentui/core" import { For } from "solid-js" import { useTheme } from "@tui/context/theme" -const LOGO_LEFT = [` `, `█▀▀█ █▀▀█ █▀▀█ █▀▀▄`, `█░░█ █░░█ █▀▀▀ █░░█`, `▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀`] +const LOGO_LEFT = [ + ` `, + `█▀▀█ █▀▀█ █▀▀█ █▀▀▄`, + `█░░█ █░░█ █▀▀▀ █░░█`, + `▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀`, +] -const LOGO_RIGHT = [` ▄ `, `█▀▀▀ █▀▀█ █▀▀█ █▀▀█`, `█░░░ █░░█ █░░█ █▀▀▀`, `▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`] +const LOGO_RIGHT = [ + ` ▄ `, + `█▀▀▀ █▀▀█ █▀▀█ █▀▀█`, + `█░░░ █░░█ █░░█ █▀▀▀`, + `▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀`, +] export function Logo() { const { theme } = useTheme() diff --git a/packages/opencode/src/cli/cmd/tui/context/route.tsx b/packages/opencode/src/cli/cmd/tui/context/route.tsx index ef230dc98..dd8ede156 100644 --- a/packages/opencode/src/cli/cmd/tui/context/route.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/route.tsx @@ -17,13 +17,11 @@ export const { use: useRoute, provider: RouteProvider } = createSimpleContext({ init: (props: { data?: Route }) => { const [store, setStore] = createStore( props.data ?? - ( - process.env["OPENCODE_ROUTE"] + (process.env["OPENCODE_ROUTE"] ? JSON.parse(process.env["OPENCODE_ROUTE"]) : { - type: "home", - } - ), + type: "home", + }), ) return { diff --git a/packages/opencode/src/cli/cmd/tui/context/sync.tsx b/packages/opencode/src/cli/cmd/tui/context/sync.tsx index 5d8f1dac8..8ba73a4b6 100644 --- a/packages/opencode/src/cli/cmd/tui/context/sync.tsx +++ b/packages/opencode/src/cli/cmd/tui/context/sync.tsx @@ -269,6 +269,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ }, async sync(sessionID: string) { const now = Date.now() + console.log("syncing", sessionID) const [session, messages, todo, diff] = await Promise.all([ sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }), sdk.client.session.messages({ path: { id: sessionID } }), diff --git a/packages/opencode/src/cli/cmd/tui/context/theme/nightowl.json b/packages/opencode/src/cli/cmd/tui/context/theme/nightowl.json index 8eff42c5f..24c74733d 100644 --- a/packages/opencode/src/cli/cmd/tui/context/theme/nightowl.json +++ b/packages/opencode/src/cli/cmd/tui/context/theme/nightowl.json @@ -218,4 +218,4 @@ "light": "nightOwlFg" } } -} \ No newline at end of file +} diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx index ee2b77afc..cfdd4d694 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-message.tsx @@ -7,7 +7,9 @@ import { useRoute } from "@tui/context/route" export function DialogMessage(props: { messageID: string; sessionID: string }) { const sync = useSync() const sdk = useSDK() - const message = createMemo(() => sync.data.message[props.sessionID]?.find((x) => x.id === props.messageID)) + const message = createMemo(() => + sync.data.message[props.sessionID]?.find((x) => x.id === props.messageID), + ) const route = useRoute() return ( diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx index f5976cdf0..b440ee1a0 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/dialog-timeline.tsx @@ -19,7 +19,9 @@ export function DialogTimeline(props: { sessionID: string; onMove: (messageID: s const result = [] as DialogSelectOption[] for (const message of messages) { if (message.role !== "user") continue - const part = (sync.data.part[message.id] ?? []).find((x) => x.type === "text" && !x.synthetic) as TextPart + const part = (sync.data.part[message.id] ?? []).find( + (x) => x.type === "text" && !x.synthetic, + ) as TextPart if (!part) continue result.push({ title: part.text.replace(/\n/g, " "), @@ -33,5 +35,11 @@ export function DialogTimeline(props: { sessionID: string; onMove: (messageID: s return result }) - return props.onMove(option.value)} title="Timeline" options={options()} /> + return ( + props.onMove(option.value)} + title="Timeline" + options={options()} + /> + ) } diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 971ed8170..7aa8ab2f4 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -105,14 +105,15 @@ export function Session() { const sidebarVisible = createMemo(() => sidebar() === "show" || (sidebar() === "auto" && wide())) const contentWidth = createMemo(() => dimensions().width - (sidebarVisible() ? 42 : 0) - 4) - createEffect(() => { - sync.session.sync(route.sessionID).catch(() => { + createEffect(async () => { + await sync.session.sync(route.sessionID).catch(() => { toast.show({ message: `Session not found: ${route.sessionID}`, variant: "error", }) return navigate({ type: "home" }) }) + scroll.scrollBy(100_000) }) const toast = useToast() diff --git a/packages/opencode/src/cli/cmd/tui/spawn.ts b/packages/opencode/src/cli/cmd/tui/spawn.ts index 29c9a3590..6a1975131 100644 --- a/packages/opencode/src/cli/cmd/tui/spawn.ts +++ b/packages/opencode/src/cli/cmd/tui/spawn.ts @@ -41,7 +41,12 @@ export const TuiSpawnCommand = cmd({ ) cwd = new URL("../../../../", import.meta.url).pathname } else cmd.push(process.execPath) - cmd.push("attach", server.url.toString(), "--dir", args.project ? path.resolve(args.project) : process.cwd()) + cmd.push( + "attach", + server.url.toString(), + "--dir", + args.project ? path.resolve(args.project) : process.cwd(), + ) const proc = Bun.spawn({ cmd, cwd, diff --git a/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx b/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx index dd5b238b1..f79ae0555 100644 --- a/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx +++ b/packages/opencode/src/cli/cmd/tui/ui/dialog-confirm.tsx @@ -53,7 +53,9 @@ export function DialogConfirm(props: DialogConfirmProps) { dialog.clear() }} > - {Locale.titlecase(key)} + + {Locale.titlecase(key)} + )} diff --git a/packages/opencode/src/cli/cmd/tui/util/editor.ts b/packages/opencode/src/cli/cmd/tui/util/editor.ts index 0aa69dcd8..18a1400c9 100644 --- a/packages/opencode/src/cli/cmd/tui/util/editor.ts +++ b/packages/opencode/src/cli/cmd/tui/util/editor.ts @@ -5,7 +5,10 @@ import { join } from "node:path" import { CliRenderer } from "@opentui/core" export namespace Editor { - export async function open(opts: { value: string; renderer: CliRenderer }): Promise { + export async function open(opts: { + value: string + renderer: CliRenderer + }): Promise { const editor = process.env["EDITOR"] if (!editor) return diff --git a/packages/opencode/src/cli/cmd/upgrade.ts b/packages/opencode/src/cli/cmd/upgrade.ts index 65f3bab4d..f0ca48014 100644 --- a/packages/opencode/src/cli/cmd/upgrade.ts +++ b/packages/opencode/src/cli/cmd/upgrade.ts @@ -27,7 +27,9 @@ export const UpgradeCommand = { const detectedMethod = await Installation.method() const method = (args.method as Installation.Method) ?? detectedMethod if (method === "unknown") { - prompts.log.error(`opencode is installed to ${process.execPath} and may be managed by a package manager`) + prompts.log.error( + `opencode is installed to ${process.execPath} and may be managed by a package manager`, + ) const install = await prompts.select({ message: "Install anyways?", options: [ diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index efd31ccba..c2ee63c61 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -574,7 +574,10 @@ export namespace Config { .object({ apiKey: z.string().optional(), baseURL: z.string().optional(), - enterpriseUrl: z.string().optional().describe("GitHub Enterprise URL for copilot authentication"), + enterpriseUrl: z + .string() + .optional() + .describe("GitHub Enterprise URL for copilot authentication"), timeout: z .union([ z diff --git a/packages/opencode/src/file/fzf.ts b/packages/opencode/src/file/fzf.ts index cd0aa4fc8..702688b17 100644 --- a/packages/opencode/src/file/fzf.ts +++ b/packages/opencode/src/file/fzf.ts @@ -81,7 +81,9 @@ export namespace Fzf { }) } if (config.extension === "zip") { - const zipFileReader = new ZipReader(new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()]))) + const zipFileReader = new ZipReader( + new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])), + ) const entries = await zipFileReader.getEntries() let fzfEntry: any for (const entry of entries) { diff --git a/packages/opencode/src/file/ripgrep.ts b/packages/opencode/src/file/ripgrep.ts index 29014d199..84a45e386 100644 --- a/packages/opencode/src/file/ripgrep.ts +++ b/packages/opencode/src/file/ripgrep.ts @@ -161,7 +161,9 @@ export namespace Ripgrep { } if (config.extension === "zip") { if (config.extension === "zip") { - const zipFileReader = new ZipReader(new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()]))) + const zipFileReader = new ZipReader( + new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])), + ) const entries = await zipFileReader.getEntries() let rgEntry: any for (const entry of entries) { @@ -354,7 +356,12 @@ export namespace Ripgrep { return lines.join("\n") } - export async function search(input: { cwd: string; pattern: string; glob?: string[]; limit?: number }) { + export async function search(input: { + cwd: string + pattern: string + glob?: string[] + limit?: number + }) { const args = [`${await filepath()}`, "--json", "--hidden", "--glob='!.git/*'"] if (input.glob) { diff --git a/packages/opencode/src/file/time.ts b/packages/opencode/src/file/time.ts index 5cba5e820..fe1dcff49 100644 --- a/packages/opencode/src/file/time.ts +++ b/packages/opencode/src/file/time.ts @@ -27,7 +27,10 @@ export namespace FileTime { export async function assert(sessionID: string, filepath: string) { const time = get(sessionID, filepath) - if (!time) throw new Error(`You must read the file ${filepath} before overwriting it. Use the Read tool first`) + if (!time) + throw new Error( + `You must read the file ${filepath} before overwriting it. Use the Read tool first`, + ) const stats = await Bun.file(filepath).stat() if (stats.mtime.getTime() > time.getTime()) { throw new Error( diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts index d5985b582..e9304af70 100644 --- a/packages/opencode/src/file/watcher.ts +++ b/packages/opencode/src/file/watcher.ts @@ -51,8 +51,10 @@ export namespace FileWatcher { for (const evt of evts) { log.info("event", evt) if (evt.type === "create") Bus.publish(Event.Updated, { file: evt.path, event: "add" }) - if (evt.type === "update") Bus.publish(Event.Updated, { file: evt.path, event: "change" }) - if (evt.type === "delete") Bus.publish(Event.Updated, { file: evt.path, event: "unlink" }) + if (evt.type === "update") + Bus.publish(Event.Updated, { file: evt.path, event: "change" }) + if (evt.type === "delete") + Bus.publish(Event.Updated, { file: evt.path, event: "unlink" }) } }, { diff --git a/packages/opencode/src/id/id.ts b/packages/opencode/src/id/id.ts index 99eb6c9ff..76b6a46be 100644 --- a/packages/opencode/src/id/id.ts +++ b/packages/opencode/src/id/id.ts @@ -49,7 +49,11 @@ export namespace Identifier { return result } - export function create(prefix: keyof typeof prefixes, descending: boolean, timestamp?: number): string { + export function create( + prefix: keyof typeof prefixes, + descending: boolean, + timestamp?: number, + ): string { const currentTimestamp = timestamp ?? Date.now() if (currentTimestamp !== lastTimestamp) { diff --git a/packages/opencode/src/lsp/client.ts b/packages/opencode/src/lsp/client.ts index 1a6e2cb71..920cecc62 100644 --- a/packages/opencode/src/lsp/client.ts +++ b/packages/opencode/src/lsp/client.ts @@ -1,5 +1,9 @@ import path from "path" -import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc/node" +import { + createMessageConnection, + StreamMessageReader, + StreamMessageWriter, +} from "vscode-jsonrpc/node" import type { Diagnostic as VSCodeDiagnostic } from "vscode-languageserver-types" import { Log } from "../util/log" import { LANGUAGE_EXTENSIONS } from "./language" @@ -34,7 +38,11 @@ export namespace LSPClient { ), } - export async function create(input: { serverID: string; server: LSPServer.Handle; root: string }) { + export async function create(input: { + serverID: string + server: LSPServer.Handle + root: string + }) { const l = log.clone().tag("serverID", input.serverID) l.info("starting client") @@ -129,7 +137,9 @@ export namespace LSPClient { }, notify: { async open(input: { path: string }) { - input.path = path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path) + input.path = path.isAbsolute(input.path) + ? input.path + : path.resolve(Instance.directory, input.path) const file = Bun.file(input.path) const text = await file.text() const extension = path.extname(input.path) @@ -171,13 +181,18 @@ export namespace LSPClient { return diagnostics }, async waitForDiagnostics(input: { path: string }) { - input.path = path.isAbsolute(input.path) ? input.path : path.resolve(Instance.directory, input.path) + input.path = path.isAbsolute(input.path) + ? input.path + : path.resolve(Instance.directory, input.path) log.info("waiting for diagnostics", input) let unsub: () => void return await withTimeout( new Promise((resolve) => { unsub = Bus.subscribe(Event.Diagnostics, (event) => { - if (event.properties.path === input.path && event.properties.serverID === result.serverID) { + if ( + event.properties.path === input.path && + event.properties.serverID === result.serverID + ) { log.info("got diagnostics", input) unsub?.() resolve() diff --git a/packages/opencode/src/patch/index.ts b/packages/opencode/src/patch/index.ts index c6e25c5cc..46467e0ba 100644 --- a/packages/opencode/src/patch/index.ts +++ b/packages/opencode/src/patch/index.ts @@ -20,7 +20,7 @@ export namespace Patch { workdir?: string } - export type Hunk = + export type Hunk = | { type: "add"; path: string; contents: string } | { type: "delete"; path: string } | { type: "update"; path: string; move_path?: string; chunks: UpdateFileChunk[] } @@ -71,60 +71,66 @@ export namespace Patch { } // Parser implementation - function parsePatchHeader(lines: string[], startIdx: number): { filePath: string; movePath?: string; nextIdx: number } | null { + function parsePatchHeader( + lines: string[], + startIdx: number, + ): { filePath: string; movePath?: string; nextIdx: number } | null { const line = lines[startIdx] - + if (line.startsWith("*** Add File:")) { const filePath = line.split(":", 2)[1]?.trim() return filePath ? { filePath, nextIdx: startIdx + 1 } : null } - + if (line.startsWith("*** Delete File:")) { const filePath = line.split(":", 2)[1]?.trim() return filePath ? { filePath, nextIdx: startIdx + 1 } : null } - + if (line.startsWith("*** Update File:")) { const filePath = line.split(":", 2)[1]?.trim() let movePath: string | undefined let nextIdx = startIdx + 1 - + // Check for move directive if (nextIdx < lines.length && lines[nextIdx].startsWith("*** Move to:")) { movePath = lines[nextIdx].split(":", 2)[1]?.trim() nextIdx++ } - + return filePath ? { filePath, movePath, nextIdx } : null } - + return null } - function parseUpdateFileChunks(lines: string[], startIdx: number): { chunks: UpdateFileChunk[]; nextIdx: number } { + function parseUpdateFileChunks( + lines: string[], + startIdx: number, + ): { chunks: UpdateFileChunk[]; nextIdx: number } { const chunks: UpdateFileChunk[] = [] let i = startIdx - + while (i < lines.length && !lines[i].startsWith("***")) { if (lines[i].startsWith("@@")) { // Parse context line const contextLine = lines[i].substring(2).trim() i++ - + const oldLines: string[] = [] const newLines: string[] = [] let isEndOfFile = false - + // Parse change lines while (i < lines.length && !lines[i].startsWith("@@") && !lines[i].startsWith("***")) { const changeLine = lines[i] - + if (changeLine === "*** End of File") { isEndOfFile = true i++ break } - + if (changeLine.startsWith(" ")) { // Keep line - appears in both old and new const content = changeLine.substring(1) @@ -137,10 +143,10 @@ export namespace Patch { // Add line - only in new newLines.push(changeLine.substring(1)) } - + i++ } - + chunks.push({ old_lines: oldLines, new_lines: newLines, @@ -151,26 +157,29 @@ export namespace Patch { i++ } } - + return { chunks, nextIdx: i } } - function parseAddFileContent(lines: string[], startIdx: number): { content: string; nextIdx: number } { + function parseAddFileContent( + lines: string[], + startIdx: number, + ): { content: string; nextIdx: number } { let content = "" let i = startIdx - + while (i < lines.length && !lines[i].startsWith("***")) { if (lines[i].startsWith("+")) { content += lines[i].substring(1) + "\n" } i++ } - + // Remove trailing newline if (content.endsWith("\n")) { content = content.slice(0, -1) } - + return { content, nextIdx: i } } @@ -178,28 +187,28 @@ export namespace Patch { const lines = patchText.split("\n") const hunks: Hunk[] = [] let i = 0 - + // Look for Begin/End patch markers const beginMarker = "*** Begin Patch" const endMarker = "*** End Patch" - - const beginIdx = lines.findIndex(line => line.trim() === beginMarker) - const endIdx = lines.findIndex(line => line.trim() === endMarker) - + + const beginIdx = lines.findIndex((line) => line.trim() === beginMarker) + const endIdx = lines.findIndex((line) => line.trim() === endMarker) + if (beginIdx === -1 || endIdx === -1 || beginIdx >= endIdx) { throw new Error("Invalid patch format: missing Begin/End markers") } - + // Parse content between markers i = beginIdx + 1 - + while (i < endIdx) { const header = parsePatchHeader(lines, i) if (!header) { i++ continue } - + if (lines[i].startsWith("*** Add File:")) { const { content, nextIdx } = parseAddFileContent(lines, header.nextIdx) hunks.push({ @@ -227,18 +236,19 @@ export namespace Patch { i++ } } - + return { hunks } } // Apply patch functionality - export function maybeParseApplyPatch(argv: string[]): + export function maybeParseApplyPatch( + argv: string[], + ): | { type: MaybeApplyPatch.Body; args: ApplyPatchArgs } | { type: MaybeApplyPatch.PatchParseError; error: Error } | { type: MaybeApplyPatch.NotApplyPatch } { - const APPLY_PATCH_COMMANDS = ["apply_patch", "applypatch"] - + // Direct invocation: apply_patch if (argv.length === 2 && APPLY_PATCH_COMMANDS.includes(argv[0])) { try { @@ -257,13 +267,13 @@ export namespace Patch { } } } - + // Bash heredoc form: bash -lc 'apply_patch <<"EOF" ...' if (argv.length === 3 && argv[0] === "bash" && argv[1] === "-lc") { // Simple extraction - in real implementation would need proper bash parsing const script = argv[2] const heredocMatch = script.match(/apply_patch\s*<<['"](\w+)['"]\s*\n([\s\S]*?)\n\1/) - + if (heredocMatch) { const patchContent = heredocMatch[2] try { @@ -283,7 +293,7 @@ export namespace Patch { } } } - + return { type: MaybeApplyPatch.NotApplyPatch } } @@ -293,7 +303,10 @@ export namespace Patch { content: string } - export function deriveNewContentsFromChunks(filePath: string, chunks: UpdateFileChunk[]): ApplyPatchFileUpdate { + export function deriveNewContentsFromChunks( + filePath: string, + chunks: UpdateFileChunk[], + ): ApplyPatchFileUpdate { // Read original file content let originalContent: string try { @@ -301,37 +314,41 @@ export namespace Patch { } catch (error) { throw new Error(`Failed to read file ${filePath}: ${error}`) } - + let originalLines = originalContent.split("\n") - + // Drop trailing empty element for consistent line counting if (originalLines.length > 0 && originalLines[originalLines.length - 1] === "") { originalLines.pop() } - + const replacements = computeReplacements(originalLines, filePath, chunks) let newLines = applyReplacements(originalLines, replacements) - + // Ensure trailing newline if (newLines.length === 0 || newLines[newLines.length - 1] !== "") { newLines.push("") } - + const newContent = newLines.join("\n") - + // Generate unified diff const unifiedDiff = generateUnifiedDiff(originalContent, newContent) - + return { unified_diff: unifiedDiff, content: newContent, } } - function computeReplacements(originalLines: string[], filePath: string, chunks: UpdateFileChunk[]): Array<[number, number, string[]]> { + function computeReplacements( + originalLines: string[], + filePath: string, + chunks: UpdateFileChunk[], + ): Array<[number, number, string[]]> { const replacements: Array<[number, number, string[]]> = [] let lineIndex = 0 - + for (const chunk of chunks) { // Handle context-based seeking if (chunk.change_context) { @@ -341,21 +358,22 @@ export namespace Patch { } lineIndex = contextIdx + 1 } - + // Handle pure addition (no old lines) if (chunk.old_lines.length === 0) { - const insertionIdx = originalLines.length > 0 && originalLines[originalLines.length - 1] === "" - ? originalLines.length - 1 - : originalLines.length + const insertionIdx = + originalLines.length > 0 && originalLines[originalLines.length - 1] === "" + ? originalLines.length - 1 + : originalLines.length replacements.push([insertionIdx, 0, chunk.new_lines]) continue } - + // Try to match old lines in the file let pattern = chunk.old_lines let newSlice = chunk.new_lines let found = seekSequence(originalLines, pattern, lineIndex) - + // Retry without trailing empty line if not found if (found === -1 && pattern.length > 0 && pattern[pattern.length - 1] === "") { pattern = pattern.slice(0, -1) @@ -364,79 +382,82 @@ export namespace Patch { } found = seekSequence(originalLines, pattern, lineIndex) } - + if (found !== -1) { replacements.push([found, pattern.length, newSlice]) lineIndex = found + pattern.length } else { throw new Error( - `Failed to find expected lines in ${filePath}:\n${chunk.old_lines.join("\n")}` + `Failed to find expected lines in ${filePath}:\n${chunk.old_lines.join("\n")}`, ) } } - + // Sort replacements by index to apply in order replacements.sort((a, b) => a[0] - b[0]) - + return replacements } - function applyReplacements(lines: string[], replacements: Array<[number, number, string[]]>): string[] { + function applyReplacements( + lines: string[], + replacements: Array<[number, number, string[]]>, + ): string[] { // Apply replacements in reverse order to avoid index shifting const result = [...lines] - + for (let i = replacements.length - 1; i >= 0; i--) { const [startIdx, oldLen, newSegment] = replacements[i] - + // Remove old lines result.splice(startIdx, oldLen) - + // Insert new lines for (let j = 0; j < newSegment.length; j++) { result.splice(startIdx + j, 0, newSegment[j]) } } - + return result } function seekSequence(lines: string[], pattern: string[], startIndex: number): number { if (pattern.length === 0) return -1 - + // Simple substring search implementation for (let i = startIndex; i <= lines.length - pattern.length; i++) { let matches = true - + for (let j = 0; j < pattern.length; j++) { if (lines[i + j] !== pattern[j]) { matches = false break } } - + if (matches) { return i } } - + return -1 } function generateUnifiedDiff(oldContent: string, newContent: string): string { const oldLines = oldContent.split("\n") const newLines = newContent.split("\n") - + // Simple diff generation - in a real implementation you'd use a proper diff algorithm let diff = "@@ -1 +1 @@\n" - + // Find changes (simplified approach) const maxLen = Math.max(oldLines.length, newLines.length) let hasChanges = false - + for (let i = 0; i < maxLen; i++) { const oldLine = oldLines[i] || "" const newLine = newLines[i] || "" - + if (oldLine !== newLine) { if (oldLine) diff += `-${oldLine}\n` if (newLine) diff += `+${newLine}\n` @@ -445,7 +466,7 @@ export namespace Patch { diff += ` ${oldLine}\n` } } - + return hasChanges ? diff : "" } @@ -454,11 +475,11 @@ export namespace Patch { if (hunks.length === 0) { throw new Error("No files were modified.") } - + const added: string[] = [] const modified: string[] = [] const deleted: string[] = [] - + for (const hunk of hunks) { switch (hunk.type) { case "add": @@ -467,28 +488,28 @@ export namespace Patch { if (addDir !== "." && addDir !== "/") { await fs.mkdir(addDir, { recursive: true }) } - + await fs.writeFile(hunk.path, hunk.contents, "utf-8") added.push(hunk.path) log.info(`Added file: ${hunk.path}`) break - + case "delete": await fs.unlink(hunk.path) deleted.push(hunk.path) log.info(`Deleted file: ${hunk.path}`) break - + case "update": const fileUpdate = deriveNewContentsFromChunks(hunk.path, hunk.chunks) - + if (hunk.move_path) { // Handle file move const moveDir = path.dirname(hunk.move_path) if (moveDir !== "." && moveDir !== "/") { await fs.mkdir(moveDir, { recursive: true }) } - + await fs.writeFile(hunk.move_path, fileUpdate.content, "utf-8") await fs.unlink(hunk.path) modified.push(hunk.move_path) @@ -502,7 +523,7 @@ export namespace Patch { break } } - + return { added, modified, deleted } } @@ -513,7 +534,10 @@ export namespace Patch { } // Async version of maybeParseApplyPatchVerified - export async function maybeParseApplyPatchVerified(argv: string[], cwd: string): Promise< + export async function maybeParseApplyPatchVerified( + argv: string[], + cwd: string, + ): Promise< | { type: MaybeApplyPatchVerified.Body; action: ApplyPatchAction } | { type: MaybeApplyPatchVerified.CorrectnessError; error: Error } | { type: MaybeApplyPatchVerified.NotApplyPatch } @@ -530,18 +554,21 @@ export namespace Patch { // Not a patch, continue } } - + const result = maybeParseApplyPatch(argv) - + switch (result.type) { case MaybeApplyPatch.Body: const { args } = result const effectiveCwd = args.workdir ? path.resolve(cwd, args.workdir) : cwd const changes = new Map() - + for (const hunk of args.hunks) { - const resolvedPath = path.resolve(effectiveCwd, hunk.type === "update" && hunk.move_path ? hunk.move_path : hunk.path) - + const resolvedPath = path.resolve( + effectiveCwd, + hunk.type === "update" && hunk.move_path ? hunk.move_path : hunk.path, + ) + switch (hunk.type) { case "add": changes.set(resolvedPath, { @@ -549,7 +576,7 @@ export namespace Patch { content: hunk.contents, }) break - + case "delete": // For delete, we need to read the current content const deletePath = path.resolve(effectiveCwd, hunk.path) @@ -566,7 +593,7 @@ export namespace Patch { } } break - + case "update": const updatePath = path.resolve(effectiveCwd, hunk.path) try { @@ -574,7 +601,9 @@ export namespace Patch { changes.set(resolvedPath, { type: "update", unified_diff: fileUpdate.unified_diff, - move_path: hunk.move_path ? path.resolve(effectiveCwd, hunk.move_path) : undefined, + move_path: hunk.move_path + ? path.resolve(effectiveCwd, hunk.move_path) + : undefined, new_content: fileUpdate.content, }) } catch (error) { @@ -586,7 +615,7 @@ export namespace Patch { break } } - + return { type: MaybeApplyPatchVerified.Body, action: { @@ -595,15 +624,15 @@ export namespace Patch { cwd: effectiveCwd, }, } - + case MaybeApplyPatch.PatchParseError: return { type: MaybeApplyPatchVerified.CorrectnessError, error: result.error, } - + case MaybeApplyPatch.NotApplyPatch: return { type: MaybeApplyPatchVerified.NotApplyPatch } } } -} \ No newline at end of file +} diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index 6212edff8..9e095f5bf 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -40,7 +40,8 @@ export namespace ProviderTransform { } for (const msg of unique([...system, ...final])) { - const shouldUseContentOptions = providerID !== "anthropic" && Array.isArray(msg.content) && msg.content.length > 0 + const shouldUseContentOptions = + providerID !== "anthropic" && Array.isArray(msg.content) && msg.content.length > 0 if (shouldUseContentOptions) { const lastContent = msg.content[msg.content.length - 1] @@ -84,7 +85,11 @@ export namespace ProviderTransform { return undefined } - export function options(providerID: string, modelID: string, sessionID: string): Record | undefined { + export function options( + providerID: string, + modelID: string, + sessionID: string, + ): Record | undefined { const result: Record = {} if (providerID === "openai") { @@ -109,7 +114,11 @@ export namespace ProviderTransform { return result } - export function providerOptions(npm: string | undefined, providerID: string, options: { [x: string]: any }) { + export function providerOptions( + npm: string | undefined, + providerID: string, + options: { [x: string]: any }, + ) { switch (npm) { case "@ai-sdk/openai": case "@ai-sdk/azure": @@ -142,7 +151,8 @@ export namespace ProviderTransform { if (providerID === "anthropic") { const thinking = options?.["thinking"] - const budgetTokens = typeof thinking?.["budgetTokens"] === "number" ? thinking["budgetTokens"] : 0 + const budgetTokens = + typeof thinking?.["budgetTokens"] === "number" ? thinking["budgetTokens"] : 0 const enabled = thinking?.["type"] === "enabled" if (enabled && budgetTokens > 0) { // Return text tokens so that text + thinking <= model cap, preferring 32k text when possible. diff --git a/packages/opencode/src/server/server.ts b/packages/opencode/src/server/server.ts index 59e066e15..308ed4380 100644 --- a/packages/opencode/src/server/server.ts +++ b/packages/opencode/src/server/server.ts @@ -755,7 +755,7 @@ export namespace Server { ), async (c) => { const messages = await Session.messages(c.req.valid("param").id) - return c.json(messages) + return c.json(messages.slice(-100)) }, ) .get( diff --git a/packages/opencode/src/session/compaction.ts b/packages/opencode/src/session/compaction.ts index cc6351675..021b544e0 100644 --- a/packages/opencode/src/session/compaction.ts +++ b/packages/opencode/src/session/compaction.ts @@ -1,4 +1,4 @@ -import { streamText, type ModelMessage, LoadAPIKeyError, type StreamTextResult, type Tool as AITool } from "ai" +import { streamText, type ModelMessage, type StreamTextResult, type Tool as AITool } from "ai" import { Session } from "." import { Identifier } from "../id/id" import { Instance } from "../project/instance" @@ -30,12 +30,17 @@ export namespace SessionCompaction { ), } - export function isOverflow(input: { tokens: MessageV2.Assistant["tokens"]; model: ModelsDev.Model }) { + export function isOverflow(input: { + tokens: MessageV2.Assistant["tokens"] + model: ModelsDev.Model + }) { if (Flag.OPENCODE_DISABLE_AUTOCOMPACT) return false const context = input.model.limit.context if (context === 0) return false const count = input.tokens.input + input.tokens.cache.read + input.tokens.output - const output = Math.min(input.model.limit.output, SessionPrompt.OUTPUT_TOKEN_MAX) || SessionPrompt.OUTPUT_TOKEN_MAX + const output = + Math.min(input.model.limit.output, SessionPrompt.OUTPUT_TOKEN_MAX) || + SessionPrompt.OUTPUT_TOKEN_MAX const usable = context - output return count > usable } @@ -87,9 +92,15 @@ export namespace SessionCompaction { } } - export async function run(input: { sessionID: string; providerID: string; modelID: string; signal?: AbortSignal }) { + export async function run(input: { + sessionID: string + providerID: string + modelID: string + signal?: AbortSignal + }) { if (!input.signal) SessionLock.assertUnlocked(input.sessionID) - await using lock = input.signal === undefined ? SessionLock.acquire({ sessionID: input.sessionID }) : undefined + await using lock = + input.signal === undefined ? SessionLock.acquire({ sessionID: input.sessionID }) : undefined const signal = input.signal ?? lock!.signal await Session.update(input.sessionID, (draft) => { @@ -113,7 +124,6 @@ export namespace SessionCompaction { role: "assistant", parentID: toSummarize.findLast((m) => m.info.role === "user")?.info.id!, sessionID: input.sessionID, - system, mode: "build", path: { cwd: Instance.directory, @@ -150,7 +160,11 @@ export namespace SessionCompaction { // set to 0, we handle loop maxRetries: 0, model: model.language, - providerOptions: ProviderTransform.providerOptions(model.npm, model.providerID, model.info.options), + providerOptions: ProviderTransform.providerOptions( + model.npm, + model.providerID, + model.info.options, + ), headers: model.info.headers, abortSignal: signal, onError(error) { @@ -230,7 +244,11 @@ export namespace SessionCompaction { error: e, }) const error = MessageV2.fromError(e, { providerID: input.providerID }) - if (retries.count < retries.max && MessageV2.APIError.isInstance(error) && error.data.isRetryable) { + if ( + retries.count < retries.max && + MessageV2.APIError.isInstance(error) && + error.data.isRetryable + ) { shouldRetry = true await Session.updatePart({ id: Identifier.ascending("part"), diff --git a/packages/opencode/src/session/lock.ts b/packages/opencode/src/session/lock.ts index ed024edab..22eb8187c 100644 --- a/packages/opencode/src/session/lock.ts +++ b/packages/opencode/src/session/lock.ts @@ -50,7 +50,10 @@ export namespace SessionLock { export function acquire(input: { sessionID: string }) { const lock = get(input.sessionID) if (lock) { - throw new LockedError({ sessionID: input.sessionID, message: `Session ${input.sessionID} is locked` }) + throw new LockedError({ + sessionID: input.sessionID, + message: `Session ${input.sessionID} is locked`, + }) } const controller = new AbortController() state().locks.set(input.sessionID, { diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts index 3b28afe0f..f35735b71 100644 --- a/packages/opencode/src/session/message-v2.ts +++ b/packages/opencode/src/session/message-v2.ts @@ -2,14 +2,23 @@ import z from "zod" import { Bus } from "../bus" import { NamedError } from "../util/error" import { Message } from "./message" -import { APICallError, convertToModelMessages, LoadAPIKeyError, type ModelMessage, type UIMessage } from "ai" +import { + APICallError, + convertToModelMessages, + LoadAPIKeyError, + type ModelMessage, + type UIMessage, +} from "ai" import { Identifier } from "../id/id" import { LSP } from "../lsp" import { Snapshot } from "@/snapshot" export namespace MessageV2 { export const OutputLengthError = NamedError.create("MessageOutputLengthError", z.object({})) - export const AbortedError = NamedError.create("MessageAbortedError", z.object({ message: z.string() })) + export const AbortedError = NamedError.create( + "MessageAbortedError", + z.object({ message: z.string() }), + ) export const AuthError = NamedError.create( "ProviderAuthError", z.object({ @@ -242,7 +251,12 @@ export namespace MessageV2 { export type ToolStateError = z.infer export const ToolState = z - .discriminatedUnion("status", [ToolStatePending, ToolStateRunning, ToolStateCompleted, ToolStateError]) + .discriminatedUnion("status", [ + ToolStatePending, + ToolStateRunning, + ToolStateCompleted, + ToolStateError, + ]) .meta({ ref: "ToolState", }) @@ -313,7 +327,6 @@ export namespace MessageV2 { APIError.Schema, ]) .optional(), - system: z.string().array(), parentID: z.string(), modelID: z.string(), providerID: z.string(), @@ -397,7 +410,6 @@ export namespace MessageV2 { tokens: v1.metadata.assistant!.tokens, modelID: v1.metadata.assistant!.modelID, providerID: v1.metadata.assistant!.providerID, - system: v1.metadata.assistant!.system, mode: "build", error: v1.metadata.error, } @@ -440,7 +452,8 @@ export namespace MessageV2 { } } - const { title, time, ...metadata } = v1.metadata.tool[part.toolInvocation.toolCallId] ?? {} + const { title, time, ...metadata } = + v1.metadata.tool[part.toolInvocation.toolCallId] ?? {} if (part.toolInvocation.state === "call") { return { status: "running", @@ -541,7 +554,11 @@ export namespace MessageV2 { }, ] // text/plain and directory files are converted into text parts, ignore them - if (part.type === "file" && part.mime !== "text/plain" && part.mime !== "application/x-directory") + if ( + part.type === "file" && + part.mime !== "text/plain" && + part.mime !== "application/x-directory" + ) return [ { type: "file", @@ -600,7 +617,9 @@ export namespace MessageV2 { state: "output-available", toolCallId: part.callID, input: part.state.input, - output: part.state.time.compacted ? "[Old tool result content cleared]" : part.state.output, + output: part.state.time.compacted + ? "[Old tool result content cleared]" + : part.state.output, callProviderMetadata: part.metadata, }, ] diff --git a/packages/opencode/src/session/message.ts b/packages/opencode/src/session/message.ts index 4471f9235..baa8c00f1 100644 --- a/packages/opencode/src/session/message.ts +++ b/packages/opencode/src/session/message.ts @@ -51,9 +51,11 @@ export namespace Message { }) export type ToolResult = z.infer - export const ToolInvocation = z.discriminatedUnion("state", [ToolCall, ToolPartialCall, ToolResult]).meta({ - ref: "ToolInvocation", - }) + export const ToolInvocation = z + .discriminatedUnion("state", [ToolCall, ToolPartialCall, ToolResult]) + .meta({ + ref: "ToolInvocation", + }) export type ToolInvocation = z.infer export const TextPart = z @@ -122,7 +124,14 @@ export namespace Message { export type StepStartPart = z.infer export const MessagePart = z - .discriminatedUnion("type", [TextPart, ReasoningPart, ToolInvocationPart, SourceUrlPart, FilePart, StepStartPart]) + .discriminatedUnion("type", [ + TextPart, + ReasoningPart, + ToolInvocationPart, + SourceUrlPart, + FilePart, + StepStartPart, + ]) .meta({ ref: "MessagePart", }) @@ -140,7 +149,11 @@ export namespace Message { completed: z.number().optional(), }), error: z - .discriminatedUnion("name", [AuthError.Schema, NamedError.Unknown.Schema, OutputLengthError.Schema]) + .discriminatedUnion("name", [ + AuthError.Schema, + NamedError.Unknown.Schema, + OutputLengthError.Schema, + ]) .optional(), sessionID: z.string(), tool: z.record( diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index a70045349..9072135f6 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -961,7 +961,6 @@ export namespace SessionPrompt { id: Identifier.ascending("message"), parentID, role: "assistant", - system: input.system, mode: input.agent, path: { cwd: Instance.directory, @@ -1412,7 +1411,6 @@ export namespace SessionPrompt { id: Identifier.ascending("message"), sessionID: input.sessionID, parentID: userMsg.id, - system: [], mode: input.agent, cost: 0, path: { @@ -1709,7 +1707,6 @@ export namespace SessionPrompt { id: Identifier.ascending("message"), sessionID: input.sessionID, parentID: userMsg.id, - system: [], mode: agentName, cost: 0, path: { diff --git a/packages/opencode/src/session/revert.ts b/packages/opencode/src/session/revert.ts index a88b5f08f..7439d59cf 100644 --- a/packages/opencode/src/session/revert.ts +++ b/packages/opencode/src/session/revert.ts @@ -45,7 +45,9 @@ export namespace SessionRevert { if (!revert) { if ((msg.info.id === input.messageID && !input.partID) || part.id === input.partID) { // if no useful parts left in message, same as reverting whole message - const partID = remaining.some((item) => ["text", "tool"].includes(item.type)) ? input.partID : undefined + const partID = remaining.some((item) => ["text", "tool"].includes(item.type)) + ? input.partID + : undefined revert = { messageID: !partID && lastUser ? lastUser.id : msg.info.id, partID, diff --git a/packages/opencode/src/session/system.ts b/packages/opencode/src/session/system.ts index 3173dcac5..3bff0772b 100644 --- a/packages/opencode/src/session/system.ts +++ b/packages/opencode/src/session/system.ts @@ -24,7 +24,8 @@ export namespace SystemPrompt { export function provider(modelID: string) { if (modelID.includes("gpt-5")) return [PROMPT_CODEX] - if (modelID.includes("gpt-") || modelID.includes("o1") || modelID.includes("o3")) return [PROMPT_BEAST] + if (modelID.includes("gpt-") || modelID.includes("o1") || modelID.includes("o3")) + return [PROMPT_BEAST] if (modelID.includes("gemini-")) return [PROMPT_GEMINI] if (modelID.includes("claude")) return [PROMPT_ANTHROPIC] return [PROMPT_ANTHROPIC_WITHOUT_TODO] @@ -99,7 +100,11 @@ export namespace SystemPrompt { }), ).catch(() => []) } else { - matches = await Filesystem.globUp(instruction, Instance.directory, Instance.worktree).catch(() => []) + matches = await Filesystem.globUp( + instruction, + Instance.directory, + Instance.worktree, + ).catch(() => []) } matches.forEach((path) => paths.add(path)) } diff --git a/packages/opencode/src/session/todo.ts b/packages/opencode/src/session/todo.ts index d52087739..4d9a2650d 100644 --- a/packages/opencode/src/session/todo.ts +++ b/packages/opencode/src/session/todo.ts @@ -6,7 +6,9 @@ export namespace Todo { export const Info = z .object({ content: z.string().describe("Brief description of the task"), - status: z.string().describe("Current status of the task: pending, in_progress, completed, cancelled"), + status: z + .string() + .describe("Current status of the task: pending, in_progress, completed, cancelled"), priority: z.string().describe("Priority level of the task: high, medium, low"), id: z.string().describe("Unique identifier for the todo item"), }) diff --git a/packages/opencode/src/share/share.ts b/packages/opencode/src/share/share.ts index 1006b23d5..d48d76f89 100644 --- a/packages/opencode/src/share/share.ts +++ b/packages/opencode/src/share/share.ts @@ -50,7 +50,10 @@ export namespace Share { await sync("session/info/" + evt.properties.info.id, evt.properties.info) }) Bus.subscribe(MessageV2.Event.Updated, async (evt) => { - await sync("session/message/" + evt.properties.info.sessionID + "/" + evt.properties.info.id, evt.properties.info) + await sync( + "session/message/" + evt.properties.info.sessionID + "/" + evt.properties.info.id, + evt.properties.info, + ) }) Bus.subscribe(MessageV2.Event.PartUpdated, async (evt) => { await sync( @@ -67,7 +70,9 @@ export namespace Share { export const URL = process.env["OPENCODE_API"] ?? - (Installation.isPreview() || Installation.isLocal() ? "https://api.dev.opencode.ai" : "https://api.opencode.ai") + (Installation.isPreview() || Installation.isLocal() + ? "https://api.dev.opencode.ai" + : "https://api.opencode.ai") export async function create(sessionID: string) { return fetch(`${URL}/share_create`, { diff --git a/packages/opencode/src/snapshot/index.ts b/packages/opencode/src/snapshot/index.ts index 98b316804..b4deee97c 100644 --- a/packages/opencode/src/snapshot/index.ts +++ b/packages/opencode/src/snapshot/index.ts @@ -27,7 +27,11 @@ export namespace Snapshot { log.info("initialized") } await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow() - const hash = await $`git --git-dir ${git} write-tree`.quiet().cwd(Instance.directory).nothrow().text() + const hash = await $`git --git-dir ${git} write-tree` + .quiet() + .cwd(Instance.directory) + .nothrow() + .text() log.info("tracking", { hash, cwd: Instance.directory, git }) return hash.trim() } @@ -41,7 +45,10 @@ export namespace Snapshot { export async function patch(hash: string): Promise { const git = gitdir() await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow() - const result = await $`git --git-dir ${git} diff --name-only ${hash} -- .`.quiet().cwd(Instance.directory).nothrow() + const result = await $`git --git-dir ${git} diff --name-only ${hash} -- .` + .quiet() + .cwd(Instance.directory) + .nothrow() // If git diff fails, return empty patch if (result.exitCode !== 0) { @@ -64,10 +71,11 @@ export namespace Snapshot { export async function restore(snapshot: string) { log.info("restore", { commit: snapshot }) const git = gitdir() - const result = await $`git --git-dir=${git} read-tree ${snapshot} && git --git-dir=${git} checkout-index -a -f` - .quiet() - .cwd(Instance.worktree) - .nothrow() + const result = + await $`git --git-dir=${git} read-tree ${snapshot} && git --git-dir=${git} checkout-index -a -f` + .quiet() + .cwd(Instance.worktree) + .nothrow() if (result.exitCode !== 0) { log.error("failed to restore snapshot", { @@ -113,7 +121,10 @@ export namespace Snapshot { export async function diff(hash: string) { const git = gitdir() await $`git --git-dir ${git} add .`.quiet().cwd(Instance.directory).nothrow() - const result = await $`git --git-dir=${git} diff ${hash} -- .`.quiet().cwd(Instance.worktree).nothrow() + const result = await $`git --git-dir=${git} diff ${hash} -- .` + .quiet() + .cwd(Instance.worktree) + .nothrow() if (result.exitCode !== 0) { log.warn("failed to get diff", { diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts index ba3d2c0bf..057fb9e8f 100644 --- a/packages/opencode/src/tool/edit.ts +++ b/packages/opencode/src/tool/edit.ts @@ -23,8 +23,13 @@ export const EditTool = Tool.define("edit", { parameters: z.object({ filePath: z.string().describe("The absolute path to the file to modify"), oldString: z.string().describe("The text to replace"), - newString: z.string().describe("The text to replace it with (must be different from oldString)"), - replaceAll: z.boolean().optional().describe("Replace all occurrences of oldString (default false)"), + newString: z + .string() + .describe("The text to replace it with (must be different from oldString)"), + replaceAll: z + .boolean() + .optional() + .describe("Replace all occurrences of oldString (default false)"), }), async execute(params, ctx) { if (!params.filePath) { @@ -35,7 +40,9 @@ export const EditTool = Tool.define("edit", { throw new Error("oldString and newString must be different") } - const filePath = path.isAbsolute(params.filePath) ? params.filePath : path.join(Instance.directory, params.filePath) + const filePath = path.isAbsolute(params.filePath) + ? params.filePath + : path.join(Instance.directory, params.filePath) if (!Filesystem.contains(Instance.directory, filePath)) { const parentDir = path.dirname(filePath) await Permission.ask({ @@ -172,7 +179,11 @@ function levenshtein(a: string, b: string): number { for (let i = 1; i <= a.length; i++) { for (let j = 1; j <= b.length; j++) { const cost = a[i - 1] === b[j - 1] ? 0 : 1 - matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost) + matrix[i][j] = Math.min( + matrix[i - 1][j] + 1, + matrix[i][j - 1] + 1, + matrix[i - 1][j - 1] + cost, + ) } } return matrix[a.length][b.length] @@ -374,7 +385,9 @@ export const WhitespaceNormalizedReplacer: Replacer = function* (content, find) // Find the actual substring in the original line that matches const words = find.trim().split(/\s+/) if (words.length > 0) { - const pattern = words.map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("\\s+") + const pattern = words + .map((word) => word.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")) + .join("\\s+") try { const regex = new RegExp(pattern) const match = line.match(regex) @@ -612,7 +625,12 @@ export function trimDiff(diff: string): string { return trimmedLines.join("\n") } -export function replace(content: string, oldString: string, newString: string, replaceAll = false): string { +export function replace( + content: string, + oldString: string, + newString: string, + replaceAll = false, +): string { if (oldString === newString) { throw new Error("oldString and newString must be different") } diff --git a/packages/opencode/src/tool/grep.ts b/packages/opencode/src/tool/grep.ts index 5390be21a..9f7d04ea1 100644 --- a/packages/opencode/src/tool/grep.ts +++ b/packages/opencode/src/tool/grep.ts @@ -9,8 +9,14 @@ export const GrepTool = Tool.define("grep", { description: DESCRIPTION, parameters: z.object({ pattern: z.string().describe("The regex pattern to search for in file contents"), - path: z.string().optional().describe("The directory to search in. Defaults to the current working directory."), - include: z.string().optional().describe('File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")'), + path: z + .string() + .optional() + .describe("The directory to search in. Defaults to the current working directory."), + include: z + .string() + .optional() + .describe('File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")'), }), async execute(params) { if (!params.pattern) { diff --git a/packages/opencode/src/tool/ls.ts b/packages/opencode/src/tool/ls.ts index 95c36e745..4fcfcaa8c 100644 --- a/packages/opencode/src/tool/ls.ts +++ b/packages/opencode/src/tool/ls.ts @@ -37,13 +37,18 @@ const LIMIT = 100 export const ListTool = Tool.define("list", { description: DESCRIPTION, parameters: z.object({ - path: z.string().describe("The absolute path to the directory to list (must be absolute, not relative)").optional(), + path: z + .string() + .describe("The absolute path to the directory to list (must be absolute, not relative)") + .optional(), ignore: z.array(z.string()).describe("List of glob patterns to ignore").optional(), }), async execute(params) { const searchPath = path.resolve(Instance.directory, params.path || ".") - const ignoreGlobs = IGNORE_PATTERNS.map((p) => `!${p}*`).concat(params.ignore?.map((p) => `!${p}`) || []) + const ignoreGlobs = IGNORE_PATTERNS.map((p) => `!${p}*`).concat( + params.ignore?.map((p) => `!${p}`) || [], + ) const files = [] for await (const file of Ripgrep.files({ cwd: searchPath, glob: ignoreGlobs })) { files.push(file) diff --git a/packages/opencode/src/tool/lsp-diagnostics.ts b/packages/opencode/src/tool/lsp-diagnostics.ts index 18a6868b6..78c8c3cae 100644 --- a/packages/opencode/src/tool/lsp-diagnostics.ts +++ b/packages/opencode/src/tool/lsp-diagnostics.ts @@ -11,7 +11,9 @@ export const LspDiagnosticTool = Tool.define("lsp_diagnostics", { path: z.string().describe("The path to the file to get diagnostics."), }), execute: async (args) => { - const normalized = path.isAbsolute(args.path) ? args.path : path.join(Instance.directory, args.path) + const normalized = path.isAbsolute(args.path) + ? args.path + : path.join(Instance.directory, args.path) await LSP.touchFile(normalized, true) const diagnostics = await LSP.diagnostics() const file = diagnostics[normalized] diff --git a/packages/opencode/src/tool/multiedit.ts b/packages/opencode/src/tool/multiedit.ts index 7f562f473..f3a657354 100644 --- a/packages/opencode/src/tool/multiedit.ts +++ b/packages/opencode/src/tool/multiedit.ts @@ -14,8 +14,13 @@ export const MultiEditTool = Tool.define("multiedit", { z.object({ filePath: z.string().describe("The absolute path to the file to modify"), oldString: z.string().describe("The text to replace"), - newString: z.string().describe("The text to replace it with (must be different from oldString)"), - replaceAll: z.boolean().optional().describe("Replace all occurrences of oldString (default false)"), + newString: z + .string() + .describe("The text to replace it with (must be different from oldString)"), + replaceAll: z + .boolean() + .optional() + .describe("Replace all occurrences of oldString (default false)"), }), ) .describe("Array of edit operations to perform sequentially on the file"), diff --git a/packages/opencode/src/tool/read.ts b/packages/opencode/src/tool/read.ts index 963636fd1..56a67bb03 100644 --- a/packages/opencode/src/tool/read.ts +++ b/packages/opencode/src/tool/read.ts @@ -18,7 +18,10 @@ export const ReadTool = Tool.define("read", { description: DESCRIPTION, parameters: z.object({ filePath: z.string().describe("The path to the file to read"), - offset: z.coerce.number().describe("The line number to start reading from (0-based)").optional(), + offset: z.coerce + .number() + .describe("The line number to start reading from (0-based)") + .optional(), limit: z.coerce.number().describe("The number of lines to read (defaults to 2000)").optional(), }), async execute(params, ctx) { @@ -53,13 +56,16 @@ export const ReadTool = Tool.define("read", { const suggestions = dirEntries .filter( (entry) => - entry.toLowerCase().includes(base.toLowerCase()) || base.toLowerCase().includes(entry.toLowerCase()), + entry.toLowerCase().includes(base.toLowerCase()) || + base.toLowerCase().includes(entry.toLowerCase()), ) .map((entry) => path.join(dir, entry)) .slice(0, 3) if (suggestions.length > 0) { - throw new Error(`File not found: ${filepath}\n\nDid you mean one of these?\n${suggestions.join("\n")}`) + throw new Error( + `File not found: ${filepath}\n\nDid you mean one of these?\n${suggestions.join("\n")}`, + ) } throw new Error(`File not found: ${filepath}`) diff --git a/packages/opencode/src/tool/registry.ts b/packages/opencode/src/tool/registry.ts index c4d54597d..6234a4e61 100644 --- a/packages/opencode/src/tool/registry.ts +++ b/packages/opencode/src/tool/registry.ts @@ -24,7 +24,12 @@ export namespace ToolRegistry { const glob = new Bun.Glob("tool/*.{js,ts}") for (const dir of await Config.directories()) { - for await (const match of glob.scan({ cwd: dir, absolute: true, followSymlinks: true, dot: true })) { + for await (const match of glob.scan({ + cwd: dir, + absolute: true, + followSymlinks: true, + dot: true, + })) { const namespace = path.basename(match, path.extname(match)) const mod = await import(match) for (const [id, def] of Object.entries(mod)) { diff --git a/packages/opencode/src/tool/task.ts b/packages/opencode/src/tool/task.ts index 342645c30..ac0b204ba 100644 --- a/packages/opencode/src/tool/task.ts +++ b/packages/opencode/src/tool/task.ts @@ -14,7 +14,10 @@ export const TaskTool = Tool.define("task", async () => { const description = DESCRIPTION.replace( "{agents}", agents - .map((a) => `- ${a.name}: ${a.description ?? "This subagent should only be called manually by the user."}`) + .map( + (a) => + `- ${a.name}: ${a.description ?? "This subagent should only be called manually by the user."}`, + ) .join("\n"), ) return { @@ -26,7 +29,8 @@ export const TaskTool = Tool.define("task", async () => { }), async execute(params, ctx) { const agent = await Agent.get(params.subagent_type) - if (!agent) throw new Error(`Unknown agent type: ${params.subagent_type} is not a valid agent type`) + if (!agent) + throw new Error(`Unknown agent type: ${params.subagent_type} is not a valid agent type`) const session = await Session.create({ parentID: ctx.sessionID, title: params.description + ` (@${agent.name} subagent)`, @@ -91,7 +95,9 @@ export const TaskTool = Tool.define("task", async () => { let all all = await Session.messages(session.id) all = all.filter((x) => x.info.role === "assistant") - all = all.flatMap((msg) => msg.parts.filter((x: any) => x.type === "tool") as MessageV2.ToolPart[]) + all = all.flatMap( + (msg) => msg.parts.filter((x: any) => x.type === "tool") as MessageV2.ToolPart[], + ) return { title: params.description, metadata: { diff --git a/packages/opencode/src/tool/webfetch.ts b/packages/opencode/src/tool/webfetch.ts index 0333bb018..d85351ffe 100644 --- a/packages/opencode/src/tool/webfetch.ts +++ b/packages/opencode/src/tool/webfetch.ts @@ -48,13 +48,15 @@ export const WebFetchTool = Tool.define("webfetch", { let acceptHeader = "*/*" switch (params.format) { case "markdown": - acceptHeader = "text/markdown;q=1.0, text/x-markdown;q=0.9, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1" + acceptHeader = + "text/markdown;q=1.0, text/x-markdown;q=0.9, text/plain;q=0.8, text/html;q=0.7, */*;q=0.1" break case "text": acceptHeader = "text/plain;q=1.0, text/markdown;q=0.9, text/html;q=0.8, */*;q=0.1" break case "html": - acceptHeader = "text/html;q=1.0, application/xhtml+xml;q=0.9, text/plain;q=0.8, text/markdown;q=0.7, */*;q=0.1" + acceptHeader = + "text/html;q=1.0, application/xhtml+xml;q=0.9, text/plain;q=0.8, text/markdown;q=0.7, */*;q=0.1" break default: acceptHeader = @@ -158,7 +160,9 @@ async function extractTextFromHTML(html: string) { .on("*", { element(element) { // Reset skip flag when entering other elements - if (!["script", "style", "noscript", "iframe", "object", "embed"].includes(element.tagName)) { + if ( + !["script", "style", "noscript", "iframe", "object", "embed"].includes(element.tagName) + ) { skipContent = false } }, diff --git a/packages/opencode/src/tool/write.ts b/packages/opencode/src/tool/write.ts index acaa12392..c7b998160 100644 --- a/packages/opencode/src/tool/write.ts +++ b/packages/opencode/src/tool/write.ts @@ -15,10 +15,14 @@ export const WriteTool = Tool.define("write", { description: DESCRIPTION, parameters: z.object({ content: z.string().describe("The content to write to the file"), - filePath: z.string().describe("The absolute path to the file to write (must be absolute, not relative)"), + filePath: z + .string() + .describe("The absolute path to the file to write (must be absolute, not relative)"), }), async execute(params, ctx) { - const filepath = path.isAbsolute(params.filePath) ? params.filePath : path.join(Instance.directory, params.filePath) + const filepath = path.isAbsolute(params.filePath) + ? params.filePath + : path.join(Instance.directory, params.filePath) if (!Filesystem.contains(Instance.directory, filepath)) { const parentDir = path.dirname(filepath) await Permission.ask({ diff --git a/packages/opencode/src/util/binary.ts b/packages/opencode/src/util/binary.ts index 3d8f61851..d72cc85d4 100644 --- a/packages/opencode/src/util/binary.ts +++ b/packages/opencode/src/util/binary.ts @@ -1,5 +1,9 @@ export namespace Binary { - export function search(array: T[], id: string, compare: (item: T) => string): { found: boolean; index: number } { + export function search( + array: T[], + id: string, + compare: (item: T) => string, + ): { found: boolean; index: number } { let left = 0 let right = array.length - 1 diff --git a/packages/opencode/src/util/defer.ts b/packages/opencode/src/util/defer.ts index 8de21528c..69b5c1788 100644 --- a/packages/opencode/src/util/defer.ts +++ b/packages/opencode/src/util/defer.ts @@ -1,6 +1,8 @@ export function defer void | Promise>( fn: T, -): T extends () => Promise ? { [Symbol.asyncDispose]: () => Promise } : { [Symbol.dispose]: () => void } { +): T extends () => Promise + ? { [Symbol.asyncDispose]: () => Promise } + : { [Symbol.dispose]: () => void } { return { [Symbol.dispose]() { fn() diff --git a/packages/opencode/src/util/eventloop.ts b/packages/opencode/src/util/eventloop.ts index 87f6eef41..f7096d383 100644 --- a/packages/opencode/src/util/eventloop.ts +++ b/packages/opencode/src/util/eventloop.ts @@ -4,11 +4,17 @@ export namespace EventLoop { export async function wait() { return new Promise((resolve) => { const check = () => { - const active = [...(process as any)._getActiveHandles(), ...(process as any)._getActiveRequests()] + const active = [ + ...(process as any)._getActiveHandles(), + ...(process as any)._getActiveRequests(), + ] Log.Default.info("eventloop", { active, }) - if ((process as any)._getActiveHandles().length === 0 && (process as any)._getActiveRequests().length === 0) { + if ( + (process as any)._getActiveHandles().length === 0 && + (process as any)._getActiveRequests().length === 0 + ) { resolve() } else { setImmediate(check) diff --git a/packages/opencode/src/util/lock.ts b/packages/opencode/src/util/lock.ts index 3aea64394..c503ddd33 100644 --- a/packages/opencode/src/util/lock.ts +++ b/packages/opencode/src/util/lock.ts @@ -39,7 +39,12 @@ export namespace Lock { } // Clean up empty locks - if (lock.readers === 0 && !lock.writer && lock.waitingReaders.length === 0 && lock.waitingWriters.length === 0) { + if ( + lock.readers === 0 && + !lock.writer && + lock.waitingReaders.length === 0 && + lock.waitingWriters.length === 0 + ) { locks.delete(key) } } diff --git a/packages/opencode/src/util/rpc.ts b/packages/opencode/src/util/rpc.ts index 57c695c48..981cc0714 100644 --- a/packages/opencode/src/util/rpc.ts +++ b/packages/opencode/src/util/rpc.ts @@ -30,7 +30,10 @@ export namespace Rpc { } } return { - call(method: Method, input: Parameters[0]): Promise> { + call( + method: Method, + input: Parameters[0], + ): Promise> { const requestId = id++ return new Promise((resolve) => { pending.set(requestId, resolve) diff --git a/packages/opencode/src/util/wildcard.ts b/packages/opencode/src/util/wildcard.ts index 9b595a0a9..feda96961 100644 --- a/packages/opencode/src/util/wildcard.ts +++ b/packages/opencode/src/util/wildcard.ts @@ -15,7 +15,11 @@ export namespace Wildcard { } export function all(input: string, patterns: Record) { - const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"])) + const sorted = pipe( + patterns, + Object.entries, + sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"]), + ) let result = undefined for (const [pattern, value] of sorted) { if (match(input, pattern)) { @@ -26,8 +30,15 @@ export namespace Wildcard { return result } - export function allStructured(input: { head: string; tail: string[] }, patterns: Record) { - const sorted = pipe(patterns, Object.entries, sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"])) + export function allStructured( + input: { head: string; tail: string[] }, + patterns: Record, + ) { + const sorted = pipe( + patterns, + Object.entries, + sortBy([([key]) => key.length, "asc"], [([key]) => key, "asc"]), + ) let result = undefined for (const [pattern, value] of sorted) { const parts = pattern.split(/\s+/) diff --git a/packages/opencode/sst-env.d.ts b/packages/opencode/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/opencode/sst-env.d.ts +++ b/packages/opencode/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/opencode/test/patch/patch.test.ts b/packages/opencode/test/patch/patch.test.ts index 51076c34f..020253bfe 100644 --- a/packages/opencode/test/patch/patch.test.ts +++ b/packages/opencode/test/patch/patch.test.ts @@ -6,23 +6,23 @@ import { tmpdir } from "os" describe("Patch namespace", () => { let tempDir: string - + beforeEach(async () => { tempDir = await fs.mkdtemp(path.join(tmpdir(), "patch-test-")) }) - + afterEach(async () => { // Clean up temp directory await fs.rm(tempDir, { recursive: true, force: true }) }) - + describe("parsePatch", () => { test("should parse simple add file patch", () => { const patchText = `*** Begin Patch *** Add File: test.txt +Hello World *** End Patch` - + const result = Patch.parsePatch(patchText) expect(result.hunks).toHaveLength(1) expect(result.hunks[0]).toEqual({ @@ -31,19 +31,19 @@ describe("Patch namespace", () => { contents: "Hello World", }) }) - + test("should parse delete file patch", () => { const patchText = `*** Begin Patch *** Delete File: old.txt *** End Patch` - + const result = Patch.parsePatch(patchText) expect(result.hunks).toHaveLength(1) const hunk = result.hunks[0] expect(hunk.type).toBe("delete") expect(hunk.path).toBe("old.txt") }) - + test("should parse patch with multiple hunks", () => { const patchText = `*** Begin Patch *** Add File: new.txt @@ -54,13 +54,13 @@ describe("Patch namespace", () => { -new line +updated line *** End Patch` - + const result = Patch.parsePatch(patchText) expect(result.hunks).toHaveLength(2) expect(result.hunks[0].type).toBe("add") expect(result.hunks[1].type).toBe("update") }) - + test("should parse file move operation", () => { const patchText = `*** Begin Patch *** Update File: old-name.txt @@ -69,7 +69,7 @@ describe("Patch namespace", () => { -Old content +New content *** End Patch` - + const result = Patch.parsePatch(patchText) expect(result.hunks).toHaveLength(1) const hunk = result.hunks[0] @@ -79,21 +79,21 @@ describe("Patch namespace", () => { expect(hunk.move_path).toBe("new-name.txt") } }) - + test("should throw error for invalid patch format", () => { const invalidPatch = `This is not a valid patch` - + expect(() => Patch.parsePatch(invalidPatch)).toThrow("Invalid patch format") }) }) - + describe("maybeParseApplyPatch", () => { test("should parse direct apply_patch command", () => { const patchText = `*** Begin Patch *** Add File: test.txt +Content *** End Patch` - + const result = Patch.maybeParseApplyPatch(["apply_patch", patchText]) expect(result.type).toBe(Patch.MaybeApplyPatch.Body) if (result.type === Patch.MaybeApplyPatch.Body) { @@ -101,17 +101,17 @@ describe("Patch namespace", () => { expect(result.args.hunks).toHaveLength(1) } }) - + test("should parse applypatch command", () => { const patchText = `*** Begin Patch *** Add File: test.txt +Content *** End Patch` - + const result = Patch.maybeParseApplyPatch(["applypatch", patchText]) expect(result.type).toBe(Patch.MaybeApplyPatch.Body) }) - + test("should handle bash heredoc format", () => { const script = `apply_patch <<'PATCH' *** Begin Patch @@ -119,20 +119,20 @@ describe("Patch namespace", () => { +Content *** End Patch PATCH` - + const result = Patch.maybeParseApplyPatch(["bash", "-lc", script]) expect(result.type).toBe(Patch.MaybeApplyPatch.Body) if (result.type === Patch.MaybeApplyPatch.Body) { expect(result.args.hunks).toHaveLength(1) } }) - + test("should return NotApplyPatch for non-patch commands", () => { const result = Patch.maybeParseApplyPatch(["echo", "hello"]) expect(result.type).toBe(Patch.MaybeApplyPatch.NotApplyPatch) }) }) - + describe("applyPatch", () => { test("should add a new file", async () => { const patchText = `*** Begin Patch @@ -140,36 +140,39 @@ PATCH` +Hello World +This is a new file *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.added).toHaveLength(1) expect(result.modified).toHaveLength(0) expect(result.deleted).toHaveLength(0) - + const content = await fs.readFile(result.added[0], "utf-8") expect(content).toBe("Hello World\nThis is a new file") }) - + test("should delete an existing file", async () => { const filePath = path.join(tempDir, "to-delete.txt") await fs.writeFile(filePath, "This file will be deleted") - + const patchText = `*** Begin Patch *** Delete File: ${filePath} *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.deleted).toHaveLength(1) expect(result.deleted[0]).toBe(filePath) - - const exists = await fs.access(filePath).then(() => true).catch(() => false) + + const exists = await fs + .access(filePath) + .then(() => true) + .catch(() => false) expect(exists).toBe(false) }) - + test("should update an existing file", async () => { const filePath = path.join(tempDir, "to-update.txt") await fs.writeFile(filePath, "line 1\nline 2\nline 3\n") - + const patchText = `*** Begin Patch *** Update File: ${filePath} @@ @@ -178,20 +181,20 @@ PATCH` +line 2 updated line 3 *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.modified).toHaveLength(1) expect(result.modified[0]).toBe(filePath) - + const content = await fs.readFile(filePath, "utf-8") expect(content).toBe("line 1\nline 2 updated\nline 3\n") }) - + test("should move and update a file", async () => { const oldPath = path.join(tempDir, "old-name.txt") const newPath = path.join(tempDir, "new-name.txt") await fs.writeFile(oldPath, "old content\n") - + const patchText = `*** Begin Patch *** Update File: ${oldPath} *** Move to: ${newPath} @@ -199,26 +202,29 @@ PATCH` -old content +new content *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.modified).toHaveLength(1) expect(result.modified[0]).toBe(newPath) - - const oldExists = await fs.access(oldPath).then(() => true).catch(() => false) + + const oldExists = await fs + .access(oldPath) + .then(() => true) + .catch(() => false) expect(oldExists).toBe(false) - + const newContent = await fs.readFile(newPath, "utf-8") expect(newContent).toBe("new content\n") }) - + test("should handle multiple operations in one patch", async () => { const file1 = path.join(tempDir, "file1.txt") const file2 = path.join(tempDir, "file2.txt") const file3 = path.join(tempDir, "file3.txt") - + await fs.writeFile(file1, "content 1") await fs.writeFile(file2, "content 2") - + const patchText = `*** Begin Patch *** Add File: ${file3} +new file content @@ -228,95 +234,98 @@ PATCH` +updated content 1 *** Delete File: ${file2} *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.added).toHaveLength(1) expect(result.modified).toHaveLength(1) expect(result.deleted).toHaveLength(1) }) - + test("should create parent directories when adding files", async () => { const nestedPath = path.join(tempDir, "deep", "nested", "file.txt") - + const patchText = `*** Begin Patch *** Add File: ${nestedPath} +Deep nested content *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.added).toHaveLength(1) expect(result.added[0]).toBe(nestedPath) - - const exists = await fs.access(nestedPath).then(() => true).catch(() => false) + + const exists = await fs + .access(nestedPath) + .then(() => true) + .catch(() => false) expect(exists).toBe(true) }) }) - + describe("error handling", () => { test("should throw error when updating non-existent file", async () => { const nonExistent = path.join(tempDir, "does-not-exist.txt") - + const patchText = `*** Begin Patch *** Update File: ${nonExistent} @@ -old line +new line *** End Patch` - + await expect(Patch.applyPatch(patchText)).rejects.toThrow() }) - + test("should throw error when deleting non-existent file", async () => { const nonExistent = path.join(tempDir, "does-not-exist.txt") - + const patchText = `*** Begin Patch *** Delete File: ${nonExistent} *** End Patch` - + await expect(Patch.applyPatch(patchText)).rejects.toThrow() }) }) - + describe("edge cases", () => { test("should handle empty files", async () => { const emptyFile = path.join(tempDir, "empty.txt") await fs.writeFile(emptyFile, "") - + const patchText = `*** Begin Patch *** Update File: ${emptyFile} @@ +First line *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.modified).toHaveLength(1) - + const content = await fs.readFile(emptyFile, "utf-8") expect(content).toBe("First line\n") }) - + test("should handle files with no trailing newline", async () => { const filePath = path.join(tempDir, "no-newline.txt") await fs.writeFile(filePath, "no newline") - + const patchText = `*** Begin Patch *** Update File: ${filePath} @@ -no newline +has newline now *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.modified).toHaveLength(1) - + const content = await fs.readFile(filePath, "utf-8") expect(content).toBe("has newline now\n") }) - + test("should handle multiple update chunks in single file", async () => { const filePath = path.join(tempDir, "multi-chunk.txt") await fs.writeFile(filePath, "line 1\nline 2\nline 3\nline 4\n") - + const patchText = `*** Begin Patch *** Update File: ${filePath} @@ @@ -328,12 +337,12 @@ PATCH` -line 4 +LINE 4 *** End Patch` - + const result = await Patch.applyPatch(patchText) expect(result.modified).toHaveLength(1) - + const content = await fs.readFile(filePath, "utf-8") expect(content).toBe("line 1\nLINE 2\nline 3\nLINE 4\n") }) }) -}) \ No newline at end of file +}) diff --git a/packages/opencode/test/session/retry.test.ts b/packages/opencode/test/session/retry.test.ts index edce412c2..ebcee80df 100644 --- a/packages/opencode/test/session/retry.test.ts +++ b/packages/opencode/test/session/retry.test.ts @@ -13,7 +13,9 @@ function apiError(headers?: Record): MessageV2.APIError { describe("session.retry.getRetryDelayInMs", () => { test("doubles delay on each attempt when headers missing", () => { const error = apiError() - const delays = Array.from({ length: 7 }, (_, index) => SessionRetry.getRetryDelayInMs(error, index + 1)) + const delays = Array.from({ length: 7 }, (_, index) => + SessionRetry.getRetryDelayInMs(error, index + 1), + ) expect(delays).toStrictEqual([2000, 4000, 8000, 16000, 32000, 64000, 128000]) }) diff --git a/packages/opencode/test/util/wildcard.test.ts b/packages/opencode/test/util/wildcard.test.ts index f7f1e1545..968b4f288 100644 --- a/packages/opencode/test/util/wildcard.test.ts +++ b/packages/opencode/test/util/wildcard.test.ts @@ -24,9 +24,12 @@ test("allStructured matches command sequences", () => { "git status*": "allow", } expect(Wildcard.allStructured({ head: "git", tail: ["status", "--short"] }, rules)).toBe("allow") - expect(Wildcard.allStructured({ head: "npm", tail: ["run", "build", "--watch"] }, { "npm run *": "allow" })).toBe( - "allow", - ) + expect( + Wildcard.allStructured( + { head: "npm", tail: ["run", "build", "--watch"] }, + { "npm run *": "allow" }, + ), + ).toBe("allow") expect(Wildcard.allStructured({ head: "ls", tail: ["-la"] }, rules)).toBeUndefined() }) @@ -51,5 +54,7 @@ test("allStructured handles sed flags", () => { expect(Wildcard.allStructured({ head: "sed", tail: ["-i", "file"] }, rules)).toBe("ask") expect(Wildcard.allStructured({ head: "sed", tail: ["-i.bak", "file"] }, rules)).toBe("ask") expect(Wildcard.allStructured({ head: "sed", tail: ["-n", "1p", "file"] }, rules)).toBe("allow") - expect(Wildcard.allStructured({ head: "sed", tail: ["-i", "-n", "/./p", "myfile.txt"] }, rules)).toBe("ask") + expect( + Wildcard.allStructured({ head: "sed", tail: ["-i", "-n", "/./p", "myfile.txt"] }, rules), + ).toBe("ask") }) diff --git a/packages/plugin/package.json b/packages/plugin/package.json index c510b519b..9206efe1e 100644 --- a/packages/plugin/package.json +++ b/packages/plugin/package.json @@ -24,4 +24,4 @@ "typescript": "catalog:", "@typescript/native-preview": "catalog:" } -} \ No newline at end of file +} diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index f103749bd..1c8c6d2a1 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -151,7 +151,10 @@ export interface Hooks { input: { model: Model; provider: Provider; message: UserMessage }, output: { temperature: number; topP: number; options: Record }, ) => Promise - "permission.ask"?: (input: Permission, output: { status: "ask" | "deny" | "allow" }) => Promise + "permission.ask"?: ( + input: Permission, + output: { status: "ask" | "deny" | "allow" }, + ) => Promise "tool.execute.before"?: ( input: { tool: string; sessionID: string; callID: string }, output: { args: any }, diff --git a/packages/plugin/sst-env.d.ts b/packages/plugin/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/plugin/sst-env.d.ts +++ b/packages/plugin/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/script/sst-env.d.ts b/packages/script/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/script/sst-env.d.ts +++ b/packages/script/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/sdk/go/.github/workflows/ci.yml b/packages/sdk/go/.github/workflows/ci.yml index 4bf1e907c..0f5d45dc2 100644 --- a/packages/sdk/go/.github/workflows/ci.yml +++ b/packages/sdk/go/.github/workflows/ci.yml @@ -2,15 +2,15 @@ name: CI on: push: branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + - "generated" + - "codegen/**" + - "integrated/**" + - "stl-preview-head/**" + - "stl-preview-base/**" pull_request: branches-ignore: - - 'stl-preview-head/**' - - 'stl-preview-base/**' + - "stl-preview-head/**" + - "stl-preview-base/**" jobs: lint: diff --git a/packages/sdk/go/.release-please-manifest.json b/packages/sdk/go/.release-please-manifest.json index 4ad3fef33..5e39b9417 100644 --- a/packages/sdk/go/.release-please-manifest.json +++ b/packages/sdk/go/.release-please-manifest.json @@ -1,3 +1,3 @@ { ".": "0.18.0" -} \ No newline at end of file +} diff --git a/packages/sdk/go/CHANGELOG.md b/packages/sdk/go/CHANGELOG.md index 498a78029..937fbfdd0 100644 --- a/packages/sdk/go/CHANGELOG.md +++ b/packages/sdk/go/CHANGELOG.md @@ -6,7 +6,7 @@ Full Changelog: [v0.17.0...v0.18.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([0a7f5e7](https://github.com/sst/opencode-sdk-go/commit/0a7f5e710911506512a132ba39e0593c412beb77)) +- **api:** api update ([0a7f5e7](https://github.com/sst/opencode-sdk-go/commit/0a7f5e710911506512a132ba39e0593c412beb77)) ## 0.17.0 (2025-10-07) @@ -14,7 +14,7 @@ Full Changelog: [v0.16.2...v0.17.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([84a3df5](https://github.com/sst/opencode-sdk-go/commit/84a3df50a7ff3d87e5593e4f29dfb5d561f71cc3)) +- **api:** api update ([84a3df5](https://github.com/sst/opencode-sdk-go/commit/84a3df50a7ff3d87e5593e4f29dfb5d561f71cc3)) ## 0.16.2 (2025-09-26) @@ -22,7 +22,7 @@ Full Changelog: [v0.16.1...v0.16.2](https://github.com/sst/opencode-sdk-go/compa ### Bug Fixes -* bugfix for setting JSON keys with special characters ([ac9a36f](https://github.com/sst/opencode-sdk-go/commit/ac9a36feb1c185ebf766d76909d0b86ac805e8a6)) +- bugfix for setting JSON keys with special characters ([ac9a36f](https://github.com/sst/opencode-sdk-go/commit/ac9a36feb1c185ebf766d76909d0b86ac805e8a6)) ## 0.16.1 (2025-09-20) @@ -30,14 +30,13 @@ Full Changelog: [v0.16.0...v0.16.1](https://github.com/sst/opencode-sdk-go/compa ### Bug Fixes -* use slices.Concat instead of sometimes modifying r.Options ([12e8b40](https://github.com/sst/opencode-sdk-go/commit/12e8b40809071095b0abb9b8031686353c8ac149)) - +- use slices.Concat instead of sometimes modifying r.Options ([12e8b40](https://github.com/sst/opencode-sdk-go/commit/12e8b40809071095b0abb9b8031686353c8ac149)) ### Chores -* bump minimum go version to 1.22 ([1a61c5c](https://github.com/sst/opencode-sdk-go/commit/1a61c5cc7e8f68cc1b0c219738cab530cb6aa3a2)) -* do not install brew dependencies in ./scripts/bootstrap by default ([f6d3eaf](https://github.com/sst/opencode-sdk-go/commit/f6d3eafffc20e124bbfae6ac5ddc1b1122ad3e27)) -* update more docs for 1.22 ([a3d0b0f](https://github.com/sst/opencode-sdk-go/commit/a3d0b0f26ed92ce1a6433f5bcf37a6436d268ba5)) +- bump minimum go version to 1.22 ([1a61c5c](https://github.com/sst/opencode-sdk-go/commit/1a61c5cc7e8f68cc1b0c219738cab530cb6aa3a2)) +- do not install brew dependencies in ./scripts/bootstrap by default ([f6d3eaf](https://github.com/sst/opencode-sdk-go/commit/f6d3eafffc20e124bbfae6ac5ddc1b1122ad3e27)) +- update more docs for 1.22 ([a3d0b0f](https://github.com/sst/opencode-sdk-go/commit/a3d0b0f26ed92ce1a6433f5bcf37a6436d268ba5)) ## 0.16.0 (2025-09-17) @@ -45,7 +44,7 @@ Full Changelog: [v0.15.0...v0.16.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([46e978e](https://github.com/sst/opencode-sdk-go/commit/46e978e43aee733d5c1c09dc5be6d8ac2a752427)) +- **api:** api update ([46e978e](https://github.com/sst/opencode-sdk-go/commit/46e978e43aee733d5c1c09dc5be6d8ac2a752427)) ## 0.15.0 (2025-09-16) @@ -53,7 +52,7 @@ Full Changelog: [v0.14.0...v0.15.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([397048f](https://github.com/sst/opencode-sdk-go/commit/397048faca7a1de7a028edd2254a0ad7797b151f)) +- **api:** api update ([397048f](https://github.com/sst/opencode-sdk-go/commit/397048faca7a1de7a028edd2254a0ad7797b151f)) ## 0.14.0 (2025-09-14) @@ -61,7 +60,7 @@ Full Changelog: [v0.13.0...v0.14.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([dad0bc3](https://github.com/sst/opencode-sdk-go/commit/dad0bc3da99f20a0d002a6b94e049fb70f8e6a77)) +- **api:** api update ([dad0bc3](https://github.com/sst/opencode-sdk-go/commit/dad0bc3da99f20a0d002a6b94e049fb70f8e6a77)) ## 0.13.0 (2025-09-14) @@ -69,7 +68,7 @@ Full Changelog: [v0.12.0...v0.13.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([80da4fb](https://github.com/sst/opencode-sdk-go/commit/80da4fb4ea9c6afb51a7e7135d9f5560ce6f2a6c)) +- **api:** api update ([80da4fb](https://github.com/sst/opencode-sdk-go/commit/80da4fb4ea9c6afb51a7e7135d9f5560ce6f2a6c)) ## 0.12.0 (2025-09-14) @@ -77,7 +76,7 @@ Full Changelog: [v0.11.0...v0.12.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([7e3808b](https://github.com/sst/opencode-sdk-go/commit/7e3808ba349dc653174b32b48a1120c18d2975c2)) +- **api:** api update ([7e3808b](https://github.com/sst/opencode-sdk-go/commit/7e3808ba349dc653174b32b48a1120c18d2975c2)) ## 0.11.0 (2025-09-14) @@ -85,7 +84,7 @@ Full Changelog: [v0.10.0...v0.11.0](https://github.com/sst/opencode-sdk-go/compa ### Features -* **api:** api update ([a3d37f5](https://github.com/sst/opencode-sdk-go/commit/a3d37f5671545866547d351fc21b49809cc8b3c2)) +- **api:** api update ([a3d37f5](https://github.com/sst/opencode-sdk-go/commit/a3d37f5671545866547d351fc21b49809cc8b3c2)) ## 0.10.0 (2025-09-11) @@ -93,7 +92,7 @@ Full Changelog: [v0.9.0...v0.10.0](https://github.com/sst/opencode-sdk-go/compar ### Features -* **api:** api update ([0dc01f6](https://github.com/sst/opencode-sdk-go/commit/0dc01f6695c9b8400a4dc92166c5002bb120cf50)) +- **api:** api update ([0dc01f6](https://github.com/sst/opencode-sdk-go/commit/0dc01f6695c9b8400a4dc92166c5002bb120cf50)) ## 0.9.0 (2025-09-10) @@ -101,7 +100,7 @@ Full Changelog: [v0.8.0...v0.9.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([2d3a28d](https://github.com/sst/opencode-sdk-go/commit/2d3a28df5657845aa4d73087e1737d1fc8c3ce1c)) +- **api:** api update ([2d3a28d](https://github.com/sst/opencode-sdk-go/commit/2d3a28df5657845aa4d73087e1737d1fc8c3ce1c)) ## 0.8.0 (2025-09-01) @@ -109,7 +108,7 @@ Full Changelog: [v0.7.0...v0.8.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([ae87a71](https://github.com/sst/opencode-sdk-go/commit/ae87a71949994590ace8285a39f0991ef34b664d)) +- **api:** api update ([ae87a71](https://github.com/sst/opencode-sdk-go/commit/ae87a71949994590ace8285a39f0991ef34b664d)) ## 0.7.0 (2025-09-01) @@ -117,7 +116,7 @@ Full Changelog: [v0.6.0...v0.7.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([64bb1b1](https://github.com/sst/opencode-sdk-go/commit/64bb1b1ee0cbe153abc6fb7bd9703b47911724d4)) +- **api:** api update ([64bb1b1](https://github.com/sst/opencode-sdk-go/commit/64bb1b1ee0cbe153abc6fb7bd9703b47911724d4)) ## 0.6.0 (2025-09-01) @@ -125,7 +124,7 @@ Full Changelog: [v0.5.0...v0.6.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([928e384](https://github.com/sst/opencode-sdk-go/commit/928e3843355f96899f046f002b84372281dad0c8)) +- **api:** api update ([928e384](https://github.com/sst/opencode-sdk-go/commit/928e3843355f96899f046f002b84372281dad0c8)) ## 0.5.0 (2025-08-31) @@ -133,7 +132,7 @@ Full Changelog: [v0.4.0...v0.5.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([44b281d](https://github.com/sst/opencode-sdk-go/commit/44b281d0bb39c5022a984ac9d0fca1529ccc0604)) +- **api:** api update ([44b281d](https://github.com/sst/opencode-sdk-go/commit/44b281d0bb39c5022a984ac9d0fca1529ccc0604)) ## 0.4.0 (2025-08-31) @@ -141,7 +140,7 @@ Full Changelog: [v0.3.0...v0.4.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([fa9d6ec](https://github.com/sst/opencode-sdk-go/commit/fa9d6ec6472e62f4f6605d0a71a7aa8bf8a24559)) +- **api:** api update ([fa9d6ec](https://github.com/sst/opencode-sdk-go/commit/fa9d6ec6472e62f4f6605d0a71a7aa8bf8a24559)) ## 0.3.0 (2025-08-31) @@ -149,7 +148,7 @@ Full Changelog: [v0.2.0...v0.3.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([aae1c06](https://github.com/sst/opencode-sdk-go/commit/aae1c06bb5a93a1cd9c589846a84b3f16246f5da)) +- **api:** api update ([aae1c06](https://github.com/sst/opencode-sdk-go/commit/aae1c06bb5a93a1cd9c589846a84b3f16246f5da)) ## 0.2.0 (2025-08-31) @@ -157,7 +156,7 @@ Full Changelog: [v0.1.0...v0.2.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([1472790](https://github.com/sst/opencode-sdk-go/commit/1472790542515f47bd46e2a9e28d8afea024cf9c)) +- **api:** api update ([1472790](https://github.com/sst/opencode-sdk-go/commit/1472790542515f47bd46e2a9e28d8afea024cf9c)) ## 0.1.0 (2025-08-31) @@ -165,61 +164,59 @@ Full Changelog: [v0.0.1...v0.1.0](https://github.com/sst/opencode-sdk-go/compare ### Features -* **api:** api update ([3f03ddd](https://github.com/sst/opencode-sdk-go/commit/3f03dddd5ec0de98f99ce48679077dcae9ceffd6)) -* **api:** api update ([e9f79c4](https://github.com/sst/opencode-sdk-go/commit/e9f79c4792b21ef64ab0431ffd76f5a71e04d182)) -* **api:** api update ([139a686](https://github.com/sst/opencode-sdk-go/commit/139a6862d2f0ab0c8ea791663d736868be3e96e6)) -* **api:** api update ([2ed0800](https://github.com/sst/opencode-sdk-go/commit/2ed0800b2c5b99877e9f7fde669a6c005fad6b77)) -* **api:** api update ([88a87a4](https://github.com/sst/opencode-sdk-go/commit/88a87a458f56ce0c18b502c73da933f614f56e8b)) -* **api:** api update ([0e5d65b](https://github.com/sst/opencode-sdk-go/commit/0e5d65b571e7b30dc6347e6730098878ebba3a42)) -* **api:** api update ([ba381f1](https://github.com/sst/opencode-sdk-go/commit/ba381f1e07aad24e9824df7d53befae2a644f69f)) -* **api:** api update ([3f429f5](https://github.com/sst/opencode-sdk-go/commit/3f429f5b4be5607433ef5fdc0d5bf67fe590d039)) -* **api:** api update ([9f34787](https://github.com/sst/opencode-sdk-go/commit/9f347876b35b7f898060c1a5f71c322e95978e3e)) -* **api:** api update ([379c8e0](https://github.com/sst/opencode-sdk-go/commit/379c8e00197e13aebaf2f2d61277b125f1f90011)) -* **api:** api update ([550511c](https://github.com/sst/opencode-sdk-go/commit/550511c4c5b5055ac8ff22b7b11731331bd9d088)) -* **api:** api update ([547f0c2](https://github.com/sst/opencode-sdk-go/commit/547f0c262f2df1ce83eaa7267d68be64bb29b841)) -* **api:** api update ([b7b0720](https://github.com/sst/opencode-sdk-go/commit/b7b07204bff314da24b1819c128835a43ef64065)) -* **api:** api update ([7250ffc](https://github.com/sst/opencode-sdk-go/commit/7250ffcba262b916c958ddecc2a42927982db39f)) -* **api:** api update ([17fbab7](https://github.com/sst/opencode-sdk-go/commit/17fbab73111a3eae488737c69b12370bc69c65f7)) -* **api:** api update ([1270b5c](https://github.com/sst/opencode-sdk-go/commit/1270b5cd81e6ac769dcd92ade6d877891bf51bd5)) -* **api:** api update ([a238d4a](https://github.com/sst/opencode-sdk-go/commit/a238d4abd6ed7d15f3547d27a4b6ecf4aec8431e)) -* **api:** api update ([7475655](https://github.com/sst/opencode-sdk-go/commit/7475655aca577fe4f807c2f02f92171f6a358e9c)) -* **api:** api update ([429d258](https://github.com/sst/opencode-sdk-go/commit/429d258bb56e9cdeb1528be3944bf5537ac26a96)) -* **api:** api update ([f250915](https://github.com/sst/opencode-sdk-go/commit/f2509157eaf1b453e741ee9482127cad2e3ace25)) -* **api:** api update ([5efc987](https://github.com/sst/opencode-sdk-go/commit/5efc987353801d1e772c20edf162b1c75da32743)) -* **api:** api update ([98a8350](https://github.com/sst/opencode-sdk-go/commit/98a83504f7cfc361e83314c3e79a4e9ff53f0560)) -* **api:** api update ([6da8bf8](https://github.com/sst/opencode-sdk-go/commit/6da8bf8bfe91d45991fb580753d77c5534fc0b1b)) -* **api:** api update ([f8c7148](https://github.com/sst/opencode-sdk-go/commit/f8c7148ae56143823186e2675a78e82676154956)) -* **api:** manual updates ([7cf038f](https://github.com/sst/opencode-sdk-go/commit/7cf038ffae5da1b77e1cef11b5fa166a53b467f2)) -* **api:** update via SDK Studio ([068a0eb](https://github.com/sst/opencode-sdk-go/commit/068a0eb025010da0c8d86fa1bb496a39dbedcef9)) -* **api:** update via SDK Studio ([ca651ed](https://github.com/sst/opencode-sdk-go/commit/ca651edaf71d1f3678f929287474f5bc4f1aad10)) -* **api:** update via SDK Studio ([13550a5](https://github.com/sst/opencode-sdk-go/commit/13550a5c65d77325e945ed99fe0799cd1107b775)) -* **api:** update via SDK Studio ([7b73730](https://github.com/sst/opencode-sdk-go/commit/7b73730c7fa62ba966dda3541c3e97b49be8d2bf)) -* **api:** update via SDK Studio ([9e39a59](https://github.com/sst/opencode-sdk-go/commit/9e39a59b3d5d1bd5e64633732521fb28362cc70e)) -* **api:** update via SDK Studio ([9609d1b](https://github.com/sst/opencode-sdk-go/commit/9609d1b1db7806d00cb846c9914cb4935cdedf52)) -* **api:** update via SDK Studio ([51315fa](https://github.com/sst/opencode-sdk-go/commit/51315fa2eae424743ea79701e67d44447c44144d)) -* **api:** update via SDK Studio ([af07955](https://github.com/sst/opencode-sdk-go/commit/af0795543240aefaf04fc7663a348825541c79ed)) -* **api:** update via SDK Studio ([5e3468a](https://github.com/sst/opencode-sdk-go/commit/5e3468a0aaa5ed3b13e019c3a24e0ba9147d1675)) -* **api:** update via SDK Studio ([0a73e04](https://github.com/sst/opencode-sdk-go/commit/0a73e04c23c90b2061611edaa8fd6282dc0ce397)) -* **api:** update via SDK Studio ([9b7883a](https://github.com/sst/opencode-sdk-go/commit/9b7883a144eeac526d9d04538e0876a9d18bb844)) -* **client:** expand max streaming buffer size ([76303e5](https://github.com/sst/opencode-sdk-go/commit/76303e51067e78e732af26ced9d83b8bad7655c3)) -* **client:** support optional json html escaping ([449748f](https://github.com/sst/opencode-sdk-go/commit/449748f35a1d8cb6f91dc36d25bf9489f4f371bd)) - +- **api:** api update ([3f03ddd](https://github.com/sst/opencode-sdk-go/commit/3f03dddd5ec0de98f99ce48679077dcae9ceffd6)) +- **api:** api update ([e9f79c4](https://github.com/sst/opencode-sdk-go/commit/e9f79c4792b21ef64ab0431ffd76f5a71e04d182)) +- **api:** api update ([139a686](https://github.com/sst/opencode-sdk-go/commit/139a6862d2f0ab0c8ea791663d736868be3e96e6)) +- **api:** api update ([2ed0800](https://github.com/sst/opencode-sdk-go/commit/2ed0800b2c5b99877e9f7fde669a6c005fad6b77)) +- **api:** api update ([88a87a4](https://github.com/sst/opencode-sdk-go/commit/88a87a458f56ce0c18b502c73da933f614f56e8b)) +- **api:** api update ([0e5d65b](https://github.com/sst/opencode-sdk-go/commit/0e5d65b571e7b30dc6347e6730098878ebba3a42)) +- **api:** api update ([ba381f1](https://github.com/sst/opencode-sdk-go/commit/ba381f1e07aad24e9824df7d53befae2a644f69f)) +- **api:** api update ([3f429f5](https://github.com/sst/opencode-sdk-go/commit/3f429f5b4be5607433ef5fdc0d5bf67fe590d039)) +- **api:** api update ([9f34787](https://github.com/sst/opencode-sdk-go/commit/9f347876b35b7f898060c1a5f71c322e95978e3e)) +- **api:** api update ([379c8e0](https://github.com/sst/opencode-sdk-go/commit/379c8e00197e13aebaf2f2d61277b125f1f90011)) +- **api:** api update ([550511c](https://github.com/sst/opencode-sdk-go/commit/550511c4c5b5055ac8ff22b7b11731331bd9d088)) +- **api:** api update ([547f0c2](https://github.com/sst/opencode-sdk-go/commit/547f0c262f2df1ce83eaa7267d68be64bb29b841)) +- **api:** api update ([b7b0720](https://github.com/sst/opencode-sdk-go/commit/b7b07204bff314da24b1819c128835a43ef64065)) +- **api:** api update ([7250ffc](https://github.com/sst/opencode-sdk-go/commit/7250ffcba262b916c958ddecc2a42927982db39f)) +- **api:** api update ([17fbab7](https://github.com/sst/opencode-sdk-go/commit/17fbab73111a3eae488737c69b12370bc69c65f7)) +- **api:** api update ([1270b5c](https://github.com/sst/opencode-sdk-go/commit/1270b5cd81e6ac769dcd92ade6d877891bf51bd5)) +- **api:** api update ([a238d4a](https://github.com/sst/opencode-sdk-go/commit/a238d4abd6ed7d15f3547d27a4b6ecf4aec8431e)) +- **api:** api update ([7475655](https://github.com/sst/opencode-sdk-go/commit/7475655aca577fe4f807c2f02f92171f6a358e9c)) +- **api:** api update ([429d258](https://github.com/sst/opencode-sdk-go/commit/429d258bb56e9cdeb1528be3944bf5537ac26a96)) +- **api:** api update ([f250915](https://github.com/sst/opencode-sdk-go/commit/f2509157eaf1b453e741ee9482127cad2e3ace25)) +- **api:** api update ([5efc987](https://github.com/sst/opencode-sdk-go/commit/5efc987353801d1e772c20edf162b1c75da32743)) +- **api:** api update ([98a8350](https://github.com/sst/opencode-sdk-go/commit/98a83504f7cfc361e83314c3e79a4e9ff53f0560)) +- **api:** api update ([6da8bf8](https://github.com/sst/opencode-sdk-go/commit/6da8bf8bfe91d45991fb580753d77c5534fc0b1b)) +- **api:** api update ([f8c7148](https://github.com/sst/opencode-sdk-go/commit/f8c7148ae56143823186e2675a78e82676154956)) +- **api:** manual updates ([7cf038f](https://github.com/sst/opencode-sdk-go/commit/7cf038ffae5da1b77e1cef11b5fa166a53b467f2)) +- **api:** update via SDK Studio ([068a0eb](https://github.com/sst/opencode-sdk-go/commit/068a0eb025010da0c8d86fa1bb496a39dbedcef9)) +- **api:** update via SDK Studio ([ca651ed](https://github.com/sst/opencode-sdk-go/commit/ca651edaf71d1f3678f929287474f5bc4f1aad10)) +- **api:** update via SDK Studio ([13550a5](https://github.com/sst/opencode-sdk-go/commit/13550a5c65d77325e945ed99fe0799cd1107b775)) +- **api:** update via SDK Studio ([7b73730](https://github.com/sst/opencode-sdk-go/commit/7b73730c7fa62ba966dda3541c3e97b49be8d2bf)) +- **api:** update via SDK Studio ([9e39a59](https://github.com/sst/opencode-sdk-go/commit/9e39a59b3d5d1bd5e64633732521fb28362cc70e)) +- **api:** update via SDK Studio ([9609d1b](https://github.com/sst/opencode-sdk-go/commit/9609d1b1db7806d00cb846c9914cb4935cdedf52)) +- **api:** update via SDK Studio ([51315fa](https://github.com/sst/opencode-sdk-go/commit/51315fa2eae424743ea79701e67d44447c44144d)) +- **api:** update via SDK Studio ([af07955](https://github.com/sst/opencode-sdk-go/commit/af0795543240aefaf04fc7663a348825541c79ed)) +- **api:** update via SDK Studio ([5e3468a](https://github.com/sst/opencode-sdk-go/commit/5e3468a0aaa5ed3b13e019c3a24e0ba9147d1675)) +- **api:** update via SDK Studio ([0a73e04](https://github.com/sst/opencode-sdk-go/commit/0a73e04c23c90b2061611edaa8fd6282dc0ce397)) +- **api:** update via SDK Studio ([9b7883a](https://github.com/sst/opencode-sdk-go/commit/9b7883a144eeac526d9d04538e0876a9d18bb844)) +- **client:** expand max streaming buffer size ([76303e5](https://github.com/sst/opencode-sdk-go/commit/76303e51067e78e732af26ced9d83b8bad7655c3)) +- **client:** support optional json html escaping ([449748f](https://github.com/sst/opencode-sdk-go/commit/449748f35a1d8cb6f91dc36d25bf9489f4f371bd)) ### Bug Fixes -* **client:** process custom base url ahead of time ([9b360d6](https://github.com/sst/opencode-sdk-go/commit/9b360d642cf6f302104308af5622e17099899e5f)) -* **client:** resolve lint errors in streaming tests ([4d36cb0](https://github.com/sst/opencode-sdk-go/commit/4d36cb09fc9d436734d5dab1c499acaa88568ff7)) -* close body before retrying ([4da3f7f](https://github.com/sst/opencode-sdk-go/commit/4da3f7f372bad222a189ba3eabcfde3373166ae5)) -* don't try to deserialize as json when ResponseBodyInto is []byte ([595291f](https://github.com/sst/opencode-sdk-go/commit/595291f6dba6af472f160b9f8e3d145002f43a4a)) - +- **client:** process custom base url ahead of time ([9b360d6](https://github.com/sst/opencode-sdk-go/commit/9b360d642cf6f302104308af5622e17099899e5f)) +- **client:** resolve lint errors in streaming tests ([4d36cb0](https://github.com/sst/opencode-sdk-go/commit/4d36cb09fc9d436734d5dab1c499acaa88568ff7)) +- close body before retrying ([4da3f7f](https://github.com/sst/opencode-sdk-go/commit/4da3f7f372bad222a189ba3eabcfde3373166ae5)) +- don't try to deserialize as json when ResponseBodyInto is []byte ([595291f](https://github.com/sst/opencode-sdk-go/commit/595291f6dba6af472f160b9f8e3d145002f43a4a)) ### Chores -* **ci:** only run for pushes and fork pull requests ([bea59b8](https://github.com/sst/opencode-sdk-go/commit/bea59b886800ef555f89c47a9256d6392ed2e53d)) -* **internal:** codegen related update ([6a22ce6](https://github.com/sst/opencode-sdk-go/commit/6a22ce6df155f5003e80b8a75686a9e513a5568a)) -* **internal:** fix lint script for tests ([391c482](https://github.com/sst/opencode-sdk-go/commit/391c482148ed0a77c4ad52807abeb2d540b56797)) -* **internal:** update comment in script ([b7f1c3e](https://github.com/sst/opencode-sdk-go/commit/b7f1c3e16935c71e243004b8f321d661cd8e9474)) -* lint tests ([616796b](https://github.com/sst/opencode-sdk-go/commit/616796b761704bde6be5c6c2428f28c79c7f05ff)) -* lint tests in subpackages ([50c82ff](https://github.com/sst/opencode-sdk-go/commit/50c82ff0757c973834b68adc22566b70f767b611)) -* sync repo ([2f34d5d](https://github.com/sst/opencode-sdk-go/commit/2f34d5d53e56e9cdc3df99be7ee7efc83dd977a3)) -* update @stainless-api/prism-cli to v5.15.0 ([2f24852](https://github.com/sst/opencode-sdk-go/commit/2f2485216d4f4891d1fbfbc23ff8410c2f35152a)) +- **ci:** only run for pushes and fork pull requests ([bea59b8](https://github.com/sst/opencode-sdk-go/commit/bea59b886800ef555f89c47a9256d6392ed2e53d)) +- **internal:** codegen related update ([6a22ce6](https://github.com/sst/opencode-sdk-go/commit/6a22ce6df155f5003e80b8a75686a9e513a5568a)) +- **internal:** fix lint script for tests ([391c482](https://github.com/sst/opencode-sdk-go/commit/391c482148ed0a77c4ad52807abeb2d540b56797)) +- **internal:** update comment in script ([b7f1c3e](https://github.com/sst/opencode-sdk-go/commit/b7f1c3e16935c71e243004b8f321d661cd8e9474)) +- lint tests ([616796b](https://github.com/sst/opencode-sdk-go/commit/616796b761704bde6be5c6c2428f28c79c7f05ff)) +- lint tests in subpackages ([50c82ff](https://github.com/sst/opencode-sdk-go/commit/50c82ff0757c973834b68adc22566b70f767b611)) +- sync repo ([2f34d5d](https://github.com/sst/opencode-sdk-go/commit/2f34d5d53e56e9cdc3df99be7ee7efc83dd977a3)) +- update @stainless-api/prism-cli to v5.15.0 ([2f24852](https://github.com/sst/opencode-sdk-go/commit/2f2485216d4f4891d1fbfbc23ff8410c2f35152a)) diff --git a/packages/sdk/go/release-please-config.json b/packages/sdk/go/release-please-config.json index a38198eca..32960ce27 100644 --- a/packages/sdk/go/release-please-config.json +++ b/packages/sdk/go/release-please-config.json @@ -60,8 +60,5 @@ } ], "release-type": "go", - "extra-files": [ - "internal/version.go", - "README.md" - ] -} \ No newline at end of file + "extra-files": ["internal/version.go", "README.md"] +} diff --git a/packages/sdk/js/package.json b/packages/sdk/js/package.json index 97830ba1b..bff4866a0 100644 --- a/packages/sdk/js/package.json +++ b/packages/sdk/js/package.json @@ -26,4 +26,4 @@ "publishConfig": { "directory": "dist" } -} \ No newline at end of file +} diff --git a/packages/sdk/js/src/server.ts b/packages/sdk/js/src/server.ts index a09e14ab2..151477457 100644 --- a/packages/sdk/js/src/server.ts +++ b/packages/sdk/js/src/server.ts @@ -28,13 +28,17 @@ export async function createOpencodeServer(options?: ServerOptions) { options ?? {}, ) - const proc = spawn(`opencode`, [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`], { - signal: options.signal, - env: { - ...process.env, - OPENCODE_CONFIG_CONTENT: JSON.stringify(options.config ?? {}), + const proc = spawn( + `opencode`, + [`serve`, `--hostname=${options.hostname}`, `--port=${options.port}`], + { + signal: options.signal, + env: { + ...process.env, + OPENCODE_CONFIG_CONTENT: JSON.stringify(options.config ?? {}), + }, }, - }) + ) const url = await new Promise((resolve, reject) => { const id = setTimeout(() => { diff --git a/packages/sdk/js/sst-env.d.ts b/packages/sdk/js/sst-env.d.ts index 9b9de7327..bd5588217 100644 --- a/packages/sdk/js/sst-env.d.ts +++ b/packages/sdk/js/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/sdk/python/README.md b/packages/sdk/python/README.md index a17c36ab3..5d54434af 100644 --- a/packages/sdk/python/README.md +++ b/packages/sdk/python/README.md @@ -2,50 +2,59 @@ This package provides a Python SDK for the Opencode API. It is generated using openapi-python-client (not Stainless). - Documentation + - Full docs: see `mkdocs` site under `packages/sdk/python/docs/` - Preview locally: + ```bash uv run --project packages/sdk/python mkdocs serve -f packages/sdk/python/mkdocs.yml ``` Badges + - PyPI: https://img.shields.io/pypi/v/opencode-ai?style=flat-square Requirements + - Python 3.8+ - uv (recommended) -> https://docs.astral.sh/uv/ - openapi-python-client (invoked via `uvx`) Install uv + ```bash # macOS/Linux curl -LsSf https://astral.sh/uv/install.sh | sh ``` Set up the environment (from this directory) + ```bash uv sync --dev ``` Generate client code (from CLI-generated spec) + ```bash # From repository root OR from this directory uv run python packages/sdk/python/scripts/generate.py --source cli ``` Alternatively, fetch spec from a running server + ```bash uv run python packages/sdk/python/scripts/generate.py --source server --server-url http://localhost:4096/doc ``` This will: -1) Produce an OpenAPI spec from the local CLI or a running server -2) Run openapi-python-client (via `uvx`) to generate client code -3) Copy the generated Python package into src/opencode_ai + +1. Produce an OpenAPI spec from the local CLI or a running server +2. Run openapi-python-client (via `uvx`) to generate client code +3. Copy the generated Python package into src/opencode_ai Usage (after generation) + ```python from opencode_ai import OpenCodeClient @@ -77,6 +86,7 @@ client = OpenCodeClient(retries=2, backoff_factor=0.1) ``` Notes + - We intentionally do not use Stainless for the Python SDK. - The generator targets OpenAPI 3.1 emitted by the opencode server at /doc. - See scripts/generate.py for details and customization points. diff --git a/packages/sdk/python/docs/generation.md b/packages/sdk/python/docs/generation.md index f949760a1..2151e3709 100644 --- a/packages/sdk/python/docs/generation.md +++ b/packages/sdk/python/docs/generation.md @@ -3,10 +3,12 @@ The SDK is generated from the Opencode server's OpenAPI 3.1 spec. Two source modes are supported: + - CLI (default): runs `bun dev generate` to emit the OpenAPI JSON - Server: fetches `http://localhost:4096/doc` from a running server Generator command + ```bash # From repo root uv run --project packages/sdk/python python packages/sdk/python/scripts/generate.py --source cli @@ -15,5 +17,6 @@ uv run --project packages/sdk/python python packages/sdk/python/scripts/generate ``` Post-generation + - The generator injects `extras.py` (OpenCodeClient) and patches `__init__.py` to export it - Code is formatted with `ruff` (imports) and `black` diff --git a/packages/sdk/python/docs/index.md b/packages/sdk/python/docs/index.md index bc7b550f9..e0cfe5d5d 100644 --- a/packages/sdk/python/docs/index.md +++ b/packages/sdk/python/docs/index.md @@ -3,6 +3,7 @@ The official Python client for the Opencode API, generated from the OpenAPI spec and extended with ergonomic helpers. Highlights + - Provider-agnostic client generated from OpenAPI 3.1 - Thin convenience wrapper (OpenCodeClient) for common tasks - Sync and async SSE streaming for live event feeds diff --git a/packages/sdk/python/docs/installation.md b/packages/sdk/python/docs/installation.md index f66e217ae..fe48f5189 100644 --- a/packages/sdk/python/docs/installation.md +++ b/packages/sdk/python/docs/installation.md @@ -1,26 +1,31 @@ # Installation Requirements + - Python 3.8+ - uv (recommended) -> https://docs.astral.sh/uv/ Install uv + ```bash curl -LsSf https://astral.sh/uv/install.sh | sh ``` Project setup + ```bash # From repo root or this directory uv sync --dev --project packages/sdk/python ``` Using pip (alternative) + ```bash pip install opencode-ai ``` Preview docs locally + ```bash # From repo root uv run --project packages/sdk/python mkdocs serve -f packages/sdk/python/mkdocs.yml diff --git a/packages/sdk/python/docs/publishing.md b/packages/sdk/python/docs/publishing.md index c598baa88..c05a12a33 100644 --- a/packages/sdk/python/docs/publishing.md +++ b/packages/sdk/python/docs/publishing.md @@ -3,6 +3,7 @@ Automated publishing runs on GitHub Releases. Workflow + - Create a new Release (the tag value becomes the package version) - The `publish-python-sdk` workflow will: - Generate the SDK from OpenAPI (CLI path) @@ -10,9 +11,11 @@ Workflow - Build wheel/sdist and upload to PyPI Prerequisites + - Repository secret: `PYPI_API_TOKEN` Manual publish + ```bash # TestPyPI REPOSITORY=testpypi PYPI_TOKEN=$TEST_PYPI_API_TOKEN \ diff --git a/packages/sdk/python/docs/testing.md b/packages/sdk/python/docs/testing.md index 3119035d0..e2c777e31 100644 --- a/packages/sdk/python/docs/testing.md +++ b/packages/sdk/python/docs/testing.md @@ -11,5 +11,6 @@ uv run --project packages/sdk/python pytest -q ``` Notes + - Integration test starts a headless opencode server via Bun in a subprocess - SSE behavior is validated using real streaming from the server diff --git a/packages/sdk/python/mkdocs.yml b/packages/sdk/python/mkdocs.yml index 25de28fb3..565eb7701 100644 --- a/packages/sdk/python/mkdocs.yml +++ b/packages/sdk/python/mkdocs.yml @@ -3,7 +3,7 @@ site_description: Official Python SDK for the Opencode API site_url: https://opencode.ai repo_url: https://github.com/sst/opencode repo_name: sst/opencode -edit_uri: '' +edit_uri: "" theme: name: material features: diff --git a/packages/slack/src/index.ts b/packages/slack/src/index.ts index 046f069e2..2a952b63e 100644 --- a/packages/slack/src/index.ts +++ b/packages/slack/src/index.ts @@ -19,7 +19,10 @@ const opencode = await createOpencode({ }) console.log("✅ Opencode server ready") -const sessions = new Map() +const sessions = new Map< + string, + { client: any; server: any; sessionId: string; channel: string; thread: string } +>() ;(async () => { const events = await opencode.client.event.subscribe() for await (const event of events.stream) { @@ -81,7 +84,10 @@ app.message(async ({ message, say }) => { if (createResult.error) { console.error("❌ Failed to create session:", createResult.error) - await say({ text: "Sorry, I had trouble creating a session. Please try again.", thread_ts: thread }) + await say({ + text: "Sorry, I had trouble creating a session. Please try again.", + thread_ts: thread, + }) return } @@ -108,7 +114,10 @@ app.message(async ({ message, say }) => { if (result.error) { console.error("❌ Failed to send message:", result.error) - await say({ text: "Sorry, I had trouble processing your message. Please try again.", thread_ts: thread }) + await say({ + text: "Sorry, I had trouble processing your message. Please try again.", + thread_ts: thread, + }) return } diff --git a/packages/slack/sst-env.d.ts b/packages/slack/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/slack/sst-env.d.ts +++ b/packages/slack/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/ui/src/assets/favicon/site.webmanifest b/packages/ui/src/assets/favicon/site.webmanifest index f7522f8f3..41290e840 100644 --- a/packages/ui/src/assets/favicon/site.webmanifest +++ b/packages/ui/src/assets/favicon/site.webmanifest @@ -18,4 +18,4 @@ "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" -} \ No newline at end of file +} diff --git a/packages/ui/src/components/checkbox.tsx b/packages/ui/src/components/checkbox.tsx index 2009a430b..ac9abfdab 100644 --- a/packages/ui/src/components/checkbox.tsx +++ b/packages/ui/src/components/checkbox.tsx @@ -9,7 +9,14 @@ export interface CheckboxProps extends ParentProps local.children) return ( @@ -35,7 +42,9 @@ export function Checkbox(props: CheckboxProps) { - {local.description} + + {local.description} +
    diff --git a/packages/ui/src/components/dialog.tsx b/packages/ui/src/components/dialog.tsx index ce7a4b3ac..fdbb9022d 100644 --- a/packages/ui/src/components/dialog.tsx +++ b/packages/ui/src/components/dialog.tsx @@ -38,7 +38,11 @@ export function DialogRoot(props: DialogProps) { return ( - + {props.trigger} diff --git a/packages/ui/src/styles/base.css b/packages/ui/src/styles/base.css index b80398e1d..02b473238 100644 --- a/packages/ui/src/styles/base.css +++ b/packages/ui/src/styles/base.css @@ -269,8 +269,8 @@ textarea, crash when using `color-mix(…)` with `currentcolor`. (https://github.com/tailwindlabs/tailwindcss/issues/17194) */ -@supports (not (-webkit-appearance: -apple-pay-button)) /* Not Safari */ or (contain-intrinsic-size: 1px) - /* Safari 17+ */ { +@supports (not (-webkit-appearance: -apple-pay-button)) /* Not Safari */ or + (contain-intrinsic-size: 1px) /* Safari 17+ */ { ::placeholder { color: color-mix(in oklab, currentcolor 50%, transparent); } diff --git a/packages/ui/src/styles/tailwind/colors.css b/packages/ui/src/styles/tailwind/colors.css index 527c43109..a5f982d6f 100644 --- a/packages/ui/src/styles/tailwind/colors.css +++ b/packages/ui/src/styles/tailwind/colors.css @@ -232,4 +232,4 @@ --color-border-weaker-focus: var(--border-weaker-focus); --color-button-ghost-hover: var(--button-ghost-hover); --color-button-ghost-hover2: var(--button-ghost-hover2); -} \ No newline at end of file +} diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index 20c43b10e..6187eef9d 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -56,23 +56,18 @@ 0 6px 8px -4px rgba(19, 16, 16, 0.12), 0 4px 3px -2px rgba(19, 16, 16, 0.12), 0 1px 2px -1px rgba(19, 16, 16, 0.12); --shadow-xs-border: - 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.20)), - 0 1px 2px -1px rgba(19, 16, 16, 0.04), - 0 1px 2px 0 rgba(19, 16, 16, 0.06), - 0 1px 3px 0 rgba(19, 16, 16, 0.08); + 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.2)), 0 1px 2px -1px rgba(19, 16, 16, 0.04), + 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08); --shadow-xs-border-select: 0 0 0 3px var(--border-weak-selected, rgba(1, 103, 255, 0.29)), 0 0 0 1px var(--border-selected, rgba(0, 74, 255, 0.99)), 0 1px 2px -1px rgba(19, 16, 16, 0.25), 0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12); --shadow-xs-border-focus: - 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.20)), - 0 1px 2px -1px rgba(19, 16, 16, 0.25), - 0 1px 2px 0 rgba(19, 16, 16, 0.08), - 0 1px 3px 0 rgba(19, 16, 16, 0.12), - 0 0 0 2px var(--background-weak, #F1F0F0), + 0 0 0 1px var(--border-base, rgba(11, 6, 0, 0.2)), 0 1px 2px -1px rgba(19, 16, 16, 0.25), + 0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12), + 0 0 0 2px var(--background-weak, #f1f0f0), 0 0 0 3px var(--border-selected, rgba(0, 74, 255, 0.99)); - --text-mix-blend-mode: multiply; } @@ -413,7 +408,7 @@ --text-on-brand-weaker: var(--smoke-dark-alpha-8); --text-on-brand-strong: var(--smoke-dark-alpha-12); --button-secondary-base: var(--smoke-dark-4); - --button-secondary-hover: #2A2727; + --button-secondary-hover: #2a2727; --border-base: var(--smoke-dark-alpha-7); --border-hover: var(--smoke-dark-alpha-8); --border-active: var(--smoke-dark-alpha-9); diff --git a/packages/ui/sst-env.d.ts b/packages/ui/sst-env.d.ts index b6a7e9066..0397645b5 100644 --- a/packages/ui/sst-env.d.ts +++ b/packages/ui/sst-env.d.ts @@ -6,4 +6,4 @@ /// import "sst" -export {} \ No newline at end of file +export {} diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json index 440aa8f9b..c35314407 100644 --- a/packages/ui/tsconfig.json +++ b/packages/ui/tsconfig.json @@ -13,16 +13,9 @@ "module": "ESNext", "moduleResolution": "bundler", "noEmit": true, - "lib": [ - "es2022", - "dom", - "dom.iterable" - ], + "lib": ["es2022", "dom", "dom.iterable"], // Type Checking & Safety "strict": true, - "types": [ - "vite/client", - "bun" - ] + "types": ["vite/client", "bun"] } } diff --git a/packages/web/config.mjs b/packages/web/config.mjs index 5e2c8d3e4..7adb147cd 100644 --- a/packages/web/config.mjs +++ b/packages/web/config.mjs @@ -2,7 +2,8 @@ const stage = process.env.SST_STAGE || "dev" export default { url: stage === "production" ? "https://opencode.ai" : `https://${stage}.opencode.ai`, - console: stage === "production" ? "https://opencode.ai/auth" : `https://${stage}.opencode.ai/auth`, + console: + stage === "production" ? "https://opencode.ai/auth" : `https://${stage}.opencode.ai/auth`, email: "contact@anoma.ly", socialCard: "https://social-cards.sst.dev", github: "https://github.com/sst/opencode", diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx index 062449712..486b6e44b 100644 --- a/packages/web/src/components/Share.tsx +++ b/packages/web/src/components/Share.tsx @@ -1,4 +1,14 @@ -import { For, Show, onMount, Suspense, onCleanup, createMemo, createSignal, SuspenseList, createEffect } from "solid-js" +import { + For, + Show, + onMount, + Suspense, + onCleanup, + createMemo, + createSignal, + SuspenseList, + createEffect, +} from "solid-js" import { DateTime } from "luxon" import { createStore, reconcile, unwrap } from "solid-js/store" import { IconArrowDown } from "./icons" @@ -66,8 +76,13 @@ export default function Share(props: { id: string; api: string; info: Session.In }, messages: {}, }) - const messages = createMemo(() => Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id))) - const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>(["disconnected", "Disconnected"]) + const messages = createMemo(() => + Object.values(store.messages).toSorted((a, b) => a.id?.localeCompare(b.id)), + ) + const [connectionStatus, setConnectionStatus] = createSignal<[Status, string?]>([ + "disconnected", + "Disconnected", + ]) createEffect(() => { console.log(unwrap(store)) }) @@ -330,7 +345,9 @@ export default function Share(props: { id: string; api: string; info: Session.In
    {DateTime.fromMillis(data().created || 0).toLocaleString(DateTime.DATETIME_MED)}
    @@ -352,7 +369,10 @@ export default function Share(props: { id: string; api: string; info: Session.In if (x.type === "text" && x.synthetic === true) return false if (x.type === "tool" && x.tool === "todoread") return false if (x.type === "text" && !x.text) return false - if (x.type === "tool" && (x.state.status === "pending" || x.state.status === "running")) + if ( + x.type === "tool" && + (x.state.status === "pending" || x.state.status === "running") + ) return false return true }), @@ -364,7 +384,8 @@ export default function Share(props: { id: string; api: string; info: Session.In {(part, partIndex) => { const last = createMemo( () => - data().messages.length === msgIndex() + 1 && filteredParts().length === partIndex() + 1, + data().messages.length === msgIndex() + 1 && + filteredParts().length === partIndex() + 1, ) onMount(() => { @@ -381,7 +402,9 @@ export default function Share(props: { id: string; api: string; info: Session.In } }) - return + return ( + + ) }} @@ -406,7 +429,11 @@ export default function Share(props: { id: string; api: string; info: Session.In
  • Input Tokens - {data().tokens.input ? {data().tokens.input} : } + {data().tokens.input ? ( + {data().tokens.input} + ) : ( + + )}
  • Output Tokens @@ -560,7 +587,8 @@ export function fromV1(v1: Message.Info): MessageWithParts { } } - const { title, time, ...metadata } = v1.metadata.tool[part.toolInvocation.toolCallId] + const { title, time, ...metadata } = + v1.metadata.tool[part.toolInvocation.toolCallId] if (part.toolInvocation.state === "call") { return { status: "running", diff --git a/packages/web/src/components/icons/index.tsx b/packages/web/src/components/icons/index.tsx index 62445611f..9aef7a927 100644 --- a/packages/web/src/components/icons/index.tsx +++ b/packages/web/src/components/icons/index.tsx @@ -4418,7 +4418,14 @@ export function IconMultiSelect(props: JSX.SvgSVGAttributes) { } export function IconSettings(props: JSX.SvgSVGAttributes) { return ( - + +
    { diff --git a/packages/web/src/components/share/content-code.tsx b/packages/web/src/components/share/content-code.tsx index 2f383b8be..0f9d28fa3 100644 --- a/packages/web/src/components/share/content-code.tsx +++ b/packages/web/src/components/share/content-code.tsx @@ -26,7 +26,11 @@ export function ContentCode(props: Props) { ) return ( -
    +
    ) } diff --git a/packages/web/src/components/share/content-diff.tsx b/packages/web/src/components/share/content-diff.tsx index 9ccd554d0..79c2723cb 100644 --- a/packages/web/src/components/share/content-diff.tsx +++ b/packages/web/src/components/share/content-diff.tsx @@ -124,7 +124,9 @@ export function ContentDiff(props: Props) { // Collect consecutive modified/removed/added rows while ( i < currentRows.length && - (currentRows[i].type === "modified" || currentRows[i].type === "removed" || currentRows[i].type === "added") + (currentRows[i].type === "modified" || + currentRows[i].type === "removed" || + currentRows[i].type === "added") ) { const row = currentRows[i] if (row.left && (row.type === "removed" || row.type === "modified")) { @@ -162,10 +164,16 @@ export function ContentDiff(props: Props) {
    {rows().map((r) => (
    -
    +
    -
    +
    @@ -176,7 +184,11 @@ export function ContentDiff(props: Props) { {mobileRows().map((block) => (
    {block.lines.map((line) => ( -
    +
    ))} diff --git a/packages/web/src/components/share/copy-button.tsx b/packages/web/src/components/share/copy-button.tsx index 892d5553f..6f51bb4d8 100644 --- a/packages/web/src/components/share/copy-button.tsx +++ b/packages/web/src/components/share/copy-button.tsx @@ -21,7 +21,11 @@ export function CopyButton(props: CopyButtonProps) { return (
    ) diff --git a/packages/web/src/components/share/part.tsx b/packages/web/src/components/share/part.tsx index f7a6a9304..6ad18273b 100644 --- a/packages/web/src/components/share/part.tsx +++ b/packages/web/src/components/share/part.tsx @@ -1,6 +1,15 @@ import map from "lang-map" import { DateTime } from "luxon" -import { For, Show, Match, Switch, type JSX, createMemo, createSignal, type ParentProps } from "solid-js" +import { + For, + Show, + Match, + Switch, + type JSX, + createMemo, + createSignal, + type ParentProps, +} from "solid-js" import { IconHashtag, IconSparkles, @@ -19,7 +28,14 @@ import { IconMagnifyingGlass, IconDocumentMagnifyingGlass, } from "../icons" -import { IconMeta, IconRobot, IconOpenAI, IconGemini, IconAnthropic, IconBrain } from "../icons/custom" +import { + IconMeta, + IconRobot, + IconOpenAI, + IconGemini, + IconAnthropic, + IconBrain, +} from "../icons/custom" import { ContentCode } from "./content-code" import { ContentDiff } from "./content-diff" import { ContentText } from "./content-text" @@ -79,7 +95,11 @@ export function Part(props: PartProps) { {(model) => } @@ -147,7 +167,9 @@ export function Part(props: PartProps) { DateTime.DATETIME_FULL_WITH_SECONDS, )} > - {DateTime.fromMillis(props.message.time.completed).toLocaleString(DateTime.DATETIME_MED)} + {DateTime.fromMillis(props.message.time.completed).toLocaleString( + DateTime.DATETIME_MED, + )} )}
    @@ -343,7 +365,10 @@ function getShikiLang(filename: string) { return type ? (overrides[type] ?? type) : "plaintext" } -function getDiagnostics(diagnosticsByFile: Record, currentFile: string): JSX.Element[] { +function getDiagnostics( + diagnosticsByFile: Record, + currentFile: string, +): JSX.Element[] { const result: JSX.Element[] = [] if (diagnosticsByFile === undefined || diagnosticsByFile[currentFile] === undefined) return result @@ -397,7 +422,9 @@ export function TodoWriteTool(props: ToolProps) { completed: 2, } const todos = createMemo(() => - ((props.state.input?.todos ?? []) as Todo[]).slice().sort((a, b) => priority[a.status] - priority[b.status]), + ((props.state.input?.todos ?? []) as Todo[]) + .slice() + .sort((a, b) => priority[a.status] - priority[b.status]), ) const starting = () => todos().every((t: Todo) => t.status === "pending") const finished = () => todos().every((t: Todo) => t.status === "completed") @@ -439,13 +466,23 @@ export function GrepTool(props: ToolProps) { 0}> - +
    @@ -505,7 +542,9 @@ export function WebFetchTool(props: ToolProps) { } export function ReadTool(props: ToolProps) { - const filePath = createMemo(() => stripWorkingDirectory(props.state.input?.filePath, props.message.path.cwd)) + const filePath = createMemo(() => + stripWorkingDirectory(props.state.input?.filePath, props.message.path.cwd), + ) return ( <> @@ -522,7 +561,10 @@ export function ReadTool(props: ToolProps) { - + @@ -537,8 +579,12 @@ export function ReadTool(props: ToolProps) { } export function WriteTool(props: ToolProps) { - const filePath = createMemo(() => stripWorkingDirectory(props.state.input?.filePath, props.message.path.cwd)) - const diagnostics = createMemo(() => getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath)) + const filePath = createMemo(() => + stripWorkingDirectory(props.state.input?.filePath, props.message.path.cwd), + ) + const diagnostics = createMemo(() => + getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath), + ) return ( <> @@ -558,7 +604,10 @@ export function WriteTool(props: ToolProps) { - + @@ -568,8 +617,12 @@ export function WriteTool(props: ToolProps) { } export function EditTool(props: ToolProps) { - const filePath = createMemo(() => stripWorkingDirectory(props.state.input.filePath, props.message.path.cwd)) - const diagnostics = createMemo(() => getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath)) + const filePath = createMemo(() => + stripWorkingDirectory(props.state.input.filePath, props.message.path.cwd), + ) + const diagnostics = createMemo(() => + getDiagnostics(props.state.metadata?.diagnostics, props.state.input.filePath), + ) return ( <> @@ -586,7 +639,10 @@ export function EditTool(props: ToolProps) {
    - +
    @@ -619,7 +675,11 @@ export function GlobTool(props: ToolProps) { 0}>
    @@ -642,7 +702,12 @@ function ResultsButton(props: ResultsButtonProps) { return ( <> -
    -
    - -
    New session
    -
    - -
    - {getDirectory(sync.data.path.directory)} - {getFilename(sync.data.path.directory)} -
    + +
    New session
    +
    + +
    + {getDirectory(sync.data.path.directory)} + {getFilename(sync.data.path.directory)}
    -
    - -
    - Last modified  - - {DateTime.fromMillis(sync.data.project.time.created).toRelative()} - -
    +
    +
    + +
    + Last modified  + + {DateTime.fromMillis(sync.data.project.time.created).toRelative()} +
    - } - > - {(_) => { - return ( -
    -
    +
    + } + > + {(_) => { + return ( +
    +
    +
    + + + +
    +
    1}>
    + } + > + +
    + ) + }} + - {/* */} + + +
    +
    +
    +
    +
    All changes
    +
    + + + {(diff) => ( + + + +
    +
    + +
    + + {getDirectory(diff.file)}‎ + + {getFilename(diff.file)} +
    +
    +
    + + +
    +
    +
    +
    + + + +
    + )} +
    +
    +
    +
    +
    +
    {(tab) => { const [file] = createResource( diff --git a/packages/ui/src/components/accordion.tsx b/packages/ui/src/components/accordion.tsx index 535d38e3d..02f00b7be 100644 --- a/packages/ui/src/components/accordion.tsx +++ b/packages/ui/src/components/accordion.tsx @@ -1,9 +1,11 @@ import { Accordion as Kobalte } from "@kobalte/core/accordion" -import { splitProps } from "solid-js" +import { createSignal, splitProps } from "solid-js" import type { ComponentProps, ParentProps } from "solid-js" export interface AccordionProps extends ComponentProps {} -export interface AccordionItemProps extends ComponentProps {} +export interface AccordionItemProps extends ComponentProps { + defaultOpen?: boolean +} export interface AccordionHeaderProps extends ComponentProps {} export interface AccordionTriggerProps extends ComponentProps {} export interface AccordionContentProps extends ComponentProps {} @@ -23,11 +25,14 @@ function AccordionRoot(props: AccordionProps) { } function AccordionItem(props: AccordionItemProps) { - const [split, rest] = splitProps(props, ["class", "classList"]) + const [split, rest] = splitProps(props, ["class", "classList", "defaultOpen"]) + const [open, setOpen] = createSignal(split.defaultOpen ?? false) return ( 0 : true}> -
    +
    diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx index 082bbea90..b61a54fe1 100644 --- a/packages/ui/src/components/icon.tsx +++ b/packages/ui/src/components/icon.tsx @@ -153,7 +153,10 @@ const newIcons = { stop: ``, enter: ``, "layout-left": ``, + "layout-right": ``, "speech-bubble": ``, + "align-right": ``, + expand: ``, } export interface IconProps extends ComponentProps<"svg"> { diff --git a/packages/ui/src/components/tooltip.tsx b/packages/ui/src/components/tooltip.tsx index c3d1947d3..e3784ed8e 100644 --- a/packages/ui/src/components/tooltip.tsx +++ b/packages/ui/src/components/tooltip.tsx @@ -29,7 +29,7 @@ export function Tooltip(props: TooltipProps) { }) return ( - + {c()} -- cgit v1.2.3 From 957c43aa09a2ceea0a9544fde4d0e2d21d91863f Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Thu, 6 Nov 2025 15:34:47 -0600 Subject: fix(desktop): review tab padding --- packages/desktop/src/pages/session.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 9f72191bc..22f9c47f5 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -728,7 +728,7 @@ export default function Page() { "relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0": true, }} > -
    +
    All changes
    -- cgit v1.2.3 From e5804f64f96a3b2fbcbb4dd9ad10c6ca40a6e083 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 06:26:26 -0600 Subject: fix(desktop): layout quirks --- packages/desktop/src/pages/session.tsx | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 22f9c47f5..30b8eab91 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -358,12 +358,12 @@ export default function Page() { return (
    -
    +
    @@ -382,7 +382,7 @@ export default function Page() { role="list" classList={{ "mr-8 shrink-0 flex flex-col items-start": true, - "absolute right-full w-60 @7xl:gap-2": local.layout.review.state() !== "open", + "absolute right-full w-60 @7xl:gap-2": true, // local.layout.review.state() !== "open", "": local.layout.review.state() === "open", }} > @@ -498,7 +498,7 @@ export default function Page() {
    {/* Title */}
    @@ -645,6 +645,14 @@ export default function Page() { }}
    + +
    + { + inputRef = el + }} + /> +
    @@ -826,13 +834,6 @@ export default function Page() { -
    - { - inputRef = el - }} - /> -
    -- cgit v1.2.3 From 3a1d1a6284b66aaf385c3c97bb842a8e1d8985cb Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 12:48:12 -0600 Subject: feat(desktop): custom syntax colors --- packages/desktop/src/context/sync.tsx | 8 +- packages/desktop/src/pages/session.tsx | 3 +- packages/ui/src/components/code.tsx | 3 +- packages/ui/src/components/diff.tsx | 3937 +++------------------------- packages/ui/src/styles/tailwind/colors.css | 5 + packages/ui/src/styles/theme.css | 59 +- theme-test.java | 461 ++++ theme-test.md | 669 +++++ theme-test.tsx | 68 + 9 files changed, 1622 insertions(+), 3591 deletions(-) create mode 100644 theme-test.java create mode 100644 theme-test.md (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/context/sync.tsx b/packages/desktop/src/context/sync.tsx index c5b169a38..bc9491fd3 100644 --- a/packages/desktop/src/context/sync.tsx +++ b/packages/desktop/src/context/sync.tsx @@ -188,7 +188,7 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ async sync(sessionID: string, _isRetry = false) { const [session, messages, todo, diff] = await Promise.all([ sdk.client.session.get({ path: { id: sessionID }, throwOnError: true }), - sdk.client.session.messages({ path: { id: sessionID } }), + sdk.client.session.messages({ path: { id: sessionID }, query: { limit: 100 } }), sdk.client.session.todo({ path: { id: sessionID } }), sdk.client.session.diff({ path: { id: sessionID } }), ]) @@ -211,12 +211,6 @@ export const { use: useSync, provider: SyncProvider } = createSimpleContext({ draft.session_diff[sessionID] = diff.data ?? [] }), ) - - // If no messages and this might be a new session, retry after a delay - // if (!isRetry && messages.data!.length === 0) { - // setTimeout(() => this.sync(sessionID, true), 500) - // return - // } }, fetch: async (count = 10) => { setStore("limit", (x) => x + count) diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 30b8eab91..3dcc24e61 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -736,7 +736,7 @@ export default function Page() { "relative px-6 py-2 w-full flex flex-col gap-6 flex-1 min-h-0": true, }} > -
    +
    All changes
    @@ -766,6 +766,7 @@ export default function Page() { (props: CodeProps) { createEffect(() => { const instance = new File({ - theme: { dark: "oc-1-dark", light: "oc-1-light" }, // or any Shiki theme + theme: { dark: "oc-1-dark", light: "oc-1-light" }, + // theme: { dark: "pierre-dark", light: "pierre-light" }, overflow: "wrap", // or 'scroll' themeType: "system", // 'system', 'light', or 'dark' disableFileHeader: true, diff --git a/packages/ui/src/components/diff.tsx b/packages/ui/src/components/diff.tsx index 6297a6422..09085b44c 100644 --- a/packages/ui/src/components/diff.tsx +++ b/packages/ui/src/components/diff.tsx @@ -57,6 +57,7 @@ export function Diff(props: DiffProps) { const instance = new FileDiff({ // theme: "pierre-light", theme: { dark: "oc-1-dark", light: "oc-1-light" }, + // theme: { dark: "pierre-dark", light: "pierre-light" }, // When using the 'themes' prop, 'themeType' allows you to force 'dark' // or 'light' theme, or inherit from the OS ('system') theme. themeType: "system", @@ -180,1788 +181,385 @@ export function Diff(props: DiffProps) { ) } -registerCustomTheme("oc-1-light", () => { - return Promise.resolve({ - name: "oc-1-light", - type: "light", - colors: { - "editor.background": "transparent", - "editor.foreground": "#070707", - foreground: "#070707", - focusBorder: "#008cff", - "selection.background": "#dfe7ff", - "editor.selectionBackground": "#008cff2e", - "editor.lineHighlightBackground": "#dfe7ff8c", - "editorCursor.foreground": "#008cff", - "editorLineNumber.foreground": "#84848A", - "editorLineNumber.activeForeground": "#6C6C71", - "editorIndentGuide.background": "#eeeeef", - "editorIndentGuide.activeBackground": "#dbdbdd", - "diffEditor.insertedTextBackground": "#00cab133", - "diffEditor.deletedTextBackground": "#ff2e3f33", - "sideBar.background": "#f8f8f8", - "sideBar.foreground": "#6C6C71", - "sideBar.border": "#eeeeef", - "sideBarTitle.foreground": "#070707", - "sideBarSectionHeader.background": "#f8f8f8", - "sideBarSectionHeader.foreground": "#6C6C71", - "sideBarSectionHeader.border": "#eeeeef", - "activityBar.background": "#f8f8f8", - "activityBar.foreground": "#070707", - "activityBar.border": "#eeeeef", - "activityBar.activeBorder": "#008cff", - "activityBarBadge.background": "#008cff", - "activityBarBadge.foreground": "#ffffff", - "titleBar.activeBackground": "#f8f8f8", - "titleBar.activeForeground": "#070707", - "titleBar.inactiveBackground": "#f8f8f8", - "titleBar.inactiveForeground": "#84848A", - "titleBar.border": "#eeeeef", - "list.activeSelectionBackground": "#dfe7ffcc", - "list.activeSelectionForeground": "#070707", - "list.inactiveSelectionBackground": "#dfe7ff73", - "list.hoverBackground": "#dfe7ff59", - "list.focusOutline": "#008cff", - "tab.activeBackground": "#ffffff", - "tab.activeForeground": "#070707", - "tab.activeBorderTop": "#008cff", - "tab.inactiveBackground": "#f8f8f8", - "tab.inactiveForeground": "#84848A", - "tab.border": "#eeeeef", - "editorGroupHeader.tabsBackground": "#f8f8f8", - "editorGroupHeader.tabsBorder": "#eeeeef", - "panel.background": "#f8f8f8", - "panel.border": "#eeeeef", - "panelTitle.activeBorder": "#008cff", - "panelTitle.activeForeground": "#070707", - "panelTitle.inactiveForeground": "#84848A", - "statusBar.background": "#f8f8f8", - "statusBar.foreground": "#6C6C71", - "statusBar.border": "#eeeeef", - "statusBar.noFolderBackground": "#f8f8f8", - "statusBar.debuggingBackground": "#ffca00", - "statusBar.debuggingForeground": "#ffffff", - "statusBarItem.remoteBackground": "#f8f8f8", - "statusBarItem.remoteForeground": "#6C6C71", - "input.background": "#f2f2f3", - "input.border": "#dbdbdd", - "input.foreground": "#070707", - "input.placeholderForeground": "#8E8E95", - "dropdown.background": "#f2f2f3", - "dropdown.border": "#dbdbdd", - "dropdown.foreground": "#070707", - "button.background": "#008cff", - "button.foreground": "#ffffff", - "button.hoverBackground": "#1a98ff", - "textLink.foreground": "#008cff", - "textLink.activeForeground": "#008cff", - "gitDecoration.addedResourceForeground": "#00cab1", - "gitDecoration.conflictingResourceForeground": "#ffca00", - "gitDecoration.modifiedResourceForeground": "#008cff", - "gitDecoration.deletedResourceForeground": "#ff2e3f", - "gitDecoration.untrackedResourceForeground": "#00cab1", - "gitDecoration.ignoredResourceForeground": "#84848A", - "terminal.titleForeground": "#6C6C71", - "terminal.titleInactiveForeground": "#84848A", - "terminal.background": "#f8f8f8", - "terminal.foreground": "#6C6C71", - "terminal.ansiBlack": "#1F1F21", - "terminal.ansiRed": "#ff2e3f", - "terminal.ansiGreen": "#0dbe4e", - "terminal.ansiYellow": "#ffca00", - "terminal.ansiBlue": "#008cff", - "terminal.ansiMagenta": "#c635e4", - "terminal.ansiCyan": "#08c0ef", - "terminal.ansiWhite": "#c6c6c8", - "terminal.ansiBrightBlack": "#1F1F21", - "terminal.ansiBrightRed": "#ff2e3f", - "terminal.ansiBrightGreen": "#0dbe4e", - "terminal.ansiBrightYellow": "#ffca00", - "terminal.ansiBrightBlue": "#008cff", - "terminal.ansiBrightMagenta": "#c635e4", - "terminal.ansiBrightCyan": "#08c0ef", - "terminal.ansiBrightWhite": "#c6c6c8", +const colors = { + "editor.background": "transparent", + "editor.foreground": "var(--text-base)", + "gitDecoration.addedResourceForeground": "var(--syntax-diff-add)", + "gitDecoration.deletedResourceForeground": "var(--syntax-diff-delete)", + // "gitDecoration.conflictingResourceForeground": "#ffca00", + // "gitDecoration.modifiedResourceForeground": "#1a76d4", + // "gitDecoration.untrackedResourceForeground": "#00cab1", + // "gitDecoration.ignoredResourceForeground": "#84848A", + // "terminal.titleForeground": "#adadb1", + // "terminal.titleInactiveForeground": "#84848A", + // "terminal.background": "#141415", + // "terminal.foreground": "#adadb1", + // "terminal.ansiBlack": "#141415", + // "terminal.ansiRed": "#ff2e3f", + // "terminal.ansiGreen": "#0dbe4e", + // "terminal.ansiYellow": "#ffca00", + // "terminal.ansiBlue": "#008cff", + // "terminal.ansiMagenta": "#c635e4", + // "terminal.ansiCyan": "#08c0ef", + // "terminal.ansiWhite": "#c6c6c8", + // "terminal.ansiBrightBlack": "#141415", + // "terminal.ansiBrightRed": "#ff2e3f", + // "terminal.ansiBrightGreen": "#0dbe4e", + // "terminal.ansiBrightYellow": "#ffca00", + // "terminal.ansiBrightBlue": "#008cff", + // "terminal.ansiBrightMagenta": "#c635e4", + // "terminal.ansiBrightCyan": "#08c0ef", + // "terminal.ansiBrightWhite": "#c6c6c8", +} + +const tokenColors = [ + { + scope: ["comment", "punctuation.definition.comment", "string.comment"], + settings: { + foreground: "var(--syntax-comment)", }, - tokenColors: [ - { - scope: ["comment", "punctuation.definition.comment"], - settings: { - foreground: "#84848A", - }, - }, - { - scope: "comment markup.link", - settings: { - foreground: "#84848A", - }, - }, - { - scope: ["string", "constant.other.symbol"], - settings: { - foreground: "#199f43", - }, - }, - { - scope: ["punctuation.definition.string.begin", "punctuation.definition.string.end"], - settings: { - foreground: "#199f43", - }, - }, - { - scope: ["constant.numeric", "constant.language.boolean"], - settings: { - foreground: "#1ca1c7", - }, - }, - { - scope: "constant", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "punctuation.definition.constant", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "constant.language", - settings: { - foreground: "#1ca1c7", - }, - }, - { - scope: "variable.other.constant", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.control", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["storage", "storage.type", "storage.modifier"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "token.storage", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: [ - "keyword.operator.new", - "keyword.operator.expression.instanceof", - "keyword.operator.expression.typeof", - "keyword.operator.expression.void", - "keyword.operator.expression.delete", - "keyword.operator.expression.in", - "keyword.operator.expression.of", - "keyword.operator.expression.keyof", - ], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.operator.delete", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["variable", "identifier", "meta.definition.variable"], - settings: { - foreground: "#d47628", - }, - }, - { - scope: [ - "variable.other.readwrite", - "meta.object-literal.key", - "support.variable.property", - "support.variable.object.process", - "support.variable.object.node", - ], - settings: { - foreground: "#d47628", - }, - }, - { - scope: "variable.language", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "variable.parameter.function", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "function.parameter", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "variable.parameter", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "variable.parameter.function.language.python", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "variable.parameter.function.python", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "support.function", - "entity.name.function", - "meta.function-call", - "meta.require", - "support.function.any-method", - "variable.function", - ], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "keyword.other.special-method", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "entity.name.function", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "support.function.console", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: ["support.type", "entity.name.type", "entity.name.class", "storage.type"], - settings: { - foreground: "#c635e4", - }, - }, - { - scope: ["support.class", "entity.name.type.class"], - settings: { - foreground: "#c635e4", - }, - }, - { - scope: ["entity.name.class", "variable.other.class.js", "variable.other.class.ts"], - settings: { - foreground: "#c635e4", - }, - }, - { - scope: "entity.name.class.identifier.namespace.type", - settings: { - foreground: "#c635e4", - }, - }, - { - scope: "entity.name.type.namespace", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.other.inherited-class", - settings: { - foreground: "#c635e4", - }, - }, - { - scope: "entity.name.namespace", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword.operator", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["keyword.operator.logical", "keyword.operator.bitwise", "keyword.operator.channel"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: [ - "keyword.operator.arithmetic", - "keyword.operator.comparison", - "keyword.operator.relational", - "keyword.operator.increment", - "keyword.operator.decrement", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.assignment", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.assignment.compound", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: [ - "keyword.operator.assignment.compound.js", - "keyword.operator.assignment.compound.ts", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.ternary", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.operator.optional", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "punctuation", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.separator.delimiter", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.separator.key-value", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.terminator", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace.square", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace.round", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "function.brace", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["punctuation.definition.parameters", "punctuation.definition.typeparameters"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["punctuation.definition.block", "punctuation.definition.tag"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["meta.tag.tsx", "meta.tag.jsx", "meta.tag.js", "meta.tag.ts"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "keyword.operator.expression.import", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "keyword.operator.module", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "support.type.object.console", - settings: { - foreground: "#d47628", - }, - }, - { - scope: ["support.module.node", "support.type.object.module", "entity.name.type.module"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "support.constant.math", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "support.constant.property.math", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "support.constant.json", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "support.type.object.dom", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["support.variable.dom", "support.variable.property.dom"], - settings: { - foreground: "#d47628", - }, - }, - { - scope: "support.variable.property.process", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "meta.property.object", - settings: { - foreground: "#d47628", - }, - }, - { - scope: "variable.parameter.function.js", - settings: { - foreground: "#d47628", - }, - }, - { - scope: ["keyword.other.template.begin", "keyword.other.template.end"], - settings: { - foreground: "#199f43", - }, - }, - { - scope: ["keyword.other.substitution.begin", "keyword.other.substitution.end"], - settings: { - foreground: "#199f43", - }, - }, - { - scope: [ - "punctuation.definition.template-expression.begin", - "punctuation.definition.template-expression.end", - ], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "meta.template.expression", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.section.embedded", - settings: { - foreground: "#d47628", - }, - }, - { - scope: "variable.interpolation", - settings: { - foreground: "#d47628", - }, - }, - { - scope: ["punctuation.section.embedded.begin", "punctuation.section.embedded.end"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "punctuation.quasi.element", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: [ - "support.type.primitive.ts", - "support.type.builtin.ts", - "support.type.primitive.tsx", - "support.type.builtin.tsx", - ], - settings: { - foreground: "#c635e4", - }, - }, - { - scope: "support.type.type.flowtype", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "support.type.primitive", - settings: { - foreground: "#c635e4", - }, - }, - { - scope: "support.variable.magic.python", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "variable.parameter.function.language.special.self.python", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "punctuation.separator.period.python", - "punctuation.separator.element.python", - "punctuation.parenthesis.begin.python", - "punctuation.parenthesis.end.python", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "punctuation.definition.arguments.begin.python", - "punctuation.definition.arguments.end.python", - "punctuation.separator.arguments.python", - "punctuation.definition.list.begin.python", - "punctuation.definition.list.end.python", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.type.python", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.logical.python", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "meta.function-call.generic.python", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "constant.character.format.placeholder.other.python", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "meta.function.decorator.python", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: ["support.token.decorator.python", "meta.function.decorator.identifier.python"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "storage.modifier.lifetime.rust", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.function.std.rust", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "entity.name.lifetime.rust", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "variable.language.rust", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "keyword.operator.misc.rust", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "keyword.operator.sigil.rust", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "support.constant.core.rust", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: ["meta.function.c", "meta.function.cpp"], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: [ - "punctuation.section.block.begin.bracket.curly.cpp", - "punctuation.section.block.end.bracket.curly.cpp", - "punctuation.terminator.statement.c", - "punctuation.section.block.begin.bracket.curly.c", - "punctuation.section.block.end.bracket.curly.c", - "punctuation.section.parens.begin.bracket.round.c", - "punctuation.section.parens.end.bracket.round.c", - "punctuation.section.parameters.begin.bracket.round.c", - "punctuation.section.parameters.end.bracket.round.c", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "keyword.operator.assignment.c", - "keyword.operator.comparison.c", - "keyword.operator.c", - "keyword.operator.increment.c", - "keyword.operator.decrement.c", - "keyword.operator.bitwise.shift.c", - ], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: [ - "keyword.operator.assignment.cpp", - "keyword.operator.comparison.cpp", - "keyword.operator.cpp", - "keyword.operator.increment.cpp", - "keyword.operator.decrement.cpp", - "keyword.operator.bitwise.shift.cpp", - ], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["punctuation.separator.c", "punctuation.separator.cpp"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["support.type.posix-reserved.c", "support.type.posix-reserved.cpp"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["keyword.operator.sizeof.c", "keyword.operator.sizeof.cpp"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "variable.c", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["storage.type.annotation.java", "storage.type.object.array.java"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "source.java", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: [ - "punctuation.section.block.begin.java", - "punctuation.section.block.end.java", - "punctuation.definition.method-parameters.begin.java", - "punctuation.definition.method-parameters.end.java", - "meta.method.identifier.java", - "punctuation.section.method.begin.java", - "punctuation.section.method.end.java", - "punctuation.terminator.java", - "punctuation.section.class.begin.java", - "punctuation.section.class.end.java", - "punctuation.section.inner-class.begin.java", - "punctuation.section.inner-class.end.java", - "meta.method-call.java", - "punctuation.section.class.begin.bracket.curly.java", - "punctuation.section.class.end.bracket.curly.java", - "punctuation.section.method.begin.bracket.curly.java", - "punctuation.section.method.end.bracket.curly.java", - "punctuation.separator.period.java", - "punctuation.bracket.angle.java", - "punctuation.definition.annotation.java", - "meta.method.body.java", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.method.java", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: ["storage.modifier.import.java", "storage.type.java", "storage.type.generic.java"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword.operator.instanceof.java", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "meta.definition.variable.name.java", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "token.variable.parameter.java", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "import.storage.java", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "token.package.keyword", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "token.package", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "token.storage.type.java", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword.operator.assignment.go", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: ["keyword.operator.arithmetic.go", "keyword.operator.address.go"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "entity.name.package.go", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "support.other.namespace.use.php", - "support.other.namespace.use-as.php", - "support.other.namespace.php", - "entity.other.alias.php", - "meta.interface.php", - ], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword.operator.error-control.php", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.operator.type.php", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["punctuation.section.array.begin.php", "punctuation.section.array.end.php"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "storage.type.php", - "meta.other.type.phpdoc.php", - "keyword.other.type.php", - "keyword.other.array.phpdoc.php", - ], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "meta.function-call.php", - "meta.function-call.object.php", - "meta.function-call.static.php", - ], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: [ - "punctuation.definition.parameters.begin.bracket.round.php", - "punctuation.definition.parameters.end.bracket.round.php", - "punctuation.separator.delimiter.php", - "punctuation.section.scope.begin.php", - "punctuation.section.scope.end.php", - "punctuation.terminator.expression.php", - "punctuation.definition.arguments.begin.bracket.round.php", - "punctuation.definition.arguments.end.bracket.round.php", - "punctuation.definition.storage-type.begin.bracket.round.php", - "punctuation.definition.storage-type.end.bracket.round.php", - "punctuation.definition.array.begin.bracket.round.php", - "punctuation.definition.array.end.bracket.round.php", - "punctuation.definition.begin.bracket.round.php", - "punctuation.definition.end.bracket.round.php", - "punctuation.definition.begin.bracket.curly.php", - "punctuation.definition.end.bracket.curly.php", - "punctuation.definition.section.switch-block.end.bracket.curly.php", - "punctuation.definition.section.switch-block.start.bracket.curly.php", - "punctuation.definition.section.switch-block.begin.bracket.curly.php", - "punctuation.definition.section.switch-block.end.bracket.curly.php", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "support.constant.ext.php", - "support.constant.std.php", - "support.constant.core.php", - "support.constant.parser-token.php", - ], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: ["entity.name.goto-label.php", "support.other.php"], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: [ - "keyword.operator.logical.php", - "keyword.operator.bitwise.php", - "keyword.operator.arithmetic.php", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.regexp.php", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.operator.comparison.php", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["keyword.operator.heredoc.php", "keyword.operator.nowdoc.php"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "variable.other.class.php", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "invalid.illegal.non-null-typehinted.php", - settings: { - foreground: "#f44747", - }, - }, - { - scope: "variable.other.generic-type.haskell", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "storage.type.haskell", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "storage.type.cs", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.name.variable.local.cs", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "entity.name.label.cs", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "entity.name.scope-resolution.function.call", - "entity.name.scope-resolution.function.definition", - ], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "punctuation.definition.delayed.unison", - "punctuation.definition.list.begin.unison", - "punctuation.definition.list.end.unison", - "punctuation.definition.ability.begin.unison", - "punctuation.definition.ability.end.unison", - "punctuation.operator.assignment.as.unison", - "punctuation.separator.pipe.unison", - "punctuation.separator.delimiter.unison", - "punctuation.definition.hash.unison", - ], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "support.constant.edge", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "support.type.prelude.elm", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.constant.elm", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.global.clojure", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "meta.symbol.clojure", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "constant.keyword.clojure", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["meta.arguments.coffee", "variable.parameter.function.coffee"], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "storage.modifier.import.groovy", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "meta.method.groovy", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "meta.definition.variable.name.groovy", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "meta.definition.class.inherited.classes.groovy", - settings: { - foreground: "#199f43", - }, - }, - { - scope: "support.variable.semantic.hlsl", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "support.type.texture.hlsl", - "support.type.sampler.hlsl", - "support.type.object.hlsl", - "support.type.object.rw.hlsl", - "support.type.fx.hlsl", - "support.type.object.hlsl", - ], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["text.variable", "text.bracketed"], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: ["support.type.swift", "support.type.vb.asp"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "meta.scope.prerequisites.makefile", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "source.makefile", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "source.ini", - settings: { - foreground: "#199f43", - }, - }, - { - scope: "constant.language.symbol.ruby", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["function.parameter.ruby", "function.parameter.cs"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "constant.language.symbol.elixir", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: - "text.html.laravel-blade source.php.embedded.line.html entity.name.tag.laravel-blade", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: - "text.html.laravel-blade source.php.embedded.line.html support.constant.laravel-blade", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "entity.name.function.xi", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.name.class.xi", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "constant.character.character-class.regexp.xi", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "constant.regexp.xi", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "keyword.control.xi", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "invalid.xi", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "beginning.punctuation.definition.quote.markdown.xi", - settings: { - foreground: "#199f43", - }, - }, - { - scope: "beginning.punctuation.definition.list.markdown.xi", - settings: { - foreground: "#84848A", - }, - }, - { - scope: "constant.character.xi", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "accent.xi", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "wikiword.xi", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "constant.other.color.rgb-value.xi", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "punctuation.definition.tag.xi", - settings: { - foreground: "#84848A", - }, - }, - { - scope: ["support.constant.property-value.scss", "support.constant.property-value.css"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: ["keyword.operator.css", "keyword.operator.scss", "keyword.operator.less"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: [ - "support.constant.color.w3c-standard-color-name.css", - "support.constant.color.w3c-standard-color-name.scss", - ], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "punctuation.separator.list.comma.css", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.type.vendored.property-name.css", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name.css", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.constant.property-value", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.constant.font-name", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.other.attribute-name.class.css", - settings: { - foreground: "#16a994", - fontStyle: "normal", - }, - }, - { - scope: "entity.other.attribute-name.id", - settings: { - foreground: "#7b43f8", - fontStyle: "normal", - }, - }, - { - scope: [ - "entity.other.attribute-name.pseudo-element", - "entity.other.attribute-name.pseudo-class", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "meta.selector", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "selector.sass", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "rgb-value", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "inline-color-decoration rgb-value", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "less rgb-value", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "control.elements", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "keyword.operator.less", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "entity.name.tag", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "entity.other.attribute-name", - settings: { - foreground: "#16a994", - fontStyle: "normal", - }, - }, - { - scope: "constant.character.entity", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "meta.tag", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "invalid.illegal.bad-ampersand.html", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "markup.heading", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: ["markup.heading punctuation.definition.heading", "entity.name.section"], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "entity.name.section.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "punctuation.definition.heading.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "markup.heading.setext", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["markup.heading.setext.1.markdown", "markup.heading.setext.2.markdown"], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: ["markup.bold", "todo.bold"], - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "punctuation.definition.bold", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "punctuation.definition.bold.markdown", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: ["markup.italic", "punctuation.definition.italic", "todo.emphasis"], - settings: { - foreground: "#fc2b73", - fontStyle: "italic", - }, - }, - { - scope: "emphasis md", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "markup.italic.markdown", - settings: { - fontStyle: "italic", - }, - }, - { - scope: ["markup.underline.link.markdown", "markup.underline.link.image.markdown"], - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: ["string.other.link.title.markdown", "string.other.link.description.markdown"], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "punctuation.definition.metadata.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: ["markup.inline.raw.markdown", "markup.inline.raw.string.markdown"], - settings: { - foreground: "#199f43", - }, - }, - { - scope: "punctuation.definition.list.begin.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "punctuation.definition.list.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "beginning.punctuation.definition.list.markdown", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: [ - "punctuation.definition.string.begin.markdown", - "punctuation.definition.string.end.markdown", - ], - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "markup.quote.markdown", - settings: { - foreground: "#84848A", - }, - }, - { - scope: "keyword.other.unit", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "markup.changed.diff", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: [ - "meta.diff.header.from-file", - "meta.diff.header.to-file", - "punctuation.definition.from-file.diff", - "punctuation.definition.to-file.diff", - ], - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "markup.inserted.diff", - settings: { - foreground: "#199f43", - }, - }, - { - scope: "markup.deleted.diff", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "string.regexp", - settings: { - foreground: "#17a5af", - }, - }, - { - scope: "constant.other.character-class.regexp", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "keyword.operator.quantifier.regexp", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "constant.character.escape", - settings: { - foreground: "#1ca1c7", - }, - }, - { - scope: "source.json meta.structure.dictionary.json > string.quoted.json", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: - "source.json meta.structure.dictionary.json > string.quoted.json > punctuation.string", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: [ - "source.json meta.structure.dictionary.json > value.json > string.quoted.json", - "source.json meta.structure.array.json > value.json > string.quoted.json", - "source.json meta.structure.dictionary.json > value.json > string.quoted.json > punctuation", - "source.json meta.structure.array.json > value.json > string.quoted.json > punctuation", - ], - settings: { - foreground: "#199f43", - }, - }, - { - scope: [ - "source.json meta.structure.dictionary.json > constant.language.json", - "source.json meta.structure.array.json > constant.language.json", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name.json", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "support.type.property-name.json punctuation", - settings: { - foreground: "#d52c36", - }, - }, - { - scope: "punctuation.definition.block.sequence.item.yaml", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "block.scope.end", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "block.scope.begin", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "token.info-token", - settings: { - foreground: "#7b43f8", - }, - }, - { - scope: "token.warn-token", - settings: { - foreground: "#d5a910", - }, - }, - { - scope: "token.error-token", - settings: { - foreground: "#f44747", - }, - }, - { - scope: "token.debug-token", - settings: { - foreground: "#fc2b73", - }, - }, - { - scope: "invalid.illegal", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.broken", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.deprecated", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.unimplemented", - settings: { - foreground: "#ffffff", - }, - }, + }, + { + scope: ["entity.other.attribute-name"], + settings: { + foreground: "var(--syntax-property)", // maybe attribute + }, + }, + { + scope: [ + "constant", + "entity.name.constant", + "variable.other.constant", + "variable.language", + "entity", + ], + settings: { + foreground: "var(--syntax-constant)", + }, + }, + { + scope: ["entity.name", "meta.export.default", "meta.definition.variable"], + settings: { + foreground: "var(--syntax-type)", + }, + }, + { + scope: [ + "variable.parameter.function", + "meta.jsx.children", + "meta.block", + "meta.tag.attributes", + "entity.name.constant", + "meta.object.member", + "meta.embedded.expression", + "meta.template.expression", + "string.other.begin.yaml", + "string.other.end.yaml", + ], + settings: { + foreground: "var(--syntax-punctuation)", + }, + }, + { + scope: ["entity.name.function", "support.type.primitive"], + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: ["support.class.component"], + settings: { + foreground: "var(--syntax-type)", + }, + }, + { + scope: "keyword", + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: [ + "keyword.operator", + "storage.type.function.arrow", + "punctuation.separator.key-value.css", + "entity.name.tag.yaml", + "punctuation.separator.key-value.mapping.yaml", ], - semanticTokenColors: { - comment: "#84848A", - string: "#199f43", - number: "#1ca1c7", - regexp: "#17a5af", - keyword: "#fc2b73", - variable: "#d47628", - parameter: "#79797F", - property: "#d47628", - function: "#7b43f8", - method: "#7b43f8", - type: "#c635e4", - class: "#c635e4", - namespace: "#d5a910", - enumMember: "#08c0ef", - "variable.constant": "#d5a910", - "variable.defaultLibrary": "#d5a910", + settings: { + foreground: "var(--syntax-operator)", + }, + }, + { + scope: ["storage", "storage.type"], + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: ["storage.modifier.package", "storage.modifier.import", "storage.type.java"], + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: [ + "string", + "punctuation.definition.string", + "string punctuation.section.embedded source", + "entity.name.tag", + ], + settings: { + foreground: "var(--syntax-string)", + }, + }, + { + scope: "support", + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: [ + "support.type.object.module", + "variable.other.object", + "support.type.property-name.css", + ], + settings: { + foreground: "var(--syntax-object)", + }, + }, + { + scope: "meta.property-name", + settings: { + foreground: "var(--syntax-property)", + }, + }, + { + scope: "variable", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "variable.other", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: [ + "invalid.broken", + "invalid.illegal", + "invalid.unimplemented", + "invalid.deprecated", + "message.error", + "markup.deleted", + "meta.diff.header.from-file", + "punctuation.definition.deleted", + "brackethighlighter.unmatched", + "token.error-token", + ], + settings: { + foreground: "var(--syntax-critical)", + }, + }, + { + scope: "carriage-return", + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: "string source", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "string variable", + settings: { + foreground: "var(--syntax-constant)", + }, + }, + { + scope: [ + "source.regexp", + "string.regexp", + "string.regexp.character-class", + "string.regexp constant.character.escape", + "string.regexp source.ruby.embedded", + "string.regexp string.regexp.arbitrary-repitition", + "string.regexp constant.character.escape", + ], + settings: { + foreground: "var(--syntax-regexp)", + }, + }, + { + scope: "support.constant", + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: "support.variable", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "meta.module-reference", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "punctuation.definition.list.begin.markdown", + settings: { + foreground: "var(--syntax-punctuation)", + }, + }, + { + scope: ["markup.heading", "markup.heading entity.name"], + settings: { + fontStyle: "bold", + foreground: "var(--syntax-info)", + }, + }, + { + scope: "markup.quote", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "markup.italic", + settings: { + fontStyle: "italic", + // foreground: "", + }, + }, + { + scope: "markup.bold", + settings: { + fontStyle: "bold", + foreground: "var(--text-strong)", + }, + }, + { + scope: [ + "markup.raw", + "markup.inserted", + "meta.diff.header.to-file", + "punctuation.definition.inserted", + "markup.changed", + "punctuation.definition.changed", + "markup.ignored", + "markup.untracked", + ], + settings: { + foreground: "var(--text-base)", + }, + }, + { + scope: "meta.diff.range", + settings: { + fontStyle: "bold", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.diff.header", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.separator", + settings: { + fontStyle: "bold", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.output", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.export.default", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: [ + "brackethighlighter.tag", + "brackethighlighter.curly", + "brackethighlighter.round", + "brackethighlighter.square", + "brackethighlighter.angle", + "brackethighlighter.quote", + ], + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: ["constant.other.reference.link", "string.other.link"], + settings: { + fontStyle: "underline", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "token.info-token", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "token.warn-token", + settings: { + foreground: "var(--syntax-warning)", }, + }, + { + scope: "token.debug-token", + settings: { + foreground: "var(--syntax-info)", + }, + }, +] + +const semanticTokenColors = { + comment: "var(--syntax-comment)", + string: "var(--syntax-string)", + number: "var(--syntax-constant)", + regexp: "var(--syntax-regexp)", + keyword: "var(--syntax-keyword)", + variable: "var(--syntax-variable)", + parameter: "var(--syntax-variable)", + property: "var(--syntax-property)", + function: "var(--syntax-primitive)", + method: "var(--syntax-primitive)", + type: "var(--syntax-type)", + class: "var(--syntax-type)", + namespace: "var(--syntax-type)", + enumMember: "var(--syntax-primitive)", + "variable.constant": "var(--syntax-constant)", + "variable.defaultLibrary": "var(--syntax-unknown)", +} + +registerCustomTheme("oc-1-light", () => { + return Promise.resolve({ + type: "light", + name: "oc-1-light", + colors, + tokenColors, + semanticTokenColors, } as unknown as ThemeRegistrationResolved) }) @@ -1969,1783 +567,8 @@ registerCustomTheme("oc-1-dark", () => { return Promise.resolve({ name: "oc-1-dark", type: "dark", - colors: { - "editor.background": "transparent", - "editor.foreground": "#fbfbfb", - foreground: "#fbfbfb", - focusBorder: "#1a76d4", - "selection.background": "#19253c", - "editor.selectionBackground": "#1a76d44d", - "editor.lineHighlightBackground": "#19253c8c", - "editorCursor.foreground": "#1a76d4", - "editorLineNumber.foreground": "#84848A", - "editorLineNumber.activeForeground": "#adadb1", - "editorIndentGuide.background": "#39393c", - "editorIndentGuide.activeBackground": "#2e2e30", - "diffEditor.insertedTextBackground": "#00cab11a", - "diffEditor.deletedTextBackground": "#ff2e3f1a", - "sideBar.background": "#141415", - "sideBar.foreground": "#adadb1", - "sideBar.border": "#070707", - "sideBarTitle.foreground": "#fbfbfb", - "sideBarSectionHeader.background": "#141415", - "sideBarSectionHeader.foreground": "#adadb1", - "sideBarSectionHeader.border": "#070707", - "activityBar.background": "#141415", - "activityBar.foreground": "#fbfbfb", - "activityBar.border": "#070707", - "activityBar.activeBorder": "#1a76d4", - "activityBarBadge.background": "#1a76d4", - "activityBarBadge.foreground": "#070707", - "titleBar.activeBackground": "#141415", - "titleBar.activeForeground": "#fbfbfb", - "titleBar.inactiveBackground": "#141415", - "titleBar.inactiveForeground": "#84848A", - "titleBar.border": "#070707", - "list.activeSelectionBackground": "#19253c99", - "list.activeSelectionForeground": "#fbfbfb", - "list.inactiveSelectionBackground": "#19253c73", - "list.hoverBackground": "#19253c59", - "list.focusOutline": "#1a76d4", - "tab.activeBackground": "#070707", - "tab.activeForeground": "#fbfbfb", - "tab.activeBorderTop": "#1a76d4", - "tab.inactiveBackground": "#141415", - "tab.inactiveForeground": "#84848A", - "tab.border": "#070707", - "editorGroupHeader.tabsBackground": "#141415", - "editorGroupHeader.tabsBorder": "#070707", - "panel.background": "#141415", - "panel.border": "#070707", - "panelTitle.activeBorder": "#1a76d4", - "panelTitle.activeForeground": "#fbfbfb", - "panelTitle.inactiveForeground": "#84848A", - "statusBar.background": "#141415", - "statusBar.foreground": "#adadb1", - "statusBar.border": "#070707", - "statusBar.noFolderBackground": "#141415", - "statusBar.debuggingBackground": "#ffca00", - "statusBar.debuggingForeground": "#070707", - "statusBarItem.remoteBackground": "#141415", - "statusBarItem.remoteForeground": "#adadb1", - "input.background": "#1F1F21", - "input.border": "#424245", - "input.foreground": "#fbfbfb", - "input.placeholderForeground": "#79797F", - "dropdown.background": "#1F1F21", - "dropdown.border": "#424245", - "dropdown.foreground": "#fbfbfb", - "button.background": "#1a76d4", - "button.foreground": "#070707", - "button.hoverBackground": "#186bc0", - "textLink.foreground": "#1a76d4", - "textLink.activeForeground": "#1a76d4", - "gitDecoration.addedResourceForeground": "#00cab1", - "gitDecoration.conflictingResourceForeground": "#ffca00", - "gitDecoration.modifiedResourceForeground": "#1a76d4", - "gitDecoration.deletedResourceForeground": "#ff2e3f", - "gitDecoration.untrackedResourceForeground": "#00cab1", - "gitDecoration.ignoredResourceForeground": "#84848A", - "terminal.titleForeground": "#adadb1", - "terminal.titleInactiveForeground": "#84848A", - "terminal.background": "#141415", - "terminal.foreground": "#adadb1", - "terminal.ansiBlack": "#141415", - "terminal.ansiRed": "#ff2e3f", - "terminal.ansiGreen": "#0dbe4e", - "terminal.ansiYellow": "#ffca00", - "terminal.ansiBlue": "#008cff", - "terminal.ansiMagenta": "#c635e4", - "terminal.ansiCyan": "#08c0ef", - "terminal.ansiWhite": "#c6c6c8", - "terminal.ansiBrightBlack": "#141415", - "terminal.ansiBrightRed": "#ff2e3f", - "terminal.ansiBrightGreen": "#0dbe4e", - "terminal.ansiBrightYellow": "#ffca00", - "terminal.ansiBrightBlue": "#008cff", - "terminal.ansiBrightMagenta": "#c635e4", - "terminal.ansiBrightCyan": "#08c0ef", - "terminal.ansiBrightWhite": "#c6c6c8", - }, - tokenColors: [ - { - scope: ["comment", "punctuation.definition.comment"], - settings: { - foreground: "#84848A", - }, - }, - { - scope: "comment markup.link", - settings: { - foreground: "#84848A", - }, - }, - { - scope: ["string", "constant.other.symbol"], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: ["punctuation.definition.string.begin", "punctuation.definition.string.end"], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: ["constant.numeric", "constant.language.boolean"], - settings: { - foreground: "#68cdf2", - }, - }, - { - scope: "constant", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "punctuation.definition.constant", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "constant.language", - settings: { - foreground: "#68cdf2", - }, - }, - { - scope: "variable.other.constant", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "keyword", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.control", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["storage", "storage.type", "storage.modifier"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "token.storage", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: [ - "keyword.operator.new", - "keyword.operator.expression.instanceof", - "keyword.operator.expression.typeof", - "keyword.operator.expression.void", - "keyword.operator.expression.delete", - "keyword.operator.expression.in", - "keyword.operator.expression.of", - "keyword.operator.expression.keyof", - ], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.operator.delete", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["variable", "identifier", "meta.definition.variable"], - settings: { - foreground: "#ffa359", - }, - }, - { - scope: [ - "variable.other.readwrite", - "meta.object-literal.key", - "support.variable.property", - "support.variable.object.process", - "support.variable.object.node", - ], - settings: { - foreground: "#ffa359", - }, - }, - { - scope: "variable.language", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "variable.parameter.function", - settings: { - foreground: "#adadb1", - }, - }, - { - scope: "function.parameter", - settings: { - foreground: "#adadb1", - }, - }, - { - scope: "variable.parameter", - settings: { - foreground: "#adadb1", - }, - }, - { - scope: "variable.parameter.function.language.python", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "variable.parameter.function.python", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: [ - "support.function", - "entity.name.function", - "meta.function-call", - "meta.require", - "support.function.any-method", - "variable.function", - ], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "keyword.other.special-method", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "entity.name.function", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "support.function.console", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: ["support.type", "entity.name.type", "entity.name.class", "storage.type"], - settings: { - foreground: "#d568ea", - }, - }, - { - scope: ["support.class", "entity.name.type.class"], - settings: { - foreground: "#d568ea", - }, - }, - { - scope: ["entity.name.class", "variable.other.class.js", "variable.other.class.ts"], - settings: { - foreground: "#d568ea", - }, - }, - { - scope: "entity.name.class.identifier.namespace.type", - settings: { - foreground: "#d568ea", - }, - }, - { - scope: "entity.name.type.namespace", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "entity.other.inherited-class", - settings: { - foreground: "#d568ea", - }, - }, - { - scope: "entity.name.namespace", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "keyword.operator", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["keyword.operator.logical", "keyword.operator.bitwise", "keyword.operator.channel"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: [ - "keyword.operator.arithmetic", - "keyword.operator.comparison", - "keyword.operator.relational", - "keyword.operator.increment", - "keyword.operator.decrement", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.assignment", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.assignment.compound", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: [ - "keyword.operator.assignment.compound.js", - "keyword.operator.assignment.compound.ts", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.ternary", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.operator.optional", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "punctuation", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.separator.delimiter", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.separator.key-value", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.terminator", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace.square", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.brace.round", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "function.brace", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["punctuation.definition.parameters", "punctuation.definition.typeparameters"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["punctuation.definition.block", "punctuation.definition.tag"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["meta.tag.tsx", "meta.tag.jsx", "meta.tag.js", "meta.tag.ts"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "keyword.operator.expression.import", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "keyword.operator.module", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "support.type.object.console", - settings: { - foreground: "#ffa359", - }, - }, - { - scope: ["support.module.node", "support.type.object.module", "entity.name.type.module"], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "support.constant.math", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "support.constant.property.math", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "support.constant.json", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "support.type.object.dom", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["support.variable.dom", "support.variable.property.dom"], - settings: { - foreground: "#ffa359", - }, - }, - { - scope: "support.variable.property.process", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "meta.property.object", - settings: { - foreground: "#ffa359", - }, - }, - { - scope: "variable.parameter.function.js", - settings: { - foreground: "#ffa359", - }, - }, - { - scope: ["keyword.other.template.begin", "keyword.other.template.end"], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: ["keyword.other.substitution.begin", "keyword.other.substitution.end"], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: [ - "punctuation.definition.template-expression.begin", - "punctuation.definition.template-expression.end", - ], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "meta.template.expression", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "punctuation.section.embedded", - settings: { - foreground: "#ffa359", - }, - }, - { - scope: "variable.interpolation", - settings: { - foreground: "#ffa359", - }, - }, - { - scope: ["punctuation.section.embedded.begin", "punctuation.section.embedded.end"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "punctuation.quasi.element", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: [ - "support.type.primitive.ts", - "support.type.builtin.ts", - "support.type.primitive.tsx", - "support.type.builtin.tsx", - ], - settings: { - foreground: "#d568ea", - }, - }, - { - scope: "support.type.type.flowtype", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "support.type.primitive", - settings: { - foreground: "#d568ea", - }, - }, - { - scope: "support.variable.magic.python", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "variable.parameter.function.language.special.self.python", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "punctuation.separator.period.python", - "punctuation.separator.element.python", - "punctuation.parenthesis.begin.python", - "punctuation.parenthesis.end.python", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "punctuation.definition.arguments.begin.python", - "punctuation.definition.arguments.end.python", - "punctuation.separator.arguments.python", - "punctuation.definition.list.begin.python", - "punctuation.definition.list.end.python", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.type.python", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.logical.python", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "meta.function-call.generic.python", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "constant.character.format.placeholder.other.python", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "meta.function.decorator.python", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: ["support.token.decorator.python", "meta.function.decorator.identifier.python"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "storage.modifier.lifetime.rust", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.function.std.rust", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "entity.name.lifetime.rust", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "variable.language.rust", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "keyword.operator.misc.rust", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "keyword.operator.sigil.rust", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "support.constant.core.rust", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: ["meta.function.c", "meta.function.cpp"], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: [ - "punctuation.section.block.begin.bracket.curly.cpp", - "punctuation.section.block.end.bracket.curly.cpp", - "punctuation.terminator.statement.c", - "punctuation.section.block.begin.bracket.curly.c", - "punctuation.section.block.end.bracket.curly.c", - "punctuation.section.parens.begin.bracket.round.c", - "punctuation.section.parens.end.bracket.round.c", - "punctuation.section.parameters.begin.bracket.round.c", - "punctuation.section.parameters.end.bracket.round.c", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "keyword.operator.assignment.c", - "keyword.operator.comparison.c", - "keyword.operator.c", - "keyword.operator.increment.c", - "keyword.operator.decrement.c", - "keyword.operator.bitwise.shift.c", - ], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: [ - "keyword.operator.assignment.cpp", - "keyword.operator.comparison.cpp", - "keyword.operator.cpp", - "keyword.operator.increment.cpp", - "keyword.operator.decrement.cpp", - "keyword.operator.bitwise.shift.cpp", - ], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["punctuation.separator.c", "punctuation.separator.cpp"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["support.type.posix-reserved.c", "support.type.posix-reserved.cpp"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["keyword.operator.sizeof.c", "keyword.operator.sizeof.cpp"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "variable.c", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["storage.type.annotation.java", "storage.type.object.array.java"], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "source.java", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: [ - "punctuation.section.block.begin.java", - "punctuation.section.block.end.java", - "punctuation.definition.method-parameters.begin.java", - "punctuation.definition.method-parameters.end.java", - "meta.method.identifier.java", - "punctuation.section.method.begin.java", - "punctuation.section.method.end.java", - "punctuation.terminator.java", - "punctuation.section.class.begin.java", - "punctuation.section.class.end.java", - "punctuation.section.inner-class.begin.java", - "punctuation.section.inner-class.end.java", - "meta.method-call.java", - "punctuation.section.class.begin.bracket.curly.java", - "punctuation.section.class.end.bracket.curly.java", - "punctuation.section.method.begin.bracket.curly.java", - "punctuation.section.method.end.bracket.curly.java", - "punctuation.separator.period.java", - "punctuation.bracket.angle.java", - "punctuation.definition.annotation.java", - "meta.method.body.java", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "meta.method.java", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: ["storage.modifier.import.java", "storage.type.java", "storage.type.generic.java"], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "keyword.operator.instanceof.java", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "meta.definition.variable.name.java", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "token.variable.parameter.java", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "import.storage.java", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "token.package.keyword", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "token.package", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "token.storage.type.java", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "keyword.operator.assignment.go", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: ["keyword.operator.arithmetic.go", "keyword.operator.address.go"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "entity.name.package.go", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "support.other.namespace.use.php", - "support.other.namespace.use-as.php", - "support.other.namespace.php", - "entity.other.alias.php", - "meta.interface.php", - ], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "keyword.operator.error-control.php", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.operator.type.php", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["punctuation.section.array.begin.php", "punctuation.section.array.end.php"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "storage.type.php", - "meta.other.type.phpdoc.php", - "keyword.other.type.php", - "keyword.other.array.phpdoc.php", - ], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "meta.function-call.php", - "meta.function-call.object.php", - "meta.function-call.static.php", - ], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: [ - "punctuation.definition.parameters.begin.bracket.round.php", - "punctuation.definition.parameters.end.bracket.round.php", - "punctuation.separator.delimiter.php", - "punctuation.section.scope.begin.php", - "punctuation.section.scope.end.php", - "punctuation.terminator.expression.php", - "punctuation.definition.arguments.begin.bracket.round.php", - "punctuation.definition.arguments.end.bracket.round.php", - "punctuation.definition.storage-type.begin.bracket.round.php", - "punctuation.definition.storage-type.end.bracket.round.php", - "punctuation.definition.array.begin.bracket.round.php", - "punctuation.definition.array.end.bracket.round.php", - "punctuation.definition.begin.bracket.round.php", - "punctuation.definition.end.bracket.round.php", - "punctuation.definition.begin.bracket.curly.php", - "punctuation.definition.end.bracket.curly.php", - "punctuation.definition.section.switch-block.end.bracket.curly.php", - "punctuation.definition.section.switch-block.start.bracket.curly.php", - "punctuation.definition.section.switch-block.begin.bracket.curly.php", - "punctuation.definition.section.switch-block.end.bracket.curly.php", - ], - settings: { - foreground: "#79797F", - }, - }, - { - scope: [ - "support.constant.ext.php", - "support.constant.std.php", - "support.constant.core.php", - "support.constant.parser-token.php", - ], - settings: { - foreground: "#ffd452", - }, - }, - { - scope: ["entity.name.goto-label.php", "support.other.php"], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: [ - "keyword.operator.logical.php", - "keyword.operator.bitwise.php", - "keyword.operator.arithmetic.php", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "keyword.operator.regexp.php", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.operator.comparison.php", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["keyword.operator.heredoc.php", "keyword.operator.nowdoc.php"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "variable.other.class.php", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "invalid.illegal.non-null-typehinted.php", - settings: { - foreground: "#f44747", - }, - }, - { - scope: "variable.other.generic-type.haskell", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "storage.type.haskell", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "storage.type.cs", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "entity.name.variable.local.cs", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "entity.name.label.cs", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "entity.name.scope-resolution.function.call", - "entity.name.scope-resolution.function.definition", - ], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "punctuation.definition.delayed.unison", - "punctuation.definition.list.begin.unison", - "punctuation.definition.list.end.unison", - "punctuation.definition.ability.begin.unison", - "punctuation.definition.ability.end.unison", - "punctuation.operator.assignment.as.unison", - "punctuation.separator.pipe.unison", - "punctuation.separator.delimiter.unison", - "punctuation.definition.hash.unison", - ], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "support.constant.edge", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "support.type.prelude.elm", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.constant.elm", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "entity.global.clojure", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "meta.symbol.clojure", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "constant.keyword.clojure", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["meta.arguments.coffee", "variable.parameter.function.coffee"], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "storage.modifier.import.groovy", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "meta.method.groovy", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "meta.definition.variable.name.groovy", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "meta.definition.class.inherited.classes.groovy", - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: "support.variable.semantic.hlsl", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "support.type.texture.hlsl", - "support.type.sampler.hlsl", - "support.type.object.hlsl", - "support.type.object.rw.hlsl", - "support.type.fx.hlsl", - "support.type.object.hlsl", - ], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["text.variable", "text.bracketed"], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: ["support.type.swift", "support.type.vb.asp"], - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "meta.scope.prerequisites.makefile", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "source.makefile", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "source.ini", - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: "constant.language.symbol.ruby", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: ["function.parameter.ruby", "function.parameter.cs"], - settings: { - foreground: "#79797F", - }, - }, - { - scope: "constant.language.symbol.elixir", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: - "text.html.laravel-blade source.php.embedded.line.html entity.name.tag.laravel-blade", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: - "text.html.laravel-blade source.php.embedded.line.html support.constant.laravel-blade", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "entity.name.function.xi", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "entity.name.class.xi", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "constant.character.character-class.regexp.xi", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "constant.regexp.xi", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "keyword.control.xi", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "invalid.xi", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "beginning.punctuation.definition.quote.markdown.xi", - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: "beginning.punctuation.definition.list.markdown.xi", - settings: { - foreground: "#84848A", - }, - }, - { - scope: "constant.character.xi", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "accent.xi", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "wikiword.xi", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "constant.other.color.rgb-value.xi", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "punctuation.definition.tag.xi", - settings: { - foreground: "#84848A", - }, - }, - { - scope: ["support.constant.property-value.scss", "support.constant.property-value.css"], - settings: { - foreground: "#ffd452", - }, - }, - { - scope: ["keyword.operator.css", "keyword.operator.scss", "keyword.operator.less"], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: [ - "support.constant.color.w3c-standard-color-name.css", - "support.constant.color.w3c-standard-color-name.scss", - ], - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "punctuation.separator.list.comma.css", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.type.vendored.property-name.css", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name.css", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.constant.property-value", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "support.constant.font-name", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "entity.other.attribute-name.class.css", - settings: { - foreground: "#61d5c0", - fontStyle: "normal", - }, - }, - { - scope: "entity.other.attribute-name.id", - settings: { - foreground: "#9d6afb", - fontStyle: "normal", - }, - }, - { - scope: [ - "entity.other.attribute-name.pseudo-element", - "entity.other.attribute-name.pseudo-class", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "meta.selector", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "selector.sass", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "rgb-value", - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "inline-color-decoration rgb-value", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "less rgb-value", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "control.elements", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "keyword.operator.less", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "entity.name.tag", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "entity.other.attribute-name", - settings: { - foreground: "#61d5c0", - fontStyle: "normal", - }, - }, - { - scope: "constant.character.entity", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "meta.tag", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "invalid.illegal.bad-ampersand.html", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "markup.heading", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: ["markup.heading punctuation.definition.heading", "entity.name.section"], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "entity.name.section.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "punctuation.definition.heading.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "markup.heading.setext", - settings: { - foreground: "#79797F", - }, - }, - { - scope: ["markup.heading.setext.1.markdown", "markup.heading.setext.2.markdown"], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: ["markup.bold", "todo.bold"], - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "punctuation.definition.bold", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: "punctuation.definition.bold.markdown", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: ["markup.italic", "punctuation.definition.italic", "todo.emphasis"], - settings: { - foreground: "#ff678d", - fontStyle: "italic", - }, - }, - { - scope: "emphasis md", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "markup.italic.markdown", - settings: { - fontStyle: "italic", - }, - }, - { - scope: ["markup.underline.link.markdown", "markup.underline.link.image.markdown"], - settings: { - foreground: "#ff678d", - }, - }, - { - scope: ["string.other.link.title.markdown", "string.other.link.description.markdown"], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "punctuation.definition.metadata.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: ["markup.inline.raw.markdown", "markup.inline.raw.string.markdown"], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: "punctuation.definition.list.begin.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "punctuation.definition.list.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "beginning.punctuation.definition.list.markdown", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: [ - "punctuation.definition.string.begin.markdown", - "punctuation.definition.string.end.markdown", - ], - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "markup.quote.markdown", - settings: { - foreground: "#84848A", - }, - }, - { - scope: "keyword.other.unit", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "markup.changed.diff", - settings: { - foreground: "#ffca00", - }, - }, - { - scope: [ - "meta.diff.header.from-file", - "meta.diff.header.to-file", - "punctuation.definition.from-file.diff", - "punctuation.definition.to-file.diff", - ], - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "markup.inserted.diff", - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: "markup.deleted.diff", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "string.regexp", - settings: { - foreground: "#64d1db", - }, - }, - { - scope: "constant.other.character-class.regexp", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "keyword.operator.quantifier.regexp", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "constant.character.escape", - settings: { - foreground: "#68cdf2", - }, - }, - { - scope: "source.json meta.structure.dictionary.json > string.quoted.json", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: - "source.json meta.structure.dictionary.json > string.quoted.json > punctuation.string", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: [ - "source.json meta.structure.dictionary.json > value.json > string.quoted.json", - "source.json meta.structure.array.json > value.json > string.quoted.json", - "source.json meta.structure.dictionary.json > value.json > string.quoted.json > punctuation", - "source.json meta.structure.array.json > value.json > string.quoted.json > punctuation", - ], - settings: { - foreground: "#5ecc71", - }, - }, - { - scope: [ - "source.json meta.structure.dictionary.json > constant.language.json", - "source.json meta.structure.array.json > constant.language.json", - ], - settings: { - foreground: "#08c0ef", - }, - }, - { - scope: "support.type.property-name.json", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "support.type.property-name.json punctuation", - settings: { - foreground: "#ff6762", - }, - }, - { - scope: "punctuation.definition.block.sequence.item.yaml", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "block.scope.end", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "block.scope.begin", - settings: { - foreground: "#79797F", - }, - }, - { - scope: "token.info-token", - settings: { - foreground: "#9d6afb", - }, - }, - { - scope: "token.warn-token", - settings: { - foreground: "#ffd452", - }, - }, - { - scope: "token.error-token", - settings: { - foreground: "#f44747", - }, - }, - { - scope: "token.debug-token", - settings: { - foreground: "#ff678d", - }, - }, - { - scope: "invalid.illegal", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.broken", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.deprecated", - settings: { - foreground: "#ffffff", - }, - }, - { - scope: "invalid.unimplemented", - settings: { - foreground: "#ffffff", - }, - }, - ], - semanticTokenColors: { - comment: "#84848A", - string: "#5ecc71", - number: "#68cdf2", - regexp: "#64d1db", - keyword: "#ff678d", - variable: "#ffa359", - parameter: "#adadb1", - property: "#ffa359", - function: "#9d6afb", - method: "#9d6afb", - type: "#d568ea", - class: "#d568ea", - namespace: "#ffca00", - enumMember: "#08c0ef", - "variable.constant": "#ffd452", - "variable.defaultLibrary": "#ffca00", - }, + colors, + tokenColors, + semanticTokenColors, } as unknown as ThemeRegistrationResolved) }) diff --git a/packages/ui/src/styles/tailwind/colors.css b/packages/ui/src/styles/tailwind/colors.css index a5f982d6f..95a497ba9 100644 --- a/packages/ui/src/styles/tailwind/colors.css +++ b/packages/ui/src/styles/tailwind/colors.css @@ -196,15 +196,20 @@ --color-icon-diff-delete-base: var(--icon-diff-delete-base); --color-icon-diff-delete-hover: var(--icon-diff-delete-hover); --color-syntax-comment: var(--syntax-comment); + --color-syntax-regexp: var(--syntax-regexp); --color-syntax-string: var(--syntax-string); --color-syntax-keyword: var(--syntax-keyword); --color-syntax-function: var(--syntax-function); --color-syntax-number: var(--syntax-number); --color-syntax-operator: var(--syntax-operator); --color-syntax-variable: var(--syntax-variable); + --color-syntax-property: var(--syntax-property); + --color-syntax-parameter: var(--syntax-parameter); --color-syntax-type: var(--syntax-type); --color-syntax-constant: var(--syntax-constant); --color-syntax-punctuation: var(--syntax-punctuation); + --color-syntax-namespace: var(--syntax-namespace); + --color-syntax-enum: var(--syntax-enum); --color-syntax-success: var(--syntax-success); --color-syntax-warning: var(--syntax-warning); --color-syntax-critical: var(--syntax-critical); diff --git a/packages/ui/src/styles/theme.css b/packages/ui/src/styles/theme.css index 6187eef9d..431d811da 100644 --- a/packages/ui/src/styles/theme.css +++ b/packages/ui/src/styles/theme.css @@ -268,20 +268,25 @@ --icon-diff-add-active: var(--mint-light-12); --icon-diff-delete-base: var(--ember-light-10); --icon-diff-delete-hover: var(--ember-light-11); - --syntax-comment: #8a8a8a; - --syntax-string: #d68c27; - --syntax-keyword: #3b7dd8; - --syntax-function: #d1383d; - --syntax-number: #3d9a57; - --syntax-operator: #d68c27; - --syntax-variable: #b0851f; - --syntax-type: #318795; - --syntax-constant: #953170; - --syntax-punctuation: #1a1a1a; - --syntax-success: var(--apple-dark-10); + --syntax-comment: var(--text-weaker); + --syntax-regexp: var(--text-base); + --syntax-string: var(--mint-light-11); + --syntax-keyword: var(--text-weak); + --syntax-primitive: var(--ember-light-11); + --syntax-operator: var(--text-weak); + --syntax-variable: var(--text-strong); + --syntax-property: var(--lilac-light-11); + --syntax-type: var(--cobalt-light-11); + --syntax-constant: var(--lilac-light-11); + --syntax-punctuation: var(--text-weak); + --syntax-object: var(--blue-light-11); + --syntax-success: var(--apple-light-10); --syntax-warning: var(--amber-light-10); - --syntax-critical: var(--ember-dark-9); - --syntax-info: var(--lilac-dark-11); + --syntax-critical: var(--ember-light-9); + --syntax-info: var(--lilac-light-11); + --syntax-diff-add: var(--mint-light-11); + --syntax-diff-delete: var(--ember-light-11); + --syntax-unknown: red; --markdown-heading: #d68c27; --markdown-text: #1a1a1a; --markdown-link: #3b7dd8; @@ -503,20 +508,24 @@ --icon-diff-add-active: var(--mint-dark-11); --icon-diff-delete-base: var(--ember-dark-9); --icon-diff-delete-hover: var(--ember-dark-10); - --syntax-comment: #808080; - --syntax-string: #9d7cd8; - --syntax-keyword: #fab283; - --syntax-function: #e06c75; - --syntax-number: #7fd88f; - --syntax-operator: #f5a742; - --syntax-variable: #e5c07b; - --syntax-type: #56b6c2; - --syntax-constant: #c2569a; - --syntax-punctuation: #eeeeee; + --syntax-comment: var(--text-weaker); + --syntax-regexp: var(--text-base); + --syntax-string: var(--mint-dark-11); + --syntax-keyword: var(--text-weak); + --syntax-primitive: var(--ember-dark-11); + --syntax-operator: var(--text-weak); + --syntax-variable: var(--text-strong); + --syntax-property: var(--lilac-dark-11); + --syntax-type: var(--cobalt-dark-11); + --syntax-constant: var(--lilac-dark-11); + --syntax-punctuation: var(--text-weak); + --syntax-object: var(--blue-dark-11); --syntax-success: var(--apple-dark-10); --syntax-warning: var(--amber-dark-10); - --syntax-critical: var(--ember-dark-10); - --syntax-info: var(--lilac-dark-10); + --syntax-critical: var(--ember-dark-9); + --syntax-info: var(--lilac-dark-11); + --syntax-diff-add: var(--mint-dark-11); + --syntax-diff-delete: var(--ember-dark-11); --markdown-heading: #9d7cd8; --markdown-text: #eeeeee; --markdown-link: #fab283; diff --git a/theme-test.java b/theme-test.java new file mode 100644 index 000000000..7c1b582af --- /dev/null +++ b/theme-test.java @@ -0,0 +1,461 @@ +package com.example.theme; + +import java.util.*; +import java.util.concurrent.*; +import java.util.function.*; +import java.util.stream.*; +import java.time.*; +import java.time.format.*; +import java.net.*; +import java.io.*; +import java.nio.file.*; +import java.sql.*; +import java.lang.annotation.*; +import java.math.BigDecimal; +import java.math.BigInteger; + +// Enum definition +public enum LogLevel { + DEBUG(0, "Debug"), + INFO(1, "Info"), + WARN(2, "Warning"), + ERROR(3, "Error"); + + private final int level; + private final String description; + + LogLevel(int level, String description) { + this.level = level; + this.description = description; + } + + public int getLevel() { return level; } + public String getDescription() { return description; } +} + +// Interface with generics +public interface Repository { + Optional findById(Long id); + List findAll(); + T save(T entity); + void delete(Long id); + Stream stream(); + + @FunctionalInterface + interface Predicate { + boolean test(T t); + } +} + +// Abstract class +public abstract class AbstractService implements Repository { + protected final Map cache = new ConcurrentHashMap<>(); + protected volatile boolean initialized = false; + + @Override + public Optional findById(Long id) { + return Optional.ofNullable(cache.get(id)); + } + + @Override + public List findAll() { + return new ArrayList<>(cache.values()); + } + + @Override + public Stream stream() { + return cache.values().stream(); + } + + protected abstract void validate(T entity) throws ValidationException; +} + +// Annotation definition +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface Service { + String value() default ""; + boolean transactional() default true; + Class[] exceptions() = {}; +} + +// Record class (Java 14+) +public record User( + Long id, + String username, + String email, + @Deprecated String fullName, + LocalDateTime createdAt, + boolean active +) implements Entity { + + public User { + Objects.requireNonNull(username, "Username cannot be null"); + Objects.requireNonNull(email, "Email cannot be null"); + Objects.requireNonNull(createdAt, "Created date cannot be null"); + } + + public static User of(String username, String email) { + return new User(null, username, email, null, LocalDateTime.now(), true); + } + + public User withId(Long id) { + return new User(id, username, email, fullName, createdAt, active); + } +} + +// Exception classes +public class ValidationException extends RuntimeException { + private final List errors; + + public ValidationException(String message) { + super(message); + this.errors = List.of(message); + } + + public ValidationException(List errors) { + super(String.join(", ", errors)); + this.errors = Collections.unmodifiableList(errors); + } + + public List getErrors() { return errors; } +} + +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String resource, Long id) { + super(String.format("%s with id %d not found", resource, id)); + } +} + +// Service implementation +@Service(value = "userService", transactional = true) +public class UserService extends AbstractService { + + private static final Logger logger = LoggerFactory.getLogger(UserService.class); + private static final int MAX_RETRY_ATTEMPTS = 3; + private static final Duration TIMEOUT = Duration.ofSeconds(30); + + private final EmailService emailService; + private final UserRepository userRepository; + private final PasswordEncoder passwordEncoder; + + @Inject + public UserService(EmailService emailService, + UserRepository userRepository, + PasswordEncoder passwordEncoder) { + this.emailService = emailService; + this.userRepository = userRepository; + this.passwordEncoder = passwordEncoder; + } + + @Override + protected void validate(User user) throws ValidationException { + List errors = new ArrayList<>(); + + if (user.username() == null || user.username().trim().isEmpty()) { + errors.add("Username is required"); + } else if (!user.username().matches("^[a-zA-Z0-9_]{3,20}$")) { + errors.add("Username must be 3-20 characters, alphanumeric and underscore only"); + } + + if (user.email() == null || !user.email().matches("^[A-Za-z0-9+_.-]+@(.+)$")) { + errors.add("Valid email is required"); + } + + if (!errors.isEmpty()) { + throw new ValidationException(errors); + } + } + + @Transactional + public User createUser(CreateUserRequest request) throws ValidationException { + logger.info("Creating new user: {}", request.username()); + + // Check if user already exists + if (userRepository.findByUsername(request.username()).isPresent()) { + throw new ValidationException("Username already exists"); + } + + if (userRepository.findByEmail(request.email()).isPresent()) { + throw new ValidationException("Email already exists"); + } + + // Create new user + User user = User.of(request.username(), request.email()) + .withId(generateId()); + + validate(user); + + try { + User savedUser = userRepository.save(user); + cache.put(savedUser.id(), savedUser); + + // Send welcome email asynchronously + CompletableFuture.runAsync(() -> + emailService.sendWelcomeEmail(savedUser) + ).exceptionally(throwable -> { + logger.error("Failed to send welcome email to user {}", savedUser.id(), throwable); + return null; + }); + + logger.info("Successfully created user with ID: {}", savedUser.id()); + return savedUser; + + } catch (DataAccessException e) { + logger.error("Database error while creating user", e); + throw new ServiceException("Failed to create user", e); + } + } + + public Optional findByUsername(String username) { + return cache.values().stream() + .filter(user -> user.username().equals(username)) + .findFirst(); + } + + public List findActiveUsers() { + return cache.values().stream() + .filter(User::active) + .sorted(Comparator.comparing(User::createdAt).reversed()) + .collect(Collectors.toList()); + } + + @Retry(maxAttempts = MAX_RETRY_ATTEMPTS, backoff = @Backoff(delay = 1000)) + public User updateUser(Long id, UpdateUserRequest request) { + User existingUser = findById(id) + .orElseThrow(() -> new ResourceNotFoundException("User", id)); + + User updatedUser = new User( + id, + request.username() != null ? request.username() : existingUser.username(), + request.email() != null ? request.email() : existingUser.email(), + existingUser.fullName(), + existingUser.createdAt(), + request.active() != null ? request.active() : existingUser.active() + ); + + validate(updatedUser); + + try { + User savedUser = userRepository.save(updatedUser); + cache.put(id, savedUser); + return savedUser; + } catch (DataAccessException e) { + logger.error("Failed to update user with ID: {}", id, e); + throw new ServiceException("Failed to update user", e); + } + } + + @Async + public CompletableFuture deleteUser(Long id) { + return CompletableFuture.runAsync(() -> { + try { + userRepository.deleteById(id); + cache.remove(id); + logger.info("Successfully deleted user with ID: {}", id); + } catch (DataAccessException e) { + logger.error("Failed to delete user with ID: {}", id, e); + throw new ServiceException("Failed to delete user", e); + } + }); + } + + private Long generateId() { + return System.currentTimeMillis() + (long)(Math.random() * 1000); + } +} + +// Utility class +public final class DateUtils { + + private DateUtils() { + // Utility class - prevent instantiation + } + + private static final DateTimeFormatter ISO_FORMATTER = + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + + public static String formatIsoDateTime(LocalDateTime dateTime) { + return dateTime.atZone(ZoneId.systemDefault()).format(ISO_FORMATTER); + } + + public static LocalDateTime parseIsoDateTime(String isoString) { + return LocalDateTime.parse(isoString, ISO_FORMATTER); + } + + public static boolean isWithinLastDays(LocalDateTime dateTime, int days) { + return dateTime.isAfter(LocalDateTime.now().minusDays(days)); + } +} + +// Builder pattern +public class UserQueryBuilder { + private String username; + private String email; + private Boolean active; + private LocalDateTime createdAfter; + private LocalDateTime createdBefore; + private SortOrder sortOrder = SortOrder.ASC; + private String sortBy = "createdAt"; + private int limit = 100; + private int offset = 0; + + public UserQueryBuilder withUsername(String username) { + this.username = username; + return this; + } + + public UserQueryBuilder withEmail(String email) { + this.email = email; + return this; + } + + public UserQueryBuilder activeOnly(boolean active) { + this.active = active; + return this; + } + + public UserQueryBuilder createdAfter(LocalDateTime date) { + this.createdAfter = date; + return this; + } + + public UserQueryBuilder createdBefore(LocalDateTime date) { + this.createdBefore = date; + return this; + } + + public UserQueryBuilder sortBy(String field, SortOrder order) { + this.sortBy = field; + this.sortOrder = order; + return this; + } + + public UserQueryBuilder limit(int limit) { + this.limit = Math.max(1, Math.min(limit, 1000)); + return this; + } + + public UserQueryBuilder offset(int offset) { + this.offset = Math.max(0, offset); + return this; + } + + public UserQuery build() { + return new UserQuery(username, email, active, createdAfter, createdBefore, + sortBy, sortOrder, limit, offset); + } +} + +// Lambda expressions and streams +public class StreamProcessor { + + private static final Map> TYPE_CONVERTERS = Map.of( + "string", s -> s, + "int", Integer::parseInt, + "double", Double::parseDouble, + "boolean", Boolean::parseBoolean, + "bigdecimal", BigDecimal::new, + "bigint", BigInteger::new + ); + + public Map processConfig(Properties properties) { + return properties.entrySet().stream() + .filter(entry -> entry.getKey() instanceof String) + .filter(entry -> entry.getValue() != null) + .collect(Collectors.toMap( + entry -> (String) entry.getKey(), + entry -> convertValue((String) entry.getKey(), (String) entry.getValue()) + )); + } + + private Object convertValue(String key, String value) { + String type = determineType(key, value); + return TYPE_CONVERTERS.getOrDefault(type, Function.identity()).apply(value); + } + + private String determineType(String key, String value) { + if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) { + return "boolean"; + } else if (value.matches("-?\\d+")) { + return "int"; + } else if (value.matches("-?\\d*\\.\\d+")) { + return "double"; + } else if (key.toLowerCase().contains("amount") || key.toLowerCase().contains("price")) { + return "bigdecimal"; + } + return "string"; + } + + public List validateEmails(List emails) { + return emails.stream() + .filter(Objects::nonNull) + .map(String::trim) + .filter(email -> !email.isEmpty()) + .filter(email -> email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) + .distinct() + .collect(Collectors.toList()); + } + + public CompletableFuture> processUsersAsync(List users) { + return CompletableFuture.supplyAsync(() -> + users.parallelStream() + .filter(User::active) + .filter(user -> user.createdAt().isAfter(LocalDateTime.now().minusYears(1))) + .sorted(Comparator.comparing(User::username)) + .collect(Collectors.toList()) + ); + } +} + +// Main class for testing +public class Main { + private static final Logger logger = LoggerFactory.getLogger(Main.class); + + public static void main(String[] args) { + try { + // Initialize application context + ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); + + // Get service bean + UserService userService = context.getBean(UserService.class); + + // Create test users + List users = Arrays.asList( + User.of("john_doe", "john@example.com"), + User.of("jane_smith", "jane@example.com"), + User.of("bob_wilson", "bob@example.com") + ); + + // Process users + List> futures = users.stream() + .map(user -> { + try { + return CompletableFuture.completedFuture(userService.createUser( + new CreateUserRequest(user.username(), user.email()) + )); + } catch (ValidationException e) { + logger.error("Failed to create user: {}", user.username(), e); + return CompletableFuture.failedFuture(e); + } + }) + .collect(Collectors.toList()); + + // Wait for all to complete + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .thenRun(() -> { + logger.info("All users created successfully"); + System.out.println("Application started successfully!"); + }) + .exceptionally(throwable -> { + logger.error("Failed to initialize users", throwable); + System.err.println("Application startup failed!"); + return null; + }); + + } catch (Exception e) { + logger.error("Application startup failed", e); + System.exit(1); + } + } +} \ No newline at end of file diff --git a/theme-test.md b/theme-test.md new file mode 100644 index 000000000..3863cc325 --- /dev/null +++ b/theme-test.md @@ -0,0 +1,669 @@ +# TextMate Grammar Token Examples + +This file contains examples of every major TextMate token style for theme testing. + +## Comments + + + +// Single line comment +/_ Multi-line comment _/ + +# Shell comment + +/_ JSDoc comment with @param and @return _/ + +## Strings + +"Double quoted string" +'Single quoted string' +`Backtick string` +"String with \"escaped\" quotes" +'String with \'escaped\' quotes' +`String with \`escaped\` backticks` + +## Template Literals + +`Simple template literal` +`Template with ${variable} interpolation` +`Template with ${function.call()} expression` +Multi-line template with ${nested.interpolation} + +## Numbers + +42 +-17 +3.14159 +-0.001 +1e10 +-2.5e-8 +0xFF +0o755 +0b1010 + +## Keywords + +if else elif for while do switch case default +function class extends implements import export +return break continue throw try catch finally +var let const static async await yield +new this super null undefined true false + +## Storage Types + +int float double string boolean char void +static final abstract private public protected +readonly volatile transient synchronized + +## Constants + +MAX_VALUE +DEFAULT_TIMEOUT +API_ENDPOINT +PI +E + +## Variables + +variableName +\_privateVariable +$specialVariable +camelCase +snake_case +PascalCase +kebab-case + +## Functions + +functionName() +method.call() +object.property() +array[index] +arrowFunction => expression + +## Operators + +- - - / % ++ -- + == === != !== > < >= <= + && || ! & | ^ ~ << >> >>> + = += -= \*= /= %= &= |= ^= <<= >>= >>>= + +## Punctuation + +, ; : . ... ( ) [ ] { } < > / \\ + +# @ $ % ^ & \* - \_ + = | ~ ` ? + +## Entities + +ClassName +InterfaceName +EnumName +TypeName +MethodName +PropertyName + +## Tags + +
    + +

    + +description + +## Attributes + +class="container" +id="main" +data-value="123" +disabled +required +readonly + +## CSS Selectors & Properties + +.container +#header +.button:hover +input[type="text"] +::before +::after + +color: #ffffff; +background: linear-gradient(45deg, #ff0000, #00ff00); +font-size: 16px; +margin: 0 auto; +padding: 10px 20px; + +## Regular Expressions + +/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ +/\d{3}-\d{3}-\d{4}/g +/(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})/ + +## URLs & Paths + +https://example.com/path/to/resource +file:///Users/username/project +./relative/path +../parent/directory +/home/user/documents + +## JSON + +{ +"name": "example", +"version": "1.0.0", +"dependencies": { +"react": "^18.0.0", +"typescript": "^4.9.0" +}, +"scripts": { +"start": "node index.js", +"test": "jest" +} +} + +## XML/HTML + + + + + + + Document + + +

    + + + +## SQL + +SELECT u.id, u.name, u.email, COUNT(o.id) as order_count +FROM users u +LEFT JOIN orders o ON u.id = o.user_id +WHERE u.active = true +AND o.created_at >= '2023-01-01' +GROUP BY u.id, u.name, u.email +HAVING COUNT(o.id) > 5 +ORDER BY order_count DESC +LIMIT 10; + +## GraphQL + +query GetUserProfile($userId: ID!, $includePosts: Boolean!) { +user(id: $userId) { +id +name +email +avatar +createdAt +posts @include(if: $includePosts) { +id +title +content +publishedAt +comments(first: 10) { +edges { +node { +id +author +content +createdAt +} +} +} +} +} +} + +## Shell/Bash + +#!/bin/bash + +# Variables + +PROJECT*DIR="/home/user/projects" +BACKUP_DIR="$PROJECT_DIR/backups" +TIMESTAMP=$(date +"%Y%m%d*%H%M%S") + +# Functions + +create*backup() { +local source_dir=$1 + local backup_file="$BACKUP_DIR/backup*$TIMESTAMP.tar.gz" + + echo "Creating backup of $source_dir..." + tar -czf "$backup_file" "$source_dir" + echo "Backup created: $backup_file" + +} + +# Conditional logic + +if [ -d "$PROJECT_DIR" ]; then +create_backup "$PROJECT_DIR" +else +echo "Project directory not found: $PROJECT_DIR" +exit 1 +fi + +## Python + +import os +import sys +from typing import List, Dict, Optional +import requests +from dataclasses import dataclass + +@dataclass +class User: +id: int +name: str +email: Optional[str] = None +active: bool = True + + def __post_init__(self): + if not self.name.strip(): + raise ValueError("Name cannot be empty") + +class UserService: +def **init**(self, api_url: str): +self.api_url = api_url +self.session = requests.Session() + + async def get_user(self, user_id: int) -> Optional[User]: + """Fetch user data from API.""" + try: + response = await self.session.get(f"{self.api_url}/users/{user_id}") + response.raise_for_status() + data = response.json() + return User(**data) + except requests.RequestException as e: + print(f"Error fetching user {user_id}: {e}") + return None + +## Rust + +use std::collections::HashMap; +use std::fs::File; +use std::io::{self, Read}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct User { +pub id: u64, +pub name: String, +pub email: Option, #[serde(default)] +pub active: bool, +pub created_at: chrono::DateTime, +} + +impl User { +pub fn new(id: u64, name: String, email: Option) -> Self { +Self { +id, +name, +email, +active: true, +created_at: chrono::Utc::now(), +} +} + + pub fn display_name(&self) -> String { + match &self.email { + Some(email) => format!("{} <{}>", self.name, email), + None => self.name.clone(), + } + } + +} + +pub struct UserService { +api_url: String, +client: reqwest::Client, +} + +impl UserService { +pub fn new(api_url: String) -> Self { +Self { +api_url, +client: reqwest::Client::new(), +} +} + + pub async fn get_user(&self, user_id: u64) -> Result> { + let url = format!("{}/users/{}", self.api_url, user_id); + let response = self.client.get(&url).send().await?; + let user: User = response.json().await?; + Ok(user) + } + +} + +## Go + +package main + +import ( +"context" +"encoding/json" +"fmt" +"log" +"net/http" +"time" +"github.com/gorilla/mux" +) + +type User struct { +ID int64 `json:"id"` +Name string `json:"name"` +Email \*string `json:"email,omitempty"` +Active bool `json:"active"` +CreatedAt time.Time `json:"created_at"` +} + +type UserService struct { +re UserRepository +} + +func NewUserService(repo UserRepository) \*UserService { +return &UserService{repo: repo} +} + +func (s *UserService) GetUser(ctx context.Context, id int64) (*User, error) { +user, err := s.repo.FindByID(ctx, id) +if err != nil { +return nil, fmt.Errorf("failed to get user %d: %w", id, err) +} +return user, nil +} + +func (s *UserService) CreateUser(ctx context.Context, req *CreateUserRequest) (\*User, error) { +user := &User{ +Name: req.Name, +Email: req.Email, +Active: true, +CreatedAt: time.Now(), +} +if err := s.repo.Create(ctx, user); err != nil { +return nil, fmt.Errorf("failed to create user: %w", err) +} +return user, nil +} + +## YAML + +apiVersion: apps/v1 +kind: Deployment +metadata: +name: web-app +namespace: production +labels: +app: web-app +version: v1.2.3 +spec: +replicas: 3 +selector: +matchLabels: +app: web-app +template: +metadata: +labels: +app: web-app +tier: frontend +spec: +containers: - name: web-app +image: nginx:1.21-alpine +ports: - containerPort: 80 +protocol: TCP +env: - name: NODE_ENV +value: "production" - name: API_URL +valueFrom: +secretKeyRef: +name: app-secrets +key: api-url +resources: +requests: +memory: "64Mi" +cpu: "250m" +limits: +memory: "128Mi" +cpu: "500m" +livenessProbe: +httpGet: +path: /health +port: 80 +initialDelaySeconds: 30 +periodSeconds: 10 +readinessProbe: +httpGet: +path: /ready +port: 80 +initialDelaySeconds: 5 +periodSeconds: 5 + +## TOML + +[project] +name = "example-app" +version = "1.0.0" +description = "An example application" +authors = ["John Doe "] +license = "MIT" +readme = "README.md" +homepage = "https://example.com" +repository = "https://github.com/johndoe/example-app" +keywords = ["web", "api", "rust"] +categories = ["web-programming"] +edition = "2021" + +[dependencies] +tokio = { version = "1.0", features = ["full"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +reqwest = { version = "0.11", features = ["json"] } +chrono = { version = "0.4", features = ["serde"] } +log = "0.4" +env_logger = "0.10" + +[dev-dependencies] +tokio-test = "0.4" +mockito = "1.0" + +[[bin]] +name = "server" +path = "src/main.rs" + +[[bin]] +name = "client" +path = "src/client.rs" + +## Dockerfile + +FROM node:18-alpine AS base + +# Install dependencies only when needed + +FROM base AS deps + +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. + +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Install dependencies based on the preferred package manager + +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./ +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \ + else echo "Lockfile not found." && exit 1; \ + fi + +# Rebuild the source code only when needed + +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Next.js collects completely anonymous telemetry data about general usage. + +# Learn more here: https://nextjs.org/telemetry + +# Uncomment the following line in case you want to disable telemetry during the build. + +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN \ + if [ -f yarn.lock ]; then yarn run build; \ + elif [ -f package-lock.json ]; then npm run build; \ + elif [ -f pnpm-lock.yaml ]; then pnpm run build; \ + else echo "Lockfile not found." && exit 1; \ + fi + +# Production image, copy all the files and run next + +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV production + +# Uncomment the following line in case you want to disable telemetry during runtime. + +# ENV NEXT_TELEMETRY_DISABLED 1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public + +# Set the correct permission for prerender cache + +RUN mkdir .next +RUN chown nextjs:nodejs .next + +# Automatically leverage output traces to reduce image size + +# https://nextjs.org/docs/advanced-features/output-file-tracing + +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT 3000 + +# set hostname to localhost + +ENV HOSTNAME "0.0.0.0" + +# server.js is created by next build from the standalone output + +# https://nextjs.org/docs/pages/api-reference/next-config-js/output + +CMD ["node", "server.js"] + +## Makefile + +.PHONY: help build test clean install dev lint format + +# Default target + +.DEFAULT_GOAL := help + +# Variables + +APP_NAME := myapp +VERSION := $(shell git describe --tags --always --dirty) +BUILD_DIR := ./build +DIST_DIR := ./dist +GO_FILES := $(shell find . -name '\*.go' -type f) + +help: ## Show this help message +@echo "Available targets:" +@grep -E '^[a-zA-Z_-]+:._?## ._$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.\*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' + +install: ## Install dependencies +go mod download +npm install + +build: ## Build the application +@echo "Building $(APP_NAME) version $(VERSION)..." + mkdir -p $(BUILD_DIR) + go build -ldflags "-X main.version=$(VERSION)" -o $(BUILD_DIR)/$(APP_NAME) ./cmd/main.go + +test: ## Run tests +go test -v ./... +npm test + +lint: ## Run linters +golangci-lint run +npx eslint . + +format: ## Format code +go fmt ./... +npx prettier --write . + +dev: ## Run in development mode +go run ./cmd/main.go --dev + +clean: ## Clean build artifacts +rm -rf $(BUILD_DIR) +rm -rf $(DIST_DIR) +go clean -cache + +docker-build: ## Build Docker image +docker build -t $(APP_NAME):$(VERSION) . +docker tag $(APP_NAME):$(VERSION) $(APP_NAME):latest + +docker-run: ## Run Docker container +docker run -p 8080:8080 $(APP_NAME):latest + +release: ## Create a new release +@echo "Creating release $(VERSION)" +git tag -a $(VERSION) -m "Release $(VERSION)" +git push origin $(VERSION) +goreleaser release --rm-dist + +## Git Diff + +diff --git a/src/components/UserProfile.tsx b/src/components/UserProfile.tsx +index 1234567..abcdefg 100644 +--- a/src/components/UserProfile.tsx ++++ b/src/components/UserProfile.tsx +@@ -10,7 +10,7 @@ interface User { +id: number +name: string +email?: string + +- createdAt: Date + +* readonly createdAt: Date + active: boolean + } + +@@ -25,8 +25,12 @@ const UserProfile: FC<{ user: User }> = ({ user }) => { +const [isEditing, setIsEditing] = useState(false) +const [formData, setFormData] = useState(user) + +- const handleSubmit = async (e: React.FormEvent) => { +- e.preventDefault() +- // Handle form submission +- } +- return ( + +*
    + +-
    +

    {user.name}

    +

    {user.email}

    +
    diff --git a/theme-test.tsx b/theme-test.tsx index 16559bf70..61837473e 100644 --- a/theme-test.tsx +++ b/theme-test.tsx @@ -29,6 +29,8 @@ class Repository { } public find(id: number): T | undefined { + const x = undefined + type x = { foo: undefined } return this.items.find((item) => item.id === id) } @@ -66,6 +68,72 @@ const sql = ` AND created_at > '${new Date().toISOString()}' ` +// String source examples (CSS-in-JS, GraphQL, etc.) +const styledComponent = css` + .container { + display: flex; + justify-content: center; + align-items: center; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + padding: 2rem; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15); + } + + .title { + font-size: 1.5rem; + font-weight: bold; + color: white; + margin-bottom: 1rem; + } + } +` + +const graphqlQuery = ` + query GetUserProfile($userId: ID!) { + user(id: $userId) { + id + name + email + avatar + createdAt + posts { + id + title + content + publishedAt + comments { + id + author + content + createdAt + } + } + } + } +` + +const htmlTemplate = ` +
    + ${user.name} + +
    + + +
    +
    +` + // Arrow functions const debounce = any>( func: T, -- cgit v1.2.3 From 89922a8598bf344ed24fb1b472307f383ef2b18a Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 12:56:07 -0600 Subject: fix(desktop): prompt input missing on new session --- packages/desktop/src/pages/session.tsx | 728 ++++++++++++++++----------------- 1 file changed, 361 insertions(+), 367 deletions(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 3dcc24e61..b872c1434 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -330,142 +330,39 @@ export default function Page() {
    - -
    New session
    -
    - -
    - {getDirectory(sync.data.path.directory)} - {getFilename(sync.data.path.directory)} -
    -
    -
    - -
    - Last modified  - - {DateTime.fromMillis(sync.data.project.time.created).toRelative()} - -
    -
    -
    - } +
    - {(_) => { - return ( -
    -
    -
    - - - -
    -
    - 1}> -
      - - {(message) => { - const assistantMessages = createMemo(() => { - if (!session.id) return [] - return sync.data.message[session.id]?.filter( - (m) => m.role === "assistant" && m.parentID == message.id, - ) as AssistantMessageType[] - }) - const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) - const working = createMemo(() => !message.summary?.body && !error()) - - const handleClick = () => session.messages.setActive(message.id) - - return ( -
    • - - - {message.summary?.title} -
    - } - > - - - -
  • - ) - }} - - - -
    +
    + + +
    + + + +
    +
    + 1}> +
      {(message) => { - const isActive = createMemo(() => session.messages.active()?.id === message.id) - const [titled, setTitled] = createSignal(!!message.summary?.title) const assistantMessages = createMemo(() => { if (!session.id) return [] return sync.data.message[session.id]?.filter( @@ -473,261 +370,358 @@ export default function Page() { ) as AssistantMessageType[] }) const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) - const [completed, setCompleted] = createSignal(!!message.summary?.body || !!error()) - const [detailsExpanded, setDetailsExpanded] = createSignal(false) - const parts = createMemo(() => sync.data.part[message.id]) - const hasToolPart = createMemo(() => - assistantMessages() - ?.flatMap((m) => sync.data.part[m.id]) - .some((p) => p?.type === "tool"), - ) const working = createMemo(() => !message.summary?.body && !error()) - // allowing time for the animations to finish - createEffect(() => { - const title = message.summary?.title - setTimeout(() => setTitled(!!title), 10_000) - }) - createEffect(() => { - const summary = message.summary?.body - const complete = !!summary || !!error() - setTimeout(() => setCompleted(complete), 1200) - }) + const handleClick = () => session.messages.setActive(message.id) return ( - -
      + + + {message.summary?.title} +
      + } > - {/* Title */} -
      -
      - div]:bg-icon-strong-base data-[active=true]:[&>div]:w-full": true, + "@7xl:hidden": local.layout.review.state() !== "open", + }} + > +
      + + + + + ) + }} + +
    +
    +
    + + {(message) => { + const isActive = createMemo(() => session.messages.active()?.id === message.id) + const [titled, setTitled] = createSignal(!!message.summary?.title) + const assistantMessages = createMemo(() => { + if (!session.id) return [] + return sync.data.message[session.id]?.filter( + (m) => m.role === "assistant" && m.parentID == message.id, + ) as AssistantMessageType[] + }) + const error = createMemo(() => assistantMessages().find((m) => m?.error)?.error) + const [completed, setCompleted] = createSignal(!!message.summary?.body || !!error()) + const [detailsExpanded, setDetailsExpanded] = createSignal(false) + const parts = createMemo(() => sync.data.part[message.id]) + const hasToolPart = createMemo(() => + assistantMessages() + ?.flatMap((m) => sync.data.part[m.id]) + .some((p) => p?.type === "tool"), + ) + const working = createMemo(() => !message.summary?.body && !error()) + + // allowing time for the animations to finish + createEffect(() => { + const title = message.summary?.title + setTimeout(() => setTitled(!!title), 10_000) + }) + createEffect(() => { + const summary = message.summary?.body + const complete = !!summary || !!error() + setTimeout(() => setCompleted(complete), 1200) + }) + + return ( + +
    + {/* Title */} +
    +
    + + } + > +

    + {message.summary?.title} +

    +
    +
    +
    +
    + +
    + {/* Summary */} + +
    +
    +

    + + Summary + Response + +

    + + {(summary) => ( + *]:fade-up-text": !message.summary?.diffs?.length, + }} + text={summary()} /> - } - > -

    - {message.summary?.title} -

    + )}
    -
    -
    - -
    - {/* Summary */} - -
    -
    -

    - - Summary - Response - -

    - - {(summary) => ( - *]:fade-up-text": !message.summary?.diffs?.length, - }} - text={summary()} - /> - )} - -
    - - - {(diff) => ( - - - -
    -
    - -
    - - - {getDirectory(diff.file)}‎ - - - - {getFilename(diff.file)} + + + {(diff) => ( + + + +
    +
    + +
    + + + {getDirectory(diff.file)}‎ -
    -
    -
    - - + + + {getFilename(diff.file)} +
    -
    -
    - - - -
    - )} -
    -
    -
    - - - - {error()?.data?.message as string} - - - {/* Response */} -
    - - - - - - - -
    -
    - - Hide details - Show details - -
    - -
    -
    - -
    - - {(assistantMessage) => { - const parts = createMemo(() => sync.data.part[assistantMessage.id]) - return +
    + + +
    +
    + + + + - - - {error()?.data?.message as string} - - -
    - - - - + after={{ + name: diff.file!, + contents: diff.after!, + }} + /> + + + )} + +
    + + + + {error()?.data?.message as string} + + + {/* Response */} +
    + + + + + + + +
    +
    + + Hide details + Show details + +
    + +
    +
    + +
    + + {(assistantMessage) => { + const parts = createMemo(() => sync.data.part[assistantMessage.id]) + return + }} + + + + {error()?.data?.message as string} + + +
    +
    +
    +
    +
    - - ) - }} - +
    + + ) + }} +
    +
    +
    + + +
    +
    New session
    +
    + +
    + {getDirectory(sync.data.path.directory)} + {getFilename(sync.data.path.directory)}
    - -
    - { - inputRef = el - }} - /> +
    +
    + +
    + Last modified  + + {DateTime.fromMillis(sync.data.project.time.created).toRelative()} +
    - -
    -
    -
    - - - - - { - local.layout.review.tab() - session.layout.setActiveTab("review") + + +
    + { + inputRef = el + }} + /> +
    +
    + +
    +
    +
    + + + + + { + local.layout.review.tab() + session.layout.setActiveTab("review") + }} + /> + +
    +
    +
    All changes
    +
    + + + {(diff) => ( + + + +
    +
    + +
    + + + {getDirectory(diff.file)}‎ + + + {getFilename(diff.file)} +
    +
    +
    + + +
    +
    +
    +
    + + - -
    -
    -
    All changes
    -
    - - - {(diff) => ( - - - -
    -
    - -
    - - - {getDirectory(diff.file)}‎ - - - {getFilename(diff.file)} -
    -
    -
    - - -
    -
    -
    -
    - - - -
    - )} -
    -
    -
    -
    - + + + )} + +
    - ) - }} -
    +
    +
    +
    -- cgit v1.2.3 From 7adbc3ad443af942b400923b66119436589c676c Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:14:57 -0600 Subject: fix(desktop): code tab padding --- packages/desktop/src/pages/session.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index b872c1434..1c92941d0 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -791,14 +791,14 @@ export default function Page() { }, ) return ( - + {(f) => ( )} -- cgit v1.2.3 From 7f51b181d4b77326c5e7f6961552036ae2d56234 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:30:07 -0600 Subject: chore(desktop): cleanup shiki theme stuff --- packages/desktop/src/components/theme.json | 558 --------------------- packages/desktop/src/index.tsx | 36 +- packages/ui/src/components/code.tsx | 3 +- packages/ui/src/components/diff.tsx | 761 ++++++++++++++--------------- packages/ui/src/components/index.ts | 1 - packages/ui/src/context/marked.tsx | 8 +- packages/ui/src/context/shiki.tsx | 577 ---------------------- 7 files changed, 393 insertions(+), 1551 deletions(-) delete mode 100644 packages/desktop/src/components/theme.json delete mode 100644 packages/ui/src/context/shiki.tsx (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/components/theme.json b/packages/desktop/src/components/theme.json deleted file mode 100644 index 36a4d020d..000000000 --- a/packages/desktop/src/components/theme.json +++ /dev/null @@ -1,558 +0,0 @@ -{ - "colors": { - "actionBar.toggledBackground": "var(--surface-raised-base)", - "activityBarBadge.background": "var(--surface-brand-base)", - "checkbox.border": "var(--border-base)", - "editor.background": "transparent", - "editor.foreground": "var(--text-base)", - "editor.inactiveSelectionBackground": "var(--surface-raised-base)", - "editor.selectionHighlightBackground": "var(--border-active)", - "editorIndentGuide.activeBackground1": "var(--border-weak-base)", - "editorIndentGuide.background1": "var(--border-weak-base)", - "input.placeholderForeground": "var(--text-weak)", - "list.activeSelectionIconForeground": "var(--text-base)", - "list.dropBackground": "var(--surface-raised-base)", - "menu.background": "var(--surface-base)", - "menu.border": "var(--border-base)", - "menu.foreground": "var(--text-base)", - "menu.selectionBackground": "var(--surface-interactive-base)", - "menu.separatorBackground": "var(--border-base)", - "ports.iconRunningProcessForeground": "var(--icon-success-base)", - "sideBarSectionHeader.background": "transparent", - "sideBarSectionHeader.border": "var(--border-weak-base)", - "sideBarTitle.foreground": "var(--text-weak)", - "statusBarItem.remoteBackground": "var(--surface-success-base)", - "statusBarItem.remoteForeground": "var(--text-base)", - "tab.lastPinnedBorder": "var(--border-weak-base)", - "tab.selectedBackground": "var(--surface-raised-base)", - "tab.selectedForeground": "var(--text-weak)", - "terminal.inactiveSelectionBackground": "var(--surface-raised-base)", - "widget.border": "var(--border-base)" - }, - "displayName": "opencode", - "name": "opencode", - "semanticHighlighting": true, - "semanticTokenColors": { - "customLiteral": "var(--syntax-function)", - "newOperator": "var(--syntax-operator)", - "numberLiteral": "var(--syntax-number)", - "stringLiteral": "var(--syntax-string)" - }, - "tokenColors": [ - { - "scope": [ - "meta.embedded", - "source.groovy.embedded", - "string meta.image.inline.markdown", - "variable.legacy.builtin.python" - ], - "settings": { - "foreground": "var(--text-base)" - } - }, - { - "scope": "emphasis", - "settings": { - "fontStyle": "italic" - } - }, - { - "scope": "strong", - "settings": { - "fontStyle": "bold" - } - }, - { - "scope": "header", - "settings": { - "foreground": "var(--markdown-heading)" - } - }, - { - "scope": "comment", - "settings": { - "foreground": "var(--syntax-comment)" - } - }, - { - "scope": "constant.language", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": [ - "constant.numeric", - "variable.other.enummember", - "keyword.operator.plus.exponent", - "keyword.operator.minus.exponent" - ], - "settings": { - "foreground": "var(--syntax-number)" - } - }, - { - "scope": "constant.regexp", - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": "entity.name.tag", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": ["entity.name.tag.css", "entity.name.tag.less"], - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": "entity.other.attribute-name", - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": [ - "entity.other.attribute-name.class.css", - "source.css entity.other.attribute-name.class", - "entity.other.attribute-name.id.css", - "entity.other.attribute-name.parent-selector.css", - "entity.other.attribute-name.parent.less", - "source.css entity.other.attribute-name.pseudo-class", - "entity.other.attribute-name.pseudo-element.css", - "source.css.less entity.other.attribute-name.id", - "entity.other.attribute-name.scss" - ], - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": "invalid", - "settings": { - "foreground": "var(--syntax-critical)" - } - }, - { - "scope": "markup.underline", - "settings": { - "fontStyle": "underline" - } - }, - { - "scope": "markup.bold", - "settings": { - "fontStyle": "bold", - "foreground": "var(--markdown-strong)" - } - }, - { - "scope": "markup.heading", - "settings": { - "fontStyle": "bold", - "foreground": "var(--theme-markdown-heading)" - } - }, - { - "scope": "markup.italic", - "settings": { - "fontStyle": "italic" - } - }, - { - "scope": "markup.strikethrough", - "settings": { - "fontStyle": "strikethrough" - } - }, - { - "scope": "markup.inserted", - "settings": { - "foreground": "var(--text-diff-add-base)" - } - }, - { - "scope": "markup.deleted", - "settings": { - "foreground": "var(--text-diff-delete-base)" - } - }, - { - "scope": "markup.changed", - "settings": { - "foreground": "var(--text-base)" - } - }, - { - "scope": "punctuation.definition.quote.begin.markdown", - "settings": { - "foreground": "var(--markdown-block-quote)" - } - }, - { - "scope": "punctuation.definition.list.begin.markdown", - "settings": { - "foreground": "var(--markdown-list-enumeration)" - } - }, - { - "scope": "markup.inline.raw", - "settings": { - "foreground": "var(--markdown-code)" - } - }, - { - "scope": "punctuation.definition.tag", - "settings": { - "foreground": "var(--syntax-punctuation)" - } - }, - { - "scope": ["meta.preprocessor", "entity.name.function.preprocessor"], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "meta.preprocessor.string", - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": "meta.preprocessor.numeric", - "settings": { - "foreground": "var(--syntax-number)" - } - }, - { - "scope": "meta.structure.dictionary.key.python", - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": "meta.diff.header", - "settings": { - "foreground": "var(--text-weak)" - } - }, - { - "scope": "storage", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "storage.type", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": ["storage.modifier", "keyword.operator.noexcept"], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": ["string", "meta.embedded.assembly"], - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": "string.tag", - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": "string.value", - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": "string.regexp", - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": [ - "punctuation.definition.template-expression.begin", - "punctuation.definition.template-expression.end", - "punctuation.section.embedded" - ], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": ["meta.template.expression"], - "settings": { - "foreground": "var(--text-base)" - } - }, - { - "scope": [ - "support.type.vendored.property-name", - "support.type.property-name", - "source.css variable", - "source.coffee.embedded" - ], - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": "keyword", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "keyword.control", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "keyword.operator", - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": [ - "keyword.operator.new", - "keyword.operator.expression", - "keyword.operator.cast", - "keyword.operator.sizeof", - "keyword.operator.alignof", - "keyword.operator.typeid", - "keyword.operator.alignas", - "keyword.operator.instanceof", - "keyword.operator.logical.python", - "keyword.operator.wordlike" - ], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "keyword.other.unit", - "settings": { - "foreground": "var(--syntax-number)" - } - }, - { - "scope": ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "support.function.git-rebase", - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": "constant.sha.git-rebase", - "settings": { - "foreground": "var(--syntax-number)" - } - }, - { - "scope": ["storage.modifier.import.java", "variable.language.wildcard.java", "storage.modifier.package.java"], - "settings": { - "foreground": "var(--text-base)" - } - }, - { - "scope": "variable.language", - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": [ - "entity.name.function", - "support.function", - "support.constant.handlebars", - "source.powershell variable.other.member", - "entity.name.operator.custom-literal" - ], - "settings": { - "foreground": "var(--syntax-function)" - } - }, - { - "scope": [ - "support.class", - "support.type", - "entity.name.type", - "entity.name.namespace", - "entity.other.attribute", - "entity.name.scope-resolution", - "entity.name.class", - "storage.type.numeric.go", - "storage.type.byte.go", - "storage.type.boolean.go", - "storage.type.string.go", - "storage.type.uintptr.go", - "storage.type.error.go", - "storage.type.rune.go", - "storage.type.cs", - "storage.type.generic.cs", - "storage.type.modifier.cs", - "storage.type.variable.cs", - "storage.type.annotation.java", - "storage.type.generic.java", - "storage.type.java", - "storage.type.object.array.java", - "storage.type.primitive.array.java", - "storage.type.primitive.java", - "storage.type.token.java", - "storage.type.groovy", - "storage.type.annotation.groovy", - "storage.type.parameters.groovy", - "storage.type.generic.groovy", - "storage.type.object.array.groovy", - "storage.type.primitive.array.groovy", - "storage.type.primitive.groovy" - ], - "settings": { - "foreground": "var(--syntax-type)" - } - }, - { - "scope": [ - "meta.type.cast.expr", - "meta.type.new.expr", - "support.constant.math", - "support.constant.dom", - "support.constant.json", - "entity.other.inherited-class", - "punctuation.separator.namespace.ruby" - ], - "settings": { - "foreground": "var(--syntax-type)" - } - }, - { - "scope": [ - "keyword.control", - "source.cpp keyword.operator.new", - "keyword.operator.delete", - "keyword.other.using", - "keyword.other.directive.using", - "keyword.other.operator", - "entity.name.operator" - ], - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": [ - "variable", - "meta.definition.variable.name", - "support.variable", - "entity.name.variable", - "constant.other.placeholder" - ], - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": ["variable.other.constant", "variable.other.enummember"], - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": ["meta.object-literal.key"], - "settings": { - "foreground": "var(--syntax-variable)" - } - }, - { - "scope": [ - "support.constant.property-value", - "support.constant.font-name", - "support.constant.media-type", - "support.constant.media", - "constant.other.color.rgb-value", - "constant.other.rgb-value", - "support.constant.color" - ], - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": [ - "punctuation.definition.group.regexp", - "punctuation.definition.group.assertion.regexp", - "punctuation.definition.character-class.regexp", - "punctuation.character.set.begin.regexp", - "punctuation.character.set.end.regexp", - "keyword.operator.negation.regexp", - "support.other.parenthesis.regexp" - ], - "settings": { - "foreground": "var(--syntax-string)" - } - }, - { - "scope": [ - "constant.character.character-class.regexp", - "constant.other.character-class.set.regexp", - "constant.other.character-class.regexp", - "constant.character.set.regexp" - ], - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"], - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": "keyword.operator.quantifier.regexp", - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": ["constant.character", "constant.other.option"], - "settings": { - "foreground": "var(--syntax-keyword)" - } - }, - { - "scope": "constant.character.escape", - "settings": { - "foreground": "var(--syntax-operator)" - } - }, - { - "scope": "entity.name.label", - "settings": { - "foreground": "var(--text-weak)" - } - } - ], - "type": "dark" -} diff --git a/packages/desktop/src/index.tsx b/packages/desktop/src/index.tsx index 9d402138d..63d96ae84 100644 --- a/packages/desktop/src/index.tsx +++ b/packages/desktop/src/index.tsx @@ -3,7 +3,7 @@ import "@/index.css" import { render } from "solid-js/web" import { Router, Route } from "@solidjs/router" import { MetaProvider } from "@solidjs/meta" -import { Fonts, ShikiProvider, MarkedProvider } from "@opencode-ai/ui" +import { Fonts, MarkedProvider } from "@opencode-ai/ui" import { SDKProvider } from "./context/sdk" import { SyncProvider } from "./context/sync" import { LocalProvider } from "./context/local" @@ -29,24 +29,22 @@ if (import.meta.env.DEV && !(root instanceof HTMLElement)) { render( () => ( - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + ), root!, ) diff --git a/packages/ui/src/components/code.tsx b/packages/ui/src/components/code.tsx index 6b95e6653..06541fe4e 100644 --- a/packages/ui/src/components/code.tsx +++ b/packages/ui/src/components/code.tsx @@ -14,8 +14,7 @@ export function Code(props: CodeProps) { createEffect(() => { const instance = new File({ - theme: { dark: "oc-1-dark", light: "oc-1-light" }, - // theme: { dark: "pierre-dark", light: "pierre-light" }, + theme: "OpenCode", overflow: "wrap", // or 'scroll' themeType: "system", // 'system', 'light', or 'dark' disableFileHeader: true, diff --git a/packages/ui/src/components/diff.tsx b/packages/ui/src/components/diff.tsx index 09085b44c..21ff980c1 100644 --- a/packages/ui/src/components/diff.tsx +++ b/packages/ui/src/components/diff.tsx @@ -55,9 +55,7 @@ export function Diff(props: DiffProps) { // annotations and a container element to hold the diff createEffect(() => { const instance = new FileDiff({ - // theme: "pierre-light", - theme: { dark: "oc-1-dark", light: "oc-1-light" }, - // theme: { dark: "pierre-dark", light: "pierre-light" }, + theme: "OpenCode", // When using the 'themes' prop, 'themeType' allows you to force 'dark' // or 'light' theme, or inherit from the OS ('system') theme. themeType: "system", @@ -181,394 +179,377 @@ export function Diff(props: DiffProps) { ) } -const colors = { - "editor.background": "transparent", - "editor.foreground": "var(--text-base)", - "gitDecoration.addedResourceForeground": "var(--syntax-diff-add)", - "gitDecoration.deletedResourceForeground": "var(--syntax-diff-delete)", - // "gitDecoration.conflictingResourceForeground": "#ffca00", - // "gitDecoration.modifiedResourceForeground": "#1a76d4", - // "gitDecoration.untrackedResourceForeground": "#00cab1", - // "gitDecoration.ignoredResourceForeground": "#84848A", - // "terminal.titleForeground": "#adadb1", - // "terminal.titleInactiveForeground": "#84848A", - // "terminal.background": "#141415", - // "terminal.foreground": "#adadb1", - // "terminal.ansiBlack": "#141415", - // "terminal.ansiRed": "#ff2e3f", - // "terminal.ansiGreen": "#0dbe4e", - // "terminal.ansiYellow": "#ffca00", - // "terminal.ansiBlue": "#008cff", - // "terminal.ansiMagenta": "#c635e4", - // "terminal.ansiCyan": "#08c0ef", - // "terminal.ansiWhite": "#c6c6c8", - // "terminal.ansiBrightBlack": "#141415", - // "terminal.ansiBrightRed": "#ff2e3f", - // "terminal.ansiBrightGreen": "#0dbe4e", - // "terminal.ansiBrightYellow": "#ffca00", - // "terminal.ansiBrightBlue": "#008cff", - // "terminal.ansiBrightMagenta": "#c635e4", - // "terminal.ansiBrightCyan": "#08c0ef", - // "terminal.ansiBrightWhite": "#c6c6c8", -} - -const tokenColors = [ - { - scope: ["comment", "punctuation.definition.comment", "string.comment"], - settings: { - foreground: "var(--syntax-comment)", - }, - }, - { - scope: ["entity.other.attribute-name"], - settings: { - foreground: "var(--syntax-property)", // maybe attribute - }, - }, - { - scope: [ - "constant", - "entity.name.constant", - "variable.other.constant", - "variable.language", - "entity", - ], - settings: { - foreground: "var(--syntax-constant)", - }, - }, - { - scope: ["entity.name", "meta.export.default", "meta.definition.variable"], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: [ - "variable.parameter.function", - "meta.jsx.children", - "meta.block", - "meta.tag.attributes", - "entity.name.constant", - "meta.object.member", - "meta.embedded.expression", - "meta.template.expression", - "string.other.begin.yaml", - "string.other.end.yaml", - ], - settings: { - foreground: "var(--syntax-punctuation)", - }, - }, - { - scope: ["entity.name.function", "support.type.primitive"], - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: ["support.class.component"], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: "keyword", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: [ - "keyword.operator", - "storage.type.function.arrow", - "punctuation.separator.key-value.css", - "entity.name.tag.yaml", - "punctuation.separator.key-value.mapping.yaml", - ], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: ["storage", "storage.type"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["storage.modifier.package", "storage.modifier.import", "storage.type.java"], - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: [ - "string", - "punctuation.definition.string", - "string punctuation.section.embedded source", - "entity.name.tag", - ], - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "support", - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: [ - "support.type.object.module", - "variable.other.object", - "support.type.property-name.css", - ], - settings: { - foreground: "var(--syntax-object)", - }, - }, - { - scope: "meta.property-name", - settings: { - foreground: "var(--syntax-property)", - }, - }, - { - scope: "variable", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "variable.other", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: [ - "invalid.broken", - "invalid.illegal", - "invalid.unimplemented", - "invalid.deprecated", - "message.error", - "markup.deleted", - "meta.diff.header.from-file", - "punctuation.definition.deleted", - "brackethighlighter.unmatched", - "token.error-token", - ], - settings: { - foreground: "var(--syntax-critical)", - }, - }, - { - scope: "carriage-return", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "string source", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "string variable", - settings: { - foreground: "var(--syntax-constant)", - }, - }, - { - scope: [ - "source.regexp", - "string.regexp", - "string.regexp.character-class", - "string.regexp constant.character.escape", - "string.regexp source.ruby.embedded", - "string.regexp string.regexp.arbitrary-repitition", - "string.regexp constant.character.escape", - ], - settings: { - foreground: "var(--syntax-regexp)", - }, - }, - { - scope: "support.constant", - settings: { - foreground: "var(--syntax-primitive)", - }, - }, - { - scope: "support.variable", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "meta.module-reference", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "punctuation.definition.list.begin.markdown", - settings: { - foreground: "var(--syntax-punctuation)", - }, - }, - { - scope: ["markup.heading", "markup.heading entity.name"], - settings: { - fontStyle: "bold", - foreground: "var(--syntax-info)", - }, - }, - { - scope: "markup.quote", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "markup.italic", - settings: { - fontStyle: "italic", - // foreground: "", - }, - }, - { - scope: "markup.bold", - settings: { - fontStyle: "bold", - foreground: "var(--text-strong)", - }, - }, - { - scope: [ - "markup.raw", - "markup.inserted", - "meta.diff.header.to-file", - "punctuation.definition.inserted", - "markup.changed", - "punctuation.definition.changed", - "markup.ignored", - "markup.untracked", - ], - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: "meta.diff.range", - settings: { - fontStyle: "bold", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.diff.header", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.separator", - settings: { - fontStyle: "bold", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.output", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "meta.export.default", - settings: { - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: [ - "brackethighlighter.tag", - "brackethighlighter.curly", - "brackethighlighter.round", - "brackethighlighter.square", - "brackethighlighter.angle", - "brackethighlighter.quote", +registerCustomTheme("OpenCode", () => { + return Promise.resolve({ + name: "OpenCode", + colors: { + "editor.background": "transparent", + "editor.foreground": "var(--text-base)", + "gitDecoration.addedResourceForeground": "var(--syntax-diff-add)", + "gitDecoration.deletedResourceForeground": "var(--syntax-diff-delete)", + // "gitDecoration.conflictingResourceForeground": "#ffca00", + // "gitDecoration.modifiedResourceForeground": "#1a76d4", + // "gitDecoration.untrackedResourceForeground": "#00cab1", + // "gitDecoration.ignoredResourceForeground": "#84848A", + // "terminal.titleForeground": "#adadb1", + // "terminal.titleInactiveForeground": "#84848A", + // "terminal.background": "#141415", + // "terminal.foreground": "#adadb1", + // "terminal.ansiBlack": "#141415", + // "terminal.ansiRed": "#ff2e3f", + // "terminal.ansiGreen": "#0dbe4e", + // "terminal.ansiYellow": "#ffca00", + // "terminal.ansiBlue": "#008cff", + // "terminal.ansiMagenta": "#c635e4", + // "terminal.ansiCyan": "#08c0ef", + // "terminal.ansiWhite": "#c6c6c8", + // "terminal.ansiBrightBlack": "#141415", + // "terminal.ansiBrightRed": "#ff2e3f", + // "terminal.ansiBrightGreen": "#0dbe4e", + // "terminal.ansiBrightYellow": "#ffca00", + // "terminal.ansiBrightBlue": "#008cff", + // "terminal.ansiBrightMagenta": "#c635e4", + // "terminal.ansiBrightCyan": "#08c0ef", + // "terminal.ansiBrightWhite": "#c6c6c8", + }, + tokenColors: [ + { + scope: ["comment", "punctuation.definition.comment", "string.comment"], + settings: { + foreground: "var(--syntax-comment)", + }, + }, + { + scope: ["entity.other.attribute-name"], + settings: { + foreground: "var(--syntax-property)", // maybe attribute + }, + }, + { + scope: [ + "constant", + "entity.name.constant", + "variable.other.constant", + "variable.language", + "entity", + ], + settings: { + foreground: "var(--syntax-constant)", + }, + }, + { + scope: ["entity.name", "meta.export.default", "meta.definition.variable"], + settings: { + foreground: "var(--syntax-type)", + }, + }, + { + scope: [ + "variable.parameter.function", + "meta.jsx.children", + "meta.block", + "meta.tag.attributes", + "entity.name.constant", + "meta.object.member", + "meta.embedded.expression", + "meta.template.expression", + "string.other.begin.yaml", + "string.other.end.yaml", + ], + settings: { + foreground: "var(--syntax-punctuation)", + }, + }, + { + scope: ["entity.name.function", "support.type.primitive"], + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: ["support.class.component"], + settings: { + foreground: "var(--syntax-type)", + }, + }, + { + scope: "keyword", + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: [ + "keyword.operator", + "storage.type.function.arrow", + "punctuation.separator.key-value.css", + "entity.name.tag.yaml", + "punctuation.separator.key-value.mapping.yaml", + ], + settings: { + foreground: "var(--syntax-operator)", + }, + }, + { + scope: ["storage", "storage.type"], + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: ["storage.modifier.package", "storage.modifier.import", "storage.type.java"], + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: [ + "string", + "punctuation.definition.string", + "string punctuation.section.embedded source", + "entity.name.tag", + ], + settings: { + foreground: "var(--syntax-string)", + }, + }, + { + scope: "support", + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: [ + "support.type.object.module", + "variable.other.object", + "support.type.property-name.css", + ], + settings: { + foreground: "var(--syntax-object)", + }, + }, + { + scope: "meta.property-name", + settings: { + foreground: "var(--syntax-property)", + }, + }, + { + scope: "variable", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "variable.other", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: [ + "invalid.broken", + "invalid.illegal", + "invalid.unimplemented", + "invalid.deprecated", + "message.error", + "markup.deleted", + "meta.diff.header.from-file", + "punctuation.definition.deleted", + "brackethighlighter.unmatched", + "token.error-token", + ], + settings: { + foreground: "var(--syntax-critical)", + }, + }, + { + scope: "carriage-return", + settings: { + foreground: "var(--syntax-keyword)", + }, + }, + { + scope: "string source", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "string variable", + settings: { + foreground: "var(--syntax-constant)", + }, + }, + { + scope: [ + "source.regexp", + "string.regexp", + "string.regexp.character-class", + "string.regexp constant.character.escape", + "string.regexp source.ruby.embedded", + "string.regexp string.regexp.arbitrary-repitition", + "string.regexp constant.character.escape", + ], + settings: { + foreground: "var(--syntax-regexp)", + }, + }, + { + scope: "support.constant", + settings: { + foreground: "var(--syntax-primitive)", + }, + }, + { + scope: "support.variable", + settings: { + foreground: "var(--syntax-variable)", + }, + }, + { + scope: "meta.module-reference", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "punctuation.definition.list.begin.markdown", + settings: { + foreground: "var(--syntax-punctuation)", + }, + }, + { + scope: ["markup.heading", "markup.heading entity.name"], + settings: { + fontStyle: "bold", + foreground: "var(--syntax-info)", + }, + }, + { + scope: "markup.quote", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "markup.italic", + settings: { + fontStyle: "italic", + // foreground: "", + }, + }, + { + scope: "markup.bold", + settings: { + fontStyle: "bold", + foreground: "var(--text-strong)", + }, + }, + { + scope: [ + "markup.raw", + "markup.inserted", + "meta.diff.header.to-file", + "punctuation.definition.inserted", + "markup.changed", + "punctuation.definition.changed", + "markup.ignored", + "markup.untracked", + ], + settings: { + foreground: "var(--text-base)", + }, + }, + { + scope: "meta.diff.range", + settings: { + fontStyle: "bold", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.diff.header", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.separator", + settings: { + fontStyle: "bold", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.output", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "meta.export.default", + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: [ + "brackethighlighter.tag", + "brackethighlighter.curly", + "brackethighlighter.round", + "brackethighlighter.square", + "brackethighlighter.angle", + "brackethighlighter.quote", + ], + settings: { + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: ["constant.other.reference.link", "string.other.link"], + settings: { + fontStyle: "underline", + foreground: "var(--syntax-unknown)", + }, + }, + { + scope: "token.info-token", + settings: { + foreground: "var(--syntax-info)", + }, + }, + { + scope: "token.warn-token", + settings: { + foreground: "var(--syntax-warning)", + }, + }, + { + scope: "token.debug-token", + settings: { + foreground: "var(--syntax-info)", + }, + }, ], - settings: { - foreground: "var(--syntax-unknown)", + semanticTokenColors: { + comment: "var(--syntax-comment)", + string: "var(--syntax-string)", + number: "var(--syntax-constant)", + regexp: "var(--syntax-regexp)", + keyword: "var(--syntax-keyword)", + variable: "var(--syntax-variable)", + parameter: "var(--syntax-variable)", + property: "var(--syntax-property)", + function: "var(--syntax-primitive)", + method: "var(--syntax-primitive)", + type: "var(--syntax-type)", + class: "var(--syntax-type)", + namespace: "var(--syntax-type)", + enumMember: "var(--syntax-primitive)", + "variable.constant": "var(--syntax-constant)", + "variable.defaultLibrary": "var(--syntax-unknown)", }, - }, - { - scope: ["constant.other.reference.link", "string.other.link"], - settings: { - fontStyle: "underline", - foreground: "var(--syntax-unknown)", - }, - }, - { - scope: "token.info-token", - settings: { - foreground: "var(--syntax-info)", - }, - }, - { - scope: "token.warn-token", - settings: { - foreground: "var(--syntax-warning)", - }, - }, - { - scope: "token.debug-token", - settings: { - foreground: "var(--syntax-info)", - }, - }, -] - -const semanticTokenColors = { - comment: "var(--syntax-comment)", - string: "var(--syntax-string)", - number: "var(--syntax-constant)", - regexp: "var(--syntax-regexp)", - keyword: "var(--syntax-keyword)", - variable: "var(--syntax-variable)", - parameter: "var(--syntax-variable)", - property: "var(--syntax-property)", - function: "var(--syntax-primitive)", - method: "var(--syntax-primitive)", - type: "var(--syntax-type)", - class: "var(--syntax-type)", - namespace: "var(--syntax-type)", - enumMember: "var(--syntax-primitive)", - "variable.constant": "var(--syntax-constant)", - "variable.defaultLibrary": "var(--syntax-unknown)", -} - -registerCustomTheme("oc-1-light", () => { - return Promise.resolve({ - type: "light", - name: "oc-1-light", - colors, - tokenColors, - semanticTokenColors, - } as unknown as ThemeRegistrationResolved) -}) - -registerCustomTheme("oc-1-dark", () => { - return Promise.resolve({ - name: "oc-1-dark", - type: "dark", - colors, - tokenColors, - semanticTokenColors, } as unknown as ThemeRegistrationResolved) }) diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index cd2d4caa9..ebc897a1f 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -24,5 +24,4 @@ export * from "./tooltip" export * from "./typewriter" export * from "../context/helper" -export * from "../context/shiki" export * from "../context/marked" diff --git a/packages/ui/src/context/marked.tsx b/packages/ui/src/context/marked.tsx index 18ce4280a..804d449c5 100644 --- a/packages/ui/src/context/marked.tsx +++ b/packages/ui/src/context/marked.tsx @@ -1,14 +1,14 @@ import { marked } from "marked" import markedShiki from "marked-shiki" import { bundledLanguages, type BundledLanguage } from "shiki" - import { createSimpleContext } from "./helper" -import { useShiki } from "./shiki" +import { getSharedHighlighter } from "@pierre/precision-diffs" + +const highlighter = await getSharedHighlighter({ themes: ["OpenCode"], langs: [] }) export const { use: useMarked, provider: MarkedProvider } = createSimpleContext({ name: "Marked", init: () => { - const highlighter = useShiki() return marked.use( markedShiki({ async highlight(code, lang) { @@ -20,7 +20,7 @@ export const { use: useMarked, provider: MarkedProvider } = createSimpleContext( } return highlighter.codeToHtml(code, { lang: lang || "text", - theme: "opencode", + theme: "OpenCode", tabindex: false, }) }, diff --git a/packages/ui/src/context/shiki.tsx b/packages/ui/src/context/shiki.tsx deleted file mode 100644 index d33b98ab7..000000000 --- a/packages/ui/src/context/shiki.tsx +++ /dev/null @@ -1,577 +0,0 @@ -import { createSimpleContext } from "./helper" -import { createHighlighter, type ThemeInput } from "shiki" - -const theme: ThemeInput = { - colors: { - "actionBar.toggledBackground": "var(--surface-raised-base)", - "activityBarBadge.background": "var(--surface-brand-base)", - "checkbox.border": "var(--border-base)", - "editor.background": "transparent", - "editor.foreground": "var(--text-base)", - "editor.inactiveSelectionBackground": "var(--surface-raised-base)", - "editor.selectionHighlightBackground": "var(--border-active)", - "editorIndentGuide.activeBackground1": "var(--border-weak-base)", - "editorIndentGuide.background1": "var(--border-weak-base)", - "input.placeholderForeground": "var(--text-weak)", - "list.activeSelectionIconForeground": "var(--text-base)", - "list.dropBackground": "var(--surface-raised-base)", - "menu.background": "var(--surface-base)", - "menu.border": "var(--border-base)", - "menu.foreground": "var(--text-base)", - "menu.selectionBackground": "var(--surface-interactive-base)", - "menu.separatorBackground": "var(--border-base)", - "ports.iconRunningProcessForeground": "var(--icon-success-base)", - "sideBarSectionHeader.background": "transparent", - "sideBarSectionHeader.border": "var(--border-weak-base)", - "sideBarTitle.foreground": "var(--text-weak)", - "statusBarItem.remoteBackground": "var(--surface-success-base)", - "statusBarItem.remoteForeground": "var(--text-base)", - "tab.lastPinnedBorder": "var(--border-weak-base)", - "tab.selectedBackground": "var(--surface-raised-base)", - "tab.selectedForeground": "var(--text-weak)", - "terminal.inactiveSelectionBackground": "var(--surface-raised-base)", - "widget.border": "var(--border-base)", - }, - displayName: "opencode", - name: "opencode", - semanticHighlighting: true, - semanticTokenColors: { - customLiteral: "var(--syntax-function)", - newOperator: "var(--syntax-operator)", - numberLiteral: "var(--syntax-number)", - stringLiteral: "var(--syntax-string)", - }, - tokenColors: [ - { - scope: [ - "meta.embedded", - "source.groovy.embedded", - "string meta.image.inline.markdown", - "variable.legacy.builtin.python", - ], - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: "emphasis", - settings: { - fontStyle: "italic", - }, - }, - { - scope: "strong", - settings: { - fontStyle: "bold", - }, - }, - { - scope: "header", - settings: { - foreground: "var(--markdown-heading)", - }, - }, - { - scope: "comment", - settings: { - foreground: "var(--syntax-comment)", - }, - }, - { - scope: "constant.language", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: [ - "constant.numeric", - "variable.other.enummember", - "keyword.operator.plus.exponent", - "keyword.operator.minus.exponent", - ], - settings: { - foreground: "var(--syntax-number)", - }, - }, - { - scope: "constant.regexp", - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: "entity.name.tag", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["entity.name.tag.css", "entity.name.tag.less"], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: "entity.other.attribute-name", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: [ - "entity.other.attribute-name.class.css", - "source.css entity.other.attribute-name.class", - "entity.other.attribute-name.id.css", - "entity.other.attribute-name.parent-selector.css", - "entity.other.attribute-name.parent.less", - "source.css entity.other.attribute-name.pseudo-class", - "entity.other.attribute-name.pseudo-element.css", - "source.css.less entity.other.attribute-name.id", - "entity.other.attribute-name.scss", - ], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: "invalid", - settings: { - foreground: "var(--syntax-critical)", - }, - }, - { - scope: "markup.underline", - settings: { - fontStyle: "underline", - }, - }, - { - scope: "markup.bold", - settings: { - fontStyle: "bold", - foreground: "var(--markdown-strong)", - }, - }, - { - scope: "markup.heading", - settings: { - fontStyle: "bold", - foreground: "var(--theme-markdown-heading)", - }, - }, - { - scope: "markup.italic", - settings: { - fontStyle: "italic", - }, - }, - { - scope: "markup.strikethrough", - settings: { - fontStyle: "strikethrough", - }, - }, - { - scope: "markup.inserted", - settings: { - foreground: "var(--text-diff-add-base)", - }, - }, - { - scope: "markup.deleted", - settings: { - foreground: "var(--text-diff-delete-base)", - }, - }, - { - scope: "markup.changed", - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: "punctuation.definition.quote.begin.markdown", - settings: { - foreground: "var(--markdown-block-quote)", - }, - }, - { - scope: "punctuation.definition.list.begin.markdown", - settings: { - foreground: "var(--markdown-list-enumeration)", - }, - }, - { - scope: "markup.inline.raw", - settings: { - foreground: "var(--markdown-code)", - }, - }, - { - scope: "punctuation.definition.tag", - settings: { - foreground: "var(--syntax-punctuation)", - }, - }, - { - scope: ["meta.preprocessor", "entity.name.function.preprocessor"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "meta.preprocessor.string", - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "meta.preprocessor.numeric", - settings: { - foreground: "var(--syntax-number)", - }, - }, - { - scope: "meta.structure.dictionary.key.python", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "meta.diff.header", - settings: { - foreground: "var(--text-weak)", - }, - }, - { - scope: "storage", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "storage.type", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["storage.modifier", "keyword.operator.noexcept"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["string", "meta.embedded.assembly"], - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "string.tag", - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "string.value", - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: "string.regexp", - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: [ - "punctuation.definition.template-expression.begin", - "punctuation.definition.template-expression.end", - "punctuation.section.embedded", - ], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: ["meta.template.expression"], - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: [ - "support.type.vendored.property-name", - "support.type.property-name", - "source.css variable", - "source.coffee.embedded", - ], - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "keyword", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "keyword.control", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "keyword.operator", - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: [ - "keyword.operator.new", - "keyword.operator.expression", - "keyword.operator.cast", - "keyword.operator.sizeof", - "keyword.operator.alignof", - "keyword.operator.typeid", - "keyword.operator.alignas", - "keyword.operator.instanceof", - "keyword.operator.logical.python", - "keyword.operator.wordlike", - ], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "keyword.other.unit", - settings: { - foreground: "var(--syntax-number)", - }, - }, - { - scope: ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "support.function.git-rebase", - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: "constant.sha.git-rebase", - settings: { - foreground: "var(--syntax-number)", - }, - }, - { - scope: [ - "storage.modifier.import.java", - "variable.language.wildcard.java", - "storage.modifier.package.java", - ], - settings: { - foreground: "var(--text-base)", - }, - }, - { - scope: "variable.language", - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: [ - "entity.name.function", - "support.function", - "support.constant.handlebars", - "source.powershell variable.other.member", - "entity.name.operator.custom-literal", - ], - settings: { - foreground: "var(--syntax-function)", - }, - }, - { - scope: [ - "support.class", - "support.type", - "entity.name.type", - "entity.name.namespace", - "entity.other.attribute", - "entity.name.scope-resolution", - "entity.name.class", - "storage.type.numeric.go", - "storage.type.byte.go", - "storage.type.boolean.go", - "storage.type.string.go", - "storage.type.uintptr.go", - "storage.type.error.go", - "storage.type.rune.go", - "storage.type.cs", - "storage.type.generic.cs", - "storage.type.modifier.cs", - "storage.type.variable.cs", - "storage.type.annotation.java", - "storage.type.generic.java", - "storage.type.java", - "storage.type.object.array.java", - "storage.type.primitive.array.java", - "storage.type.primitive.java", - "storage.type.token.java", - "storage.type.groovy", - "storage.type.annotation.groovy", - "storage.type.parameters.groovy", - "storage.type.generic.groovy", - "storage.type.object.array.groovy", - "storage.type.primitive.array.groovy", - "storage.type.primitive.groovy", - ], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: [ - "meta.type.cast.expr", - "meta.type.new.expr", - "support.constant.math", - "support.constant.dom", - "support.constant.json", - "entity.other.inherited-class", - "punctuation.separator.namespace.ruby", - ], - settings: { - foreground: "var(--syntax-type)", - }, - }, - { - scope: [ - "keyword.control", - "source.cpp keyword.operator.new", - "keyword.operator.delete", - "keyword.other.using", - "keyword.other.directive.using", - "keyword.other.operator", - "entity.name.operator", - ], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: [ - "variable", - "meta.definition.variable.name", - "support.variable", - "entity.name.variable", - "constant.other.placeholder", - ], - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: ["variable.other.constant", "variable.other.enummember"], - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: ["meta.object-literal.key"], - settings: { - foreground: "var(--syntax-variable)", - }, - }, - { - scope: [ - "support.constant.property-value", - "support.constant.font-name", - "support.constant.media-type", - "support.constant.media", - "constant.other.color.rgb-value", - "constant.other.rgb-value", - "support.constant.color", - ], - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: [ - "punctuation.definition.group.regexp", - "punctuation.definition.group.assertion.regexp", - "punctuation.definition.character-class.regexp", - "punctuation.character.set.begin.regexp", - "punctuation.character.set.end.regexp", - "keyword.operator.negation.regexp", - "support.other.parenthesis.regexp", - ], - settings: { - foreground: "var(--syntax-string)", - }, - }, - { - scope: [ - "constant.character.character-class.regexp", - "constant.other.character-class.set.regexp", - "constant.other.character-class.regexp", - "constant.character.set.regexp", - ], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"], - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: "keyword.operator.quantifier.regexp", - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: ["constant.character", "constant.other.option"], - settings: { - foreground: "var(--syntax-keyword)", - }, - }, - { - scope: "constant.character.escape", - settings: { - foreground: "var(--syntax-operator)", - }, - }, - { - scope: "entity.name.label", - settings: { - foreground: "var(--text-weak)", - }, - }, - ], - type: "dark", -} - -const highlighter = await createHighlighter({ - themes: [theme], - langs: [], -}) - -export const { use: useShiki, provider: ShikiProvider } = createSimpleContext({ - name: "Shiki", - init: () => { - return highlighter - }, -}) -- cgit v1.2.3 From c5a558f3dad12fd41d65d68eac9d774d4c4bd8d4 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:34:41 -0600 Subject: chore(desktop): remove dead code --- packages/desktop/src/components/code.tsx | 846 ------------------------------- 1 file changed, 846 deletions(-) delete mode 100644 packages/desktop/src/components/code.tsx (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/components/code.tsx b/packages/desktop/src/components/code.tsx deleted file mode 100644 index bbf7e28aa..000000000 --- a/packages/desktop/src/components/code.tsx +++ /dev/null @@ -1,846 +0,0 @@ -import { bundledLanguages, type BundledLanguage, type ShikiTransformer } from "shiki" -import { splitProps, type ComponentProps, createEffect, onMount, onCleanup, createMemo, createResource } from "solid-js" -import { useLocal, type TextSelection } from "@/context/local" -import { getFileExtension, getNodeOffsetInLine, getSelectionInContainer } from "@/utils" -import { useShiki } from "@opencode-ai/ui" - -type DefinedSelection = Exclude - -interface Props extends ComponentProps<"div"> { - code: string - path: string -} - -export function Code(props: Props) { - const ctx = useLocal() - const highlighter = useShiki() - const [local, others] = splitProps(props, ["class", "classList", "code", "path"]) - const lang = createMemo(() => { - const ext = getFileExtension(local.path) - if (ext in bundledLanguages) return ext - return "text" - }) - - let container: HTMLDivElement | undefined - let isProgrammaticSelection = false - - const ranges = createMemo(() => { - const items = ctx.context.all() as Array<{ type: "file"; path: string; selection?: DefinedSelection }> - const result: DefinedSelection[] = [] - for (const item of items) { - if (item.path !== local.path) continue - const selection = item.selection - if (!selection) continue - result.push(selection) - } - return result - }) - - const createLineNumberTransformer = (selections: DefinedSelection[]): ShikiTransformer => { - const highlighted = new Set() - for (const selection of selections) { - const startLine = selection.startLine - const endLine = selection.endLine - const start = Math.max(1, Math.min(startLine, endLine)) - const end = Math.max(start, Math.max(startLine, endLine)) - const count = end - start + 1 - if (count <= 0) continue - const values = Array.from({ length: count }, (_, index) => start + index) - for (const value of values) highlighted.add(value) - } - return { - name: "line-number-highlight", - line(node, index) { - if (!highlighted.has(index)) return - this.addClassToHast(node, "line-number-highlight") - const children = node.children - if (!Array.isArray(children)) return - for (const child of children) { - if (!child || typeof child !== "object") continue - const element = child as { type?: string; properties?: { className?: string[] } } - if (element.type !== "element") continue - const className = element.properties?.className - if (!Array.isArray(className)) continue - const matches = className.includes("diff-oldln") || className.includes("diff-newln") - if (!matches) continue - if (className.includes("line-number-highlight")) continue - className.push("line-number-highlight") - } - }, - } - } - - const [html] = createResource( - () => ranges(), - async (activeRanges) => { - if (!highlighter.getLoadedLanguages().includes(lang())) { - await highlighter.loadLanguage(lang() as BundledLanguage) - } - return highlighter.codeToHtml(local.code || "", { - lang: lang() && lang() in bundledLanguages ? lang() : "text", - theme: "opencode", - transformers: [transformerUnifiedDiff(), transformerDiffGroups(), createLineNumberTransformer(activeRanges)], - }) as string - }, - ) - - onMount(() => { - if (!container) return - - let ticking = false - const onScroll = () => { - if (!container) return - // if (ctx.file.active()?.path !== local.path) return - if (ticking) return - ticking = true - requestAnimationFrame(() => { - ticking = false - ctx.file.scroll(local.path, container!.scrollTop) - }) - } - - const onSelectionChange = async () => { - if (!container) return - if (isProgrammaticSelection) return - // if (ctx.file.active()?.path !== local.path) return - const d = getSelectionInContainer(container) - if (!d) return - const p = (await ctx.file.node(local.path))?.selection - if (p && p.startLine === d.sl && p.endLine === d.el && p.startChar === d.sch && p.endChar === d.ech) return - ctx.file.select(local.path, { startLine: d.sl, startChar: d.sch, endLine: d.el, endChar: d.ech }) - } - - const MOD = typeof navigator === "object" && /(Mac|iPod|iPhone|iPad)/.test(navigator.platform) ? "Meta" : "Control" - const onKeyDown = (e: KeyboardEvent) => { - // if (ctx.file.active()?.path !== local.path) return - const ae = document.activeElement as HTMLElement | undefined - const tag = (ae?.tagName || "").toLowerCase() - const inputFocused = !!ae && (tag === "input" || tag === "textarea" || ae.isContentEditable) - if (inputFocused) return - if (e.getModifierState(MOD) && e.key.toLowerCase() === "a") { - e.preventDefault() - if (!container) return - const element = container.querySelector("code") as HTMLElement | undefined - if (!element) return - const lines = Array.from(element.querySelectorAll(".line")) - if (!lines.length) return - const r = document.createRange() - const last = lines[lines.length - 1] - r.selectNodeContents(last) - const lastLen = r.toString().length - ctx.file.select(local.path, { startLine: 1, startChar: 0, endLine: lines.length, endChar: lastLen }) - } - } - - container.addEventListener("scroll", onScroll) - document.addEventListener("selectionchange", onSelectionChange) - document.addEventListener("keydown", onKeyDown) - - onCleanup(() => { - container?.removeEventListener("scroll", onScroll) - document.removeEventListener("selectionchange", onSelectionChange) - document.removeEventListener("keydown", onKeyDown) - }) - }) - - // Restore scroll position from store when content is ready - createEffect(async () => { - const content = html() - if (!container || !content) return - const top = (await ctx.file.node(local.path))?.scrollTop - if (top !== undefined && container.scrollTop !== top) container.scrollTop = top - }) - - // Sync selection from store -> DOM - createEffect(async () => { - const content = html() - if (!container || !content) return - // if (ctx.file.active()?.path !== local.path) return - const codeEl = container.querySelector("code") as HTMLElement | undefined - if (!codeEl) return - const target = (await ctx.file.node(local.path))?.selection - const current = getSelectionInContainer(container) - const sel = window.getSelection() - if (!sel) return - if (!target) { - if (current) { - isProgrammaticSelection = true - sel.removeAllRanges() - queueMicrotask(() => { - isProgrammaticSelection = false - }) - } - return - } - const matches = !!( - current && - current.sl === target.startLine && - current.sch === target.startChar && - current.el === target.endLine && - current.ech === target.endChar - ) - if (matches) return - const lines = Array.from(codeEl.querySelectorAll(".line")) - if (lines.length === 0) return - let sIdx = Math.max(0, target.startLine - 1) - let eIdx = Math.max(0, target.endLine - 1) - let sChar = Math.max(0, target.startChar || 0) - let eChar = Math.max(0, target.endChar || 0) - if (sIdx > eIdx || (sIdx === eIdx && sChar > eChar)) { - const ti = sIdx - sIdx = eIdx - eIdx = ti - const tc = sChar - sChar = eChar - eChar = tc - } - if (eChar === 0 && eIdx > sIdx) { - eIdx = eIdx - 1 - eChar = Number.POSITIVE_INFINITY - } - if (sIdx >= lines.length) return - if (eIdx >= lines.length) eIdx = lines.length - 1 - const s = getNodeOffsetInLine(lines[sIdx], sChar) ?? { node: lines[sIdx], offset: 0 } - const e = getNodeOffsetInLine(lines[eIdx], eChar) ?? { node: lines[eIdx], offset: lines[eIdx].childNodes.length } - const range = document.createRange() - range.setStart(s.node, s.offset) - range.setEnd(e.node, e.offset) - isProgrammaticSelection = true - sel.removeAllRanges() - sel.addRange(range) - queueMicrotask(() => { - isProgrammaticSelection = false - }) - }) - - // Build/toggle split layout and apply folding (both unified and split) - createEffect(() => { - const content = html() - if (!container || !content) return - const view = ctx.file.view(local.path) - - const pres = Array.from(container.querySelectorAll("pre")) - if (pres.length === 0) return - const originalPre = pres[0] - - const split = container.querySelector(".diff-split") - if (view === "diff-split") { - applySplitDiff(container) - const next = container.querySelector(".diff-split") - if (next) next.style.display = "" - originalPre.style.display = "none" - } else { - if (split) split.style.display = "none" - originalPre.style.display = "" - } - - const expanded = ctx.file.folded(local.path) - if (view === "diff-split") { - const left = container.querySelector(".diff-split pre:nth-child(1) code") - const right = container.querySelector(".diff-split pre:nth-child(2) code") - if (left) - applyDiffFolding(left, 3, { expanded, onExpand: (key) => ctx.file.unfold(local.path, key), side: "left" }) - if (right) - applyDiffFolding(right, 3, { expanded, onExpand: (key) => ctx.file.unfold(local.path, key), side: "right" }) - } else { - const code = container.querySelector("pre code") - if (code) - applyDiffFolding(code, 3, { - expanded, - onExpand: (key) => ctx.file.unfold(local.path, key), - }) - } - }) - - // Highlight groups + scroll coupling - const clearHighlights = () => { - if (!container) return - container.querySelectorAll(".diff-selected").forEach((el) => el.classList.remove("diff-selected")) - } - - const applyHighlight = (idx: number, scroll?: boolean) => { - if (!container) return - const view = ctx.file.view(local.path) - if (view === "raw") return - - clearHighlights() - - const nodes: HTMLElement[] = [] - if (view === "diff-split") { - const left = container.querySelector(".diff-split pre:nth-child(1) code") - const right = container.querySelector(".diff-split pre:nth-child(2) code") - if (left) - nodes.push(...Array.from(left.querySelectorAll(`[data-chgrp="${idx}"][data-diff="remove"]`))) - if (right) - nodes.push(...Array.from(right.querySelectorAll(`[data-chgrp="${idx}"][data-diff="add"]`))) - } else { - const code = container.querySelector("pre code") - if (code) nodes.push(...Array.from(code.querySelectorAll(`[data-chgrp="${idx}"]`))) - } - - for (const n of nodes) n.classList.add("diff-selected") - if (scroll && nodes.length) nodes[0].scrollIntoView({ block: "center", behavior: "smooth" }) - } - - const countGroups = () => { - if (!container) return 0 - const code = container.querySelector("pre code") - if (!code) return 0 - const set = new Set() - for (const el of Array.from(code.querySelectorAll(".diff-line[data-chgrp]"))) { - const v = el.getAttribute("data-chgrp") - if (v != undefined) set.add(v) - } - return set.size - } - - let lastIdx: number | undefined = undefined - let lastView: string | undefined - let lastContent: string | undefined - let lastRawIdx: number | undefined = undefined - createEffect(() => { - const content = html() - if (!container || !content) return - const view = ctx.file.view(local.path) - const raw = ctx.file.changeIndex(local.path) - if (raw === undefined) return - const total = countGroups() - if (total <= 0) return - const next = ((raw % total) + total) % total - - const navigated = lastRawIdx !== undefined && lastRawIdx !== raw - - if (next !== raw) { - ctx.file.setChangeIndex(local.path, next) - applyHighlight(next, true) - } else { - if (lastView !== view || lastContent !== content) applyHighlight(next) - if ((lastIdx !== undefined && lastIdx !== next) || navigated) applyHighlight(next, true) - } - - lastRawIdx = raw - lastIdx = next - lastView = view - lastContent = content - }) - - return ( -
    { - container = el - }} - innerHTML={html()} - class=" - font-mono text-xs tracking-wide overflow-y-auto h-full - [&]:[counter-reset:line] - [&_pre]:focus-visible:outline-none - [&_pre]:overflow-x-auto [&_pre]:no-scrollbar - [&_code]:min-w-full [&_code]:inline-block - [&_.tab]:relative - [&_.tab::before]:content['⇥'] - [&_.tab::before]:absolute - [&_.tab::before]:opacity-0 - [&_.space]:relative - [&_.space::before]:content-['·'] - [&_.space::before]:absolute - [&_.space::before]:opacity-0 - [&_.line]:inline-block [&_.line]:w-full - [&_.line]:hover:bg-background-element - [&_.line::before]:sticky [&_.line::before]:left-0 - [&_.line::before]:w-12 [&_.line::before]:pr-4 - [&_.line::before]:z-10 - [&_.line::before]:bg-background-panel - [&_.line::before]:text-text-muted/60 - [&_.line::before]:text-right [&_.line::before]:inline-block - [&_.line::before]:select-none - [&_.line::before]:[counter-increment:line] - [&_.line::before]:content-[counter(line)] - [&_.line-number-highlight]:bg-accent/20 - [&_.line-number-highlight::before]:bg-accent/40! - [&_.line-number-highlight::before]:text-background-panel! - [&_code.code-diff_.line::before]:content-[''] - [&_code.code-diff_.line::before]:w-0 - [&_code.code-diff_.line::before]:pr-0 - [&_.diff-split_code.code-diff::before]:w-10 - [&_.diff-split_.diff-newln]:left-0 - [&_.diff-oldln]:sticky [&_.diff-oldln]:left-0 - [&_.diff-oldln]:w-10 [&_.diff-oldln]:pr-2 - [&_.diff-oldln]:z-40 - [&_.diff-oldln]:text-text-muted/60 - [&_.diff-oldln]:text-right [&_.diff-oldln]:inline-block - [&_.diff-oldln]:select-none - [&_.diff-oldln]:bg-background-panel - [&_.diff-newln]:sticky [&_.diff-newln]:left-10 - [&_.diff-newln]:w-10 [&_.diff-newln]:pr-2 - [&_.diff-newln]:z-40 - [&_.diff-newln]:text-text-muted/60 - [&_.diff-newln]:text-right [&_.diff-newln]:inline-block - [&_.diff-newln]:select-none - [&_.diff-newln]:bg-background-panel - [&_.diff-add]:bg-success/20! - [&_.diff-add.diff-selected]:bg-success/50! - [&_.diff-add_.diff-oldln]:bg-success! - [&_.diff-add_.diff-oldln]:text-background-panel! - [&_.diff-add_.diff-newln]:bg-success! - [&_.diff-add_.diff-newln]:text-background-panel! - [&_.diff-remove]:bg-error/20! - [&_.diff-remove.diff-selected]:bg-error/50! - [&_.diff-remove_.diff-newln]:bg-error! - [&_.diff-remove_.diff-newln]:text-background-panel! - [&_.diff-remove_.diff-oldln]:bg-error! - [&_.diff-remove_.diff-oldln]:text-background-panel! - [&_.diff-sign]:inline-block [&_.diff-sign]:px-2 [&_.diff-sign]:select-none - [&_.diff-blank]:bg-background-element - [&_.diff-blank_.diff-oldln]:bg-background-element - [&_.diff-blank_.diff-newln]:bg-background-element - [&_.diff-collapsed]:block! [&_.diff-collapsed]:w-full [&_.diff-collapsed]:relative - [&_.diff-collapsed]:select-none - [&_.diff-collapsed]:bg-info/20 [&_.diff-collapsed]:hover:bg-info/40! - [&_.diff-collapsed]:text-info/80 [&_.diff-collapsed]:hover:text-info - [&_.diff-collapsed]:text-xs - [&_.diff-collapsed_.diff-oldln]:bg-info! - [&_.diff-collapsed_.diff-newln]:bg-info! - " - classList={{ - ...(local.classList || {}), - [local.class ?? ""]: !!local.class, - }} - {...others} - >
    - ) -} - -function transformerUnifiedDiff(): ShikiTransformer { - const kinds = new Map() - const meta = new Map() - let isDiff = false - - return { - name: "unified-diff", - preprocess(input) { - kinds.clear() - meta.clear() - isDiff = false - - const ls = input.split(/\r?\n/) - const out: Array = [] - let oldNo = 0 - let newNo = 0 - let inHunk = false - - for (let i = 0; i < ls.length; i++) { - const s = ls[i] - - const m = s.match(/^@@\s*-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s*@@/) - if (m) { - isDiff = true - inHunk = true - oldNo = parseInt(m[1], 10) - newNo = parseInt(m[3], 10) - continue - } - - if ( - /^diff --git /.test(s) || - /^Index: /.test(s) || - /^--- /.test(s) || - /^\+\+\+ /.test(s) || - /^[=]{3,}$/.test(s) || - /^\*{3,}$/.test(s) || - /^\\ No newline at end of file$/.test(s) - ) { - isDiff = true - continue - } - - if (!inHunk) { - out.push(s) - continue - } - - if (/^\+/.test(s)) { - out.push(s) - const ln = out.length - kinds.set(ln, "add") - meta.set(ln, { new: newNo, sign: "+" }) - newNo++ - continue - } - - if (/^-/.test(s)) { - out.push(s) - const ln = out.length - kinds.set(ln, "remove") - meta.set(ln, { old: oldNo, sign: "-" }) - oldNo++ - continue - } - - if (/^ /.test(s)) { - out.push(s) - const ln = out.length - kinds.set(ln, "context") - meta.set(ln, { old: oldNo, new: newNo }) - oldNo++ - newNo++ - continue - } - - // fallback in hunks - out.push(s) - } - - return out.join("\n").trimEnd() - }, - code(node) { - if (isDiff) this.addClassToHast(node, "code-diff") - }, - pre(node) { - if (isDiff) this.addClassToHast(node, "code-diff") - }, - line(node, line) { - if (!isDiff) return - const kind = kinds.get(line) - if (!kind) return - - const m = meta.get(line) || {} - - this.addClassToHast(node, "diff-line") - this.addClassToHast(node, `diff-${kind}`) - node.properties = node.properties || {} - ;(node.properties as any)["data-diff"] = kind - if (m.old != undefined) (node.properties as any)["data-old"] = String(m.old) - if (m.new != undefined) (node.properties as any)["data-new"] = String(m.new) - - const oldSpan = { - type: "element", - tagName: "span", - properties: { className: ["diff-oldln"] }, - children: [{ type: "text", value: m.old != undefined ? String(m.old) : " " }], - } - const newSpan = { - type: "element", - tagName: "span", - properties: { className: ["diff-newln"] }, - children: [{ type: "text", value: m.new != undefined ? String(m.new) : " " }], - } - - if (kind === "add" || kind === "remove" || kind === "context") { - const first = (node.children && (node.children as any[])[0]) as any - if (first && first.type === "element" && first.children && first.children.length > 0) { - const t = first.children[0] - if (t && t.type === "text" && typeof t.value === "string" && t.value.length > 0) { - const ch = t.value[0] - if (ch === "+" || ch === "-" || ch === " ") t.value = t.value.slice(1) - } - } - } - - const signSpan = { - type: "element", - tagName: "span", - properties: { className: ["diff-sign"] }, - children: [{ type: "text", value: (m as any).sign || " " }], - } - - // @ts-expect-error hast typing across versions - node.children = [oldSpan, newSpan, signSpan, ...(node.children || [])] - }, - } -} - -function transformerDiffGroups(): ShikiTransformer { - let group = -1 - let inGroup = false - return { - name: "diff-groups", - pre() { - group = -1 - inGroup = false - }, - line(node) { - const props = (node.properties || {}) as any - const kind = props["data-diff"] as string | undefined - if (kind === "add" || kind === "remove") { - if (!inGroup) { - group += 1 - inGroup = true - } - ;(node.properties as any)["data-chgrp"] = String(group) - } else { - inGroup = false - } - }, - } -} - -function applyDiffFolding( - root: HTMLElement, - context = 3, - options?: { expanded?: string[]; onExpand?: (key: string) => void; side?: "left" | "right" }, -) { - if (!root.classList.contains("code-diff")) return - - // Cleanup: unwrap previous collapsed blocks and remove toggles - const blocks = Array.from(root.querySelectorAll(".diff-collapsed-block")) - for (const block of blocks) { - const p = block.parentNode - if (!p) { - block.remove() - continue - } - while (block.firstChild) p.insertBefore(block.firstChild, block) - block.remove() - } - const toggles = Array.from(root.querySelectorAll(".diff-collapsed")) - for (const t of toggles) t.remove() - - const lines = Array.from(root.querySelectorAll(".diff-line")) - if (lines.length === 0) return - - const n = lines.length - const isChange = lines.map((l) => l.dataset["diff"] === "add" || l.dataset["diff"] === "remove") - const isContext = lines.map((l) => l.dataset["diff"] === "context") - if (!isChange.some(Boolean)) return - - const visible = new Array(n).fill(false) as boolean[] - for (let i = 0; i < n; i++) if (isChange[i]) visible[i] = true - for (let i = 0; i < n; i++) { - if (isChange[i]) { - const s = Math.max(0, i - context) - const e = Math.min(n - 1, i + context) - for (let j = s; j <= e; j++) if (isContext[j]) visible[j] = true - } - } - - type Range = { start: number; end: number } - const ranges: Range[] = [] - let i = 0 - while (i < n) { - if (!visible[i] && isContext[i]) { - let j = i - while (j + 1 < n && !visible[j + 1] && isContext[j + 1]) j++ - ranges.push({ start: i, end: j }) - i = j + 1 - } else { - i++ - } - } - - for (const r of ranges) { - const start = lines[r.start] - const end = lines[r.end] - const count = r.end - r.start + 1 - const minCollapse = 20 - if (count < minCollapse) { - continue - } - - // Wrap the entire collapsed chunk (including trailing newline) so it takes no space - const block = document.createElement("span") - block.className = "diff-collapsed-block" - start.parentElement?.insertBefore(block, start) - - let cur: Node | undefined = start - while (cur) { - const next: Node | undefined = cur.nextSibling || undefined - block.appendChild(cur) - if (cur === end) { - // Also move the newline after the last line into the block - if (next && next.nodeType === Node.TEXT_NODE && (next.textContent || "").startsWith("\n")) { - block.appendChild(next) - } - break - } - cur = next - } - - block.style.display = "none" - const row = document.createElement("span") - row.className = "line diff-collapsed" - row.setAttribute("data-kind", "collapsed") - row.setAttribute("data-count", String(count)) - row.setAttribute("tabindex", "0") - row.setAttribute("role", "button") - - const oldln = document.createElement("span") - oldln.className = "diff-oldln" - oldln.textContent = " " - - const newln = document.createElement("span") - newln.className = "diff-newln" - newln.textContent = " " - - const sign = document.createElement("span") - sign.className = "diff-sign" - sign.textContent = "…" - - const label = document.createElement("span") - label.textContent = `show ${count} unchanged line${count > 1 ? "s" : ""}` - - const key = `o${start.dataset["old"] || ""}-${end.dataset["old"] || ""}:n${start.dataset["new"] || ""}-${end.dataset["new"] || ""}` - - const show = (record = true) => { - if (record) options?.onExpand?.(key) - const p = block.parentNode - if (p) { - while (block.firstChild) p.insertBefore(block.firstChild, block) - block.remove() - } - row.remove() - } - - row.addEventListener("click", () => show(true)) - row.addEventListener("keydown", (ev) => { - if (ev.key === "Enter" || ev.key === " ") { - ev.preventDefault() - show(true) - } - }) - - block.parentElement?.insertBefore(row, block) - if (!options?.side || options.side === "left") row.appendChild(oldln) - if (!options?.side || options.side === "right") row.appendChild(newln) - row.appendChild(sign) - row.appendChild(label) - - if (options?.expanded && options.expanded.includes(key)) { - show(false) - } - } -} - -function applySplitDiff(container: HTMLElement) { - const pres = Array.from(container.querySelectorAll("pre")) - if (pres.length === 0) return - const originalPre = pres[0] - const originalCode = originalPre.querySelector("code") as HTMLElement | undefined - if (!originalCode || !originalCode.classList.contains("code-diff")) return - - // Rebuild split each time to match current content - const existing = container.querySelector(".diff-split") - if (existing) existing.remove() - - const grid = document.createElement("div") - grid.className = "diff-split grid grid-cols-2 gap-x-6" - - const makeColumn = () => { - const pre = document.createElement("pre") - pre.className = originalPre.className - const code = document.createElement("code") - code.className = originalCode.className - pre.appendChild(code) - return { pre, code } - } - - const left = makeColumn() - const right = makeColumn() - - // Helpers - const cloneSide = (line: HTMLElement, side: "old" | "new"): HTMLElement => { - const clone = line.cloneNode(true) as HTMLElement - const oldln = clone.querySelector(".diff-oldln") - const newln = clone.querySelector(".diff-newln") - if (side === "old") { - if (newln) newln.remove() - } else { - if (oldln) oldln.remove() - } - return clone - } - - const blankLine = (side: "old" | "new", kind: "add" | "remove"): HTMLElement => { - const span = document.createElement("span") - span.className = "line diff-line diff-blank" - span.setAttribute("data-diff", kind) - const ln = document.createElement("span") - ln.className = side === "old" ? "diff-oldln" : "diff-newln" - ln.textContent = " " - span.appendChild(ln) - return span - } - - const lines = Array.from(originalCode.querySelectorAll(".diff-line")) - let i = 0 - while (i < lines.length) { - const cur = lines[i] - const kind = cur.dataset["diff"] - - if (kind === "context") { - left.code.appendChild(cloneSide(cur, "old")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(cloneSide(cur, "new")) - right.code.appendChild(document.createTextNode("\n")) - i++ - continue - } - - if (kind === "remove") { - // Batch consecutive removes and following adds, then pair - const removes: HTMLElement[] = [] - const adds: HTMLElement[] = [] - let j = i - while (j < lines.length && lines[j].dataset["diff"] === "remove") { - removes.push(lines[j]) - j++ - } - let k = j - while (k < lines.length && lines[k].dataset["diff"] === "add") { - adds.push(lines[k]) - k++ - } - - const pairs = Math.min(removes.length, adds.length) - for (let p = 0; p < pairs; p++) { - left.code.appendChild(cloneSide(removes[p], "old")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(cloneSide(adds[p], "new")) - right.code.appendChild(document.createTextNode("\n")) - } - for (let p = pairs; p < removes.length; p++) { - left.code.appendChild(cloneSide(removes[p], "old")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(blankLine("new", "remove")) - right.code.appendChild(document.createTextNode("\n")) - } - for (let p = pairs; p < adds.length; p++) { - left.code.appendChild(blankLine("old", "add")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(cloneSide(adds[p], "new")) - right.code.appendChild(document.createTextNode("\n")) - } - - i = k - continue - } - - if (kind === "add") { - // Run of adds not preceded by removes - const adds: HTMLElement[] = [] - let j = i - while (j < lines.length && lines[j].dataset["diff"] === "add") { - adds.push(lines[j]) - j++ - } - for (let p = 0; p < adds.length; p++) { - left.code.appendChild(blankLine("old", "add")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(cloneSide(adds[p], "new")) - right.code.appendChild(document.createTextNode("\n")) - } - i = j - continue - } - - // Any other kind: mirror as context - left.code.appendChild(cloneSide(cur, "old")) - left.code.appendChild(document.createTextNode("\n")) - right.code.appendChild(cloneSide(cur, "new")) - right.code.appendChild(document.createTextNode("\n")) - i++ - } - - grid.appendChild(left.pre) - grid.appendChild(right.pre) - container.appendChild(grid) -} -- cgit v1.2.3 From 45fabec0919ba3268b6bd0b9566ec98cac0ed3d7 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:54:18 -0600 Subject: fix(desktop): prompt input on non-chat tabs --- packages/desktop/src/pages/session.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/pages/session.tsx b/packages/desktop/src/pages/session.tsx index 1c92941d0..23d44cfcb 100644 --- a/packages/desktop/src/pages/session.tsx +++ b/packages/desktop/src/pages/session.tsx @@ -829,6 +829,15 @@ export default function Page() {
    + +
    + { + inputRef = el + }} + /> +
    +
    -- cgit v1.2.3 From b46c3f2a26b06b2fe7459b082def635654756094 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Fri, 7 Nov 2025 13:54:49 -0600 Subject: fix(desktop): prompt input issues (wip) --- packages/desktop/src/components/prompt-input.tsx | 36 +++++++++++------------- 1 file changed, 16 insertions(+), 20 deletions(-) (limited to 'packages/desktop/src') diff --git a/packages/desktop/src/components/prompt-input.tsx b/packages/desktop/src/components/prompt-input.tsx index 15bc54c49..5ae56f827 100644 --- a/packages/desktop/src/components/prompt-input.tsx +++ b/packages/desktop/src/components/prompt-input.tsx @@ -174,10 +174,8 @@ export const PromptInput: Component = (props) => { const addPart = (part: ContentPart) => { const cursorPosition = getCursorPosition(editorRef) - const rawText = session.prompt - .current() - .map((p) => p.content) - .join("") + const prompt = session.prompt.current() + const rawText = prompt.map((p) => p.content).join("") const textBeforeCursor = rawText.substring(0, cursorPosition) const atMatch = textBeforeCursor.match(/@(\S*)$/) @@ -203,7 +201,7 @@ export const PromptInput: Component = (props) => { parts: nextParts, inserted, cursorPositionAfter, - } = session.prompt.current().reduce( + } = prompt.reduce( (acc, item) => { if (acc.inserted) { acc.parts.push({ ...item, start: acc.runningIndex, end: acc.runningIndex + item.content.length }) @@ -262,7 +260,7 @@ export const PromptInput: Component = (props) => { ) if (!inserted) { - const baseParts = session.prompt.current().filter((item) => !(item.type === "text" && item.content === "")) + const baseParts = prompt.filter((item) => !(item.type === "text" && item.content === "")) const runningIndex = baseParts.reduce((sum, p) => sum + p.content.length, 0) const appendedAcc = { parts: [...baseParts] as ContentPart[], runningIndex } if (part.type === "text") { @@ -316,10 +314,8 @@ export const PromptInput: Component = (props) => { const handleSubmit = async (event: Event) => { event.preventDefault() - const text = session.prompt - .current() - .map((part) => part.content) - .join("") + const prompt = session.prompt.current() + const text = prompt.map((part) => part.content).join("") if (text.trim().length === 0) { if (session.working()) abort() return @@ -329,19 +325,17 @@ export const PromptInput: Component = (props) => { if (!existing) { const created = await sdk.client.session.create() existing = created.data ?? undefined + if (existing) navigate(`/session/${existing.id}`) } if (!existing) return - navigate(`/session/${existing.id}`) - if (!session.id) { - // session.layout.setOpenedTabs( - // session.layout.copyTabs("", session.id) - } - session.layout.setActiveTab(undefined) - session.messages.setActive(undefined) - const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path)) + // if (!session.id) { + // session.layout.setOpenedTabs( + // session.layout.copyTabs("", session.id) + // } - const attachments = session.prompt.current().filter((part) => part.type === "file") + const toAbsolutePath = (path: string) => (path.startsWith("/") ? path : sync.absolute(path)) + const attachments = prompt.filter((part) => part.type === "file") // const activeFile = local.context.active() // if (activeFile) { @@ -382,9 +376,11 @@ export const PromptInput: Component = (props) => { } }) + session.layout.setActiveTab(undefined) + session.messages.setActive(undefined) session.prompt.set(DEFAULT_PROMPT, 0) - await sdk.client.session.prompt({ + sdk.client.session.prompt({ path: { id: existing.id }, body: { agent: local.agent.current()!.name, -- cgit v1.2.3