From 77ae0b527e4b1a8277b0391d0acbc7d82b08e4ea Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 28 Oct 2025 12:15:46 -0500 Subject: wip: desktop work --- bun.lock | 38 +---- package.json | 2 +- packages/desktop/package.json | 2 +- .../desktop/src/components/assistant-message.tsx | 1 - packages/desktop/src/components/diff.tsx | 176 --------------------- packages/desktop/src/pages/index.tsx | 5 +- packages/ui/src/components/diff.css | 24 +++ packages/ui/src/components/diff.tsx | 172 ++++++++++++++++++++ packages/ui/src/components/index.ts | 1 + packages/ui/src/styles/colors.css | 76 ++------- packages/ui/src/styles/index.css | 1 + 11 files changed, 217 insertions(+), 281 deletions(-) delete mode 100644 packages/desktop/src/components/diff.tsx create mode 100644 packages/ui/src/components/diff.css create mode 100644 packages/ui/src/components/diff.tsx diff --git a/bun.lock b/bun.lock index 7e1fceab3..e976f47c4 100644 --- a/bun.lock +++ b/bun.lock @@ -344,7 +344,7 @@ "@hono/zod-validator": "0.4.2", "@kobalte/core": "0.13.11", "@openauthjs/openauth": "0.0.0-20250322224806", - "@pierre/precision-diffs": "0.3.2", + "@pierre/precision-diffs": "0.3.5", "@solidjs/meta": "0.29.4", "@tailwindcss/vite": "4.1.11", "@tsconfig/bun": "1.0.9", @@ -3498,8 +3498,6 @@ "@openauthjs/openauth/jose": ["jose@5.9.6", "", {}, "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="], - "@opencode-ai/ui/@pierre/precision-diffs": ["@pierre/precision-diffs@0.3.2", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/transformers": "3.13.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.13.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-HE+wFB0TV+wmjur/J+qI5PsRQl5RN6tCEFTusW0S5FDfZJUIpkxJCacqUxyEI0DriXMKhgGQ+oCQShfaFELdrQ=="], - "@opencode-ai/web/@shikijs/transformers": ["@shikijs/transformers@3.4.2", "", { "dependencies": { "@shikijs/core": "3.4.2", "@shikijs/types": "3.4.2" } }, "sha512-I5baLVi/ynLEOZoWSAMlACHNnG+yw5HDmse0oe+GW6U1u+ULdEB3UHiVWaHoJSSONV7tlcVxuaMy74sREDkSvg=="], "@opencode-ai/web/@types/luxon": ["@types/luxon@3.6.2", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="], @@ -3736,8 +3734,6 @@ "nypm/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], - "opencode/@pierre/precision-diffs": ["@pierre/precision-diffs@0.3.2", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/transformers": "3.13.0", "diff": "8.0.2", "fast-deep-equal": "3.1.3", "hast-util-to-html": "9.0.5", "shiki": "3.13.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-HE+wFB0TV+wmjur/J+qI5PsRQl5RN6tCEFTusW0S5FDfZJUIpkxJCacqUxyEI0DriXMKhgGQ+oCQShfaFELdrQ=="], - "opencode/ulid": ["ulid@3.0.1", "", { "bin": { "ulid": "dist/cli.js" } }, "sha512-dPJyqPzx8preQhqq24bBG1YNkvigm87K8kVEHCD+ruZg24t6IFEFv00xMWfxcC4djmFtiTLdFuADn4+DOz6R7Q=="], "opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.1", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="], @@ -4072,10 +4068,6 @@ "@octokit/request/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@26.0.0", "", {}, "sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA=="], - "@opencode-ai/ui/@pierre/precision-diffs/@shikijs/transformers": ["@shikijs/transformers@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0" } }, "sha512-833lcuVzcRiG+fXvgslWsM2f4gHpjEgui1ipIknSizRuTgMkNZupiXE5/TVJ6eSYfhNBFhBZKkReKWO2GgYmqA=="], - - "@opencode-ai/ui/@pierre/precision-diffs/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="], - "@opencode-ai/web/@shikijs/transformers/@shikijs/core": ["@shikijs/core@3.4.2", "", { "dependencies": { "@shikijs/types": "3.4.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-AG8vnSi1W2pbgR2B911EfGqtLE9c4hQBYkv/x7Z+Kt0VxhgQKcW7UNDVYsu9YxwV6u+OJrvdJrMq6DNWoBjihQ=="], "@opencode-ai/web/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.4.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-zHC1l7L+eQlDXLnxvM9R91Efh2V4+rN3oMVS2swCBssbj2U/FBwybD1eeLaq8yl/iwT+zih8iUbTBCgGZOYlVg=="], @@ -4242,10 +4234,6 @@ "nypm/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], - "opencode/@pierre/precision-diffs/@shikijs/transformers": ["@shikijs/transformers@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/types": "3.13.0" } }, "sha512-833lcuVzcRiG+fXvgslWsM2f4gHpjEgui1ipIknSizRuTgMkNZupiXE5/TVJ6eSYfhNBFhBZKkReKWO2GgYmqA=="], - - "opencode/@pierre/precision-diffs/shiki": ["shiki@3.13.0", "", { "dependencies": { "@shikijs/core": "3.13.0", "@shikijs/engine-javascript": "3.13.0", "@shikijs/engine-oniguruma": "3.13.0", "@shikijs/langs": "3.13.0", "@shikijs/themes": "3.13.0", "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-aZW4l8Og16CokuCLf8CF8kq+KK2yOygapU5m3+hoGw0Mdosc6fPitjM+ujYarppj5ZIKGyPDPP1vqmQhr+5/0g=="], - "opencontrol/@modelcontextprotocol/sdk/express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="], "opencontrol/@modelcontextprotocol/sdk/pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="], @@ -4426,18 +4414,6 @@ "@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], - "@opencode-ai/ui/@pierre/precision-diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="], - - "@opencode-ai/ui/@pierre/precision-diffs/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="], - - "@opencode-ai/ui/@pierre/precision-diffs/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="], - - "@opencode-ai/ui/@pierre/precision-diffs/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="], - - "@opencode-ai/ui/@pierre/precision-diffs/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="], - - "@opencode-ai/ui/@pierre/precision-diffs/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="], - "@solidjs/start/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@2.3.0", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="], "@vercel/nft/glob/path-scurry/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -4460,18 +4436,6 @@ "nitropack/serve-static/send/statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - "opencode/@pierre/precision-diffs/@shikijs/transformers/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="], - - "opencode/@pierre/precision-diffs/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-Ty7xv32XCp8u0eQt8rItpMs6rU9Ki6LJ1dQOW3V/56PKDcpvfHPnYFbsx5FFUP2Yim34m/UkazidamMNVR4vKg=="], - - "opencode/@pierre/precision-diffs/shiki/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-O42rBGr4UDSlhT2ZFMxqM7QzIU+IcpoTMzb3W7AlziI1ZF7R8eS2M0yt5Ry35nnnTX/LTLXFPUjRFCIW+Operg=="], - - "opencode/@pierre/precision-diffs/shiki/@shikijs/langs": ["@shikijs/langs@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-672c3WAETDYHwrRP0yLy3W1QYB89Hbpj+pO4KhxK6FzIrDI2FoEXNiNCut6BQmEApYLfuYfpgOZaqbY+E9b8wQ=="], - - "opencode/@pierre/precision-diffs/shiki/@shikijs/themes": ["@shikijs/themes@3.13.0", "", { "dependencies": { "@shikijs/types": "3.13.0" } }, "sha512-Vxw1Nm1/Od8jyA7QuAenaV78BG2nSr3/gCGdBkLpfLscddCkzkL36Q5b67SrLLfvAJTOUzW39x4FHVCFriPVgg=="], - - "opencode/@pierre/precision-diffs/shiki/@shikijs/types": ["@shikijs/types@3.13.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw=="], - "opencontrol/@modelcontextprotocol/sdk/express/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "opencontrol/@modelcontextprotocol/sdk/express/body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="], diff --git a/package.json b/package.json index a5e521797..151fa4d91 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@tsconfig/bun": "1.0.9", "@cloudflare/workers-types": "4.20251008.0", "@openauthjs/openauth": "0.0.0-20250322224806", - "@pierre/precision-diffs": "0.3.2", + "@pierre/precision-diffs": "0.3.5", "@solidjs/meta": "0.29.4", "@tailwindcss/vite": "4.1.11", "diff": "8.0.2", diff --git a/packages/desktop/package.json b/packages/desktop/package.json index a2184a75a..3861c49bd 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -26,7 +26,7 @@ "@kobalte/core": "catalog:", "@opencode-ai/sdk": "workspace:*", "@opencode-ai/ui": "workspace:*", - "@pierre/precision-diffs": "0.3.5", + "@pierre/precision-diffs": "catalog:", "@shikijs/transformers": "3.9.2", "@solid-primitives/active-element": "2.1.3", "@solid-primitives/event-bus": "1.1.2", diff --git a/packages/desktop/src/components/assistant-message.tsx b/packages/desktop/src/components/assistant-message.tsx index cfc9d1a49..738293405 100644 --- a/packages/desktop/src/components/assistant-message.tsx +++ b/packages/desktop/src/components/assistant-message.tsx @@ -394,7 +394,6 @@ ToolRegistry.register({ ToolRegistry.register({ name: "todowrite", render(props) { - console.log(props.input.todos) return ( = Omit, "themes"> & { - before: FileContents - after: FileContents - annotations?: DiffLineAnnotation[] - class?: string - classList?: ComponentProps<"div">["classList"] -} - -// registerCustomTheme("opencode", () => import("./theme.json")) - -// interface ThreadMetadata { -// threadId: string -// } - -export function Diff(props: DiffProps) { - let container!: HTMLDivElement - const [local, others] = splitProps(props, ["before", "after", "class", "classList", "annotations"]) - - // const lineAnnotations: DiffLineAnnotation[] = [ - // { - // side: "additions", - // // The line number specified for an annotation is the visual line number - // // you see in the number column of a diff - // lineNumber: 16, - // metadata: { threadId: "68b329da9893e34099c7d8ad5cb9c940" }, - // }, - // ] - - // If you ever want to update the options for an instance, simple call - // 'setOptions' with the new options. Bear in mind, this does NOT merge - // existing properties, it's a full replace - // instance.setOptions({ - // ...instance.options, - // theme: "pierre-dark", - // themes: undefined, - // }) - - // When ready to render, simply call .render with old/new file, optional - // annotations and a container element to hold the diff - createEffect(() => { - const instance = new FileDiff({ - theme: "pierre-light", - // Or can also provide a 'themes' prop, which allows the code to adapt - // to your OS light or dark theme - // themes: { dark: 'pierre-night', 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", - // Disable the line numbers for your diffs, generally not recommended - disableLineNumbers: false, - // Whether code should 'wrap' with long lines or 'scroll'. - overflow: "scroll", - // Normally you shouldn't need this prop, but if you don't provide a - // valid filename or your file doesn't have an extension you may want to - // override the automatic detection. You can specify that language here: - // https://shiki.style/languages - // lang?: SupportedLanguages; - // 'diffStyle' controls whether the diff is presented side by side or - // in a unified (single column) view - diffStyle: "unified", - // Line decorators to help highlight changes. - // 'bars' (default): - // Shows some red-ish or green-ish (theme dependent) bars on the left - // edge of relevant lines - // - // 'classic': - // shows '+' characters on additions and '-' characters on deletions - // - // 'none': - // No special diff indicators are shown - diffIndicators: "bars", - // By default green-ish or red-ish background are shown on added and - // deleted lines respectively. Disable that feature here - disableBackground: false, - // Diffs are split up into hunks, this setting customizes what to show - // between each hunk. - // - // 'line-info' (default): - // Shows a bar that tells you how many lines are collapsed. If you are - // using the oldFile/newFile API then you can click those bars to - // expand the content between them - // - // 'metadata': - // Shows the content you'd see in a normal patch file, usually in some - // format like '@@ -60,6 +60,22 @@'. You cannot use these to expand - // hidden content - // - // 'simple': - // Just a subtle bar separator between each hunk - hunkSeparators: "line-info", - // hunkSeparators(hunkData: HunkData) { - // const fragment = document.createDocumentFragment() - // const numCol = document.createElement("div") - // numCol.textContent = `${hunkData.lines}` - // numCol.style.position = "sticky" - // numCol.style.left = "0" - // numCol.style.backgroundColor = "var(--pjs-bg)" - // numCol.style.zIndex = "2" - // fragment.appendChild(numCol) - // const contentCol = document.createElement("div") - // contentCol.textContent = "unmodified lines" - // contentCol.style.position = "sticky" - // contentCol.style.width = "var(--pjs-column-content-width)" - // contentCol.style.left = "var(--pjs-column-number-width)" - // fragment.appendChild(contentCol) - // return fragment - // }, - // On lines that have both additions and deletions, we can run a - // separate diff check to mark parts of the lines that change. - // 'none': - // Do not show these secondary highlights - // - // 'char': - // Show changes at a per character granularity - // - // 'word': - // Show changes but rounded up to word boundaries - // - // 'word-alt' (default): - // Similar to 'word', however we attempt to minimize single character - // gaps between highlighted changes - lineDiffType: "word-alt", - // If lines exceed these character lengths then we won't perform the - // line lineDiffType check - maxLineDiffLength: 1000, - // If any line in the diff exceeds this value then we won't attempt to - // syntax highlight the diff - maxLineLengthForHighlighting: 1000, - // Enabling this property will hide the file header with file name and - // diff stats. - disableFileHeader: true, - // You can optionally pass a render function for rendering out line - // annotations. Just return the dom node to render - // renderAnnotation(annotation: DiffLineAnnotation): HTMLElement { - // // Despite the diff itself being rendered in the shadow dom, - // // annotations are inserted via the web components 'slots' api and you - // // can use all your normal normal css and styling for them - // const element = document.createElement("div") - // element.innerText = annotation.metadata.threadId - // return element - // }, - ...others, - }) - - instance.render({ - oldFile: local.before, - newFile: local.after, - lineAnnotations: local.annotations, - containerWrapper: container, - }) - }) - - return ( -
- ) -} diff --git a/packages/desktop/src/pages/index.tsx b/packages/desktop/src/pages/index.tsx index 42e96e32e..b4bd36162 100644 --- a/packages/desktop/src/pages/index.tsx +++ b/packages/desktop/src/pages/index.tsx @@ -1,4 +1,4 @@ -import { Button, List, SelectDialog, Tooltip, IconButton, Tabs, Icon, Accordion } from "@opencode-ai/ui" +import { Button, List, SelectDialog, Tooltip, IconButton, Tabs, Icon, Accordion, Diff } from "@opencode-ai/ui" import { FileIcon } from "@/ui" import FileTree from "@/components/file-tree" import { For, onCleanup, onMount, Show, Match, Switch, createSignal, createEffect, createMemo } from "solid-js" @@ -21,7 +21,6 @@ import type { JSX } from "solid-js" import { Code } from "@/components/code" import { useSync } from "@/context/sync" import { useSDK } from "@/context/sdk" -import { Diff } from "@/components/diff" import { ProgressCircle } from "@/components/progress-circle" import { AssistantMessage } from "@/components/assistant-message" import { type AssistantMessage as AssistantMessageType } from "@opencode-ai/sdk" @@ -655,7 +654,7 @@ export default function Page() {
-
+
{(message) => { const title = createMemo(() => message.summary?.title) diff --git a/packages/ui/src/components/diff.css b/packages/ui/src/components/diff.css new file mode 100644 index 000000000..6f9a0d73d --- /dev/null +++ b/packages/ui/src/components/diff.css @@ -0,0 +1,24 @@ +[data-component="diff"] { + [data-slot="diff-hunk-separator-line-number"] { + position: sticky; + left: 0; + background-color: hsla(209, 96%, 90%, 1); + z-index: 2; + display: flex; + align-items: center; + justify-content: center; + + [data-slot="diff-hunk-separator-line-number-icon"] { + aspect-ratio: 1; + width: 24px; + height: 24px; + } + } + [data-slot="diff-hunk-separator-content"] { + position: sticky; + background-color: hsla(210, 100%, 96%, 1); + width: var(--pjs-column-content-width); + left: var(--pjs-column-number-width); + padding-left: 8px; + } +} diff --git a/packages/ui/src/components/diff.tsx b/packages/ui/src/components/diff.tsx new file mode 100644 index 000000000..88215bcb0 --- /dev/null +++ b/packages/ui/src/components/diff.tsx @@ -0,0 +1,172 @@ +import { + type FileContents, + FileDiff, + type DiffLineAnnotation, + type HunkData, + DiffFileRendererOptions, + // registerCustomTheme, +} from "@pierre/precision-diffs" +import { ComponentProps, createEffect, splitProps } from "solid-js" + +export type DiffProps = Omit, "themes"> & { + before: FileContents + after: FileContents + annotations?: DiffLineAnnotation[] + class?: string + classList?: ComponentProps<"div">["classList"] +} + +// registerCustomTheme("opencode", () => import("./theme.json")) + +// interface ThreadMetadata { +// threadId: string +// } + +export function Diff(props: DiffProps) { + let container!: HTMLDivElement + const [local, others] = splitProps(props, ["before", "after", "class", "classList", "annotations"]) + + // const lineAnnotations: DiffLineAnnotation[] = [ + // { + // side: "additions", + // // The line number specified for an annotation is the visual line number + // // you see in the number column of a diff + // lineNumber: 16, + // metadata: { threadId: "68b329da9893e34099c7d8ad5cb9c940" }, + // }, + // ] + + // If you ever want to update the options for an instance, simple call + // 'setOptions' with the new options. Bear in mind, this does NOT merge + // existing properties, it's a full replace + // instance.setOptions({ + // ...instance.options, + // theme: "pierre-dark", + // themes: undefined, + // }) + + // When ready to render, simply call .render with old/new file, optional + // annotations and a container element to hold the diff + createEffect(() => { + const instance = new FileDiff({ + theme: "pierre-light", + // Or can also provide a 'themes' prop, which allows the code to adapt + // to your OS light or dark theme + // themes: { dark: 'pierre-night', 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", + // Disable the line numbers for your diffs, generally not recommended + disableLineNumbers: false, + // Whether code should 'wrap' with long lines or 'scroll'. + overflow: "scroll", + // Normally you shouldn't need this prop, but if you don't provide a + // valid filename or your file doesn't have an extension you may want to + // override the automatic detection. You can specify that language here: + // https://shiki.style/languages + // lang?: SupportedLanguages; + // 'diffStyle' controls whether the diff is presented side by side or + // in a unified (single column) view + diffStyle: "unified", + // Line decorators to help highlight changes. + // 'bars' (default): + // Shows some red-ish or green-ish (theme dependent) bars on the left + // edge of relevant lines + // + // 'classic': + // shows '+' characters on additions and '-' characters on deletions + // + // 'none': + // No special diff indicators are shown + diffIndicators: "bars", + // By default green-ish or red-ish background are shown on added and + // deleted lines respectively. Disable that feature here + disableBackground: false, + // Diffs are split up into hunks, this setting customizes what to show + // between each hunk. + // + // 'line-info' (default): + // Shows a bar that tells you how many lines are collapsed. If you are + // using the oldFile/newFile API then you can click those bars to + // expand the content between them + // + // 'metadata': + // Shows the content you'd see in a normal patch file, usually in some + // format like '@@ -60,6 +60,22 @@'. You cannot use these to expand + // hidden content + // + // 'simple': + // Just a subtle bar separator between each hunk + // hunkSeparators: "line-info", + hunkSeparators(hunkData: HunkData) { + const fragment = document.createDocumentFragment() + const numCol = document.createElement("div") + numCol.innerHTML = ` ` + numCol.dataset["slot"] = "diff-hunk-separator-line-number" + fragment.appendChild(numCol) + const contentCol = document.createElement("div") + contentCol.textContent = `${hunkData.lines} unmodified lines` + contentCol.dataset["slot"] = "diff-hunk-separator-content" + fragment.appendChild(contentCol) + return fragment + }, + // On lines that have both additions and deletions, we can run a + // separate diff check to mark parts of the lines that change. + // 'none': + // Do not show these secondary highlights + // + // 'char': + // Show changes at a per character granularity + // + // 'word': + // Show changes but rounded up to word boundaries + // + // 'word-alt' (default): + // Similar to 'word', however we attempt to minimize single character + // gaps between highlighted changes + lineDiffType: "word-alt", + // If lines exceed these character lengths then we won't perform the + // line lineDiffType check + maxLineDiffLength: 1000, + // If any line in the diff exceeds this value then we won't attempt to + // syntax highlight the diff + maxLineLengthForHighlighting: 1000, + // Enabling this property will hide the file header with file name and + // diff stats. + disableFileHeader: true, + // You can optionally pass a render function for rendering out line + // annotations. Just return the dom node to render + // renderAnnotation(annotation: DiffLineAnnotation): HTMLElement { + // // Despite the diff itself being rendered in the shadow dom, + // // annotations are inserted via the web components 'slots' api and you + // // can use all your normal normal css and styling for them + // const element = document.createElement("div") + // element.innerText = annotation.metadata.threadId + // return element + // }, + ...others, + }) + + instance.render({ + oldFile: local.before, + newFile: local.after, + lineAnnotations: local.annotations, + containerWrapper: container, + }) + }) + + return ( +
+ ) +} diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 0024c9e76..16cbb7d95 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -3,6 +3,7 @@ export * from "./button" export * from "./checkbox" export * from "./collapsible" export * from "./dialog" +export * from "./diff" export * from "./icon" export * from "./icon-button" export * from "./input" diff --git a/packages/ui/src/styles/colors.css b/packages/ui/src/styles/colors.css index b0ddf36b7..79877d3cf 100644 --- a/packages/ui/src/styles/colors.css +++ b/packages/ui/src/styles/colors.css @@ -1,41 +1,5 @@ :root { - --grey-dark-1: #ffffff; - --grey-dark-2: #ffffff; - --grey-dark-3: #ffffff; - --grey-dark-4: #ffffff; - --grey-dark-5: #ffffff; - --grey-dark-6: #ffffff; - --grey-dark-7: #ffffff; - --grey-dark-8: #ffffff; - --grey-dark-9: #ffffff; - --grey-dark-10: #ffffff; - --grey-dark-11: #ffffff; - --grey-dark-12: #ffffff; - --grey-light-1: #ffffff; - --grey-dark-alpha-1: #ffffff; - --grey-dark-alpha-2: #ffffff; - --grey-dark-alpha-3: #ffffff; - --grey-dark-alpha-4: #ffffff; - --grey-dark-alpha-5: #ffffff; - --grey-dark-alpha-6: #ffffff; - --grey-dark-alpha-7: #ffffff; - --grey-dark-alpha-8: #ffffff; - --grey-dark-alpha-9: #ffffff; - --grey-dark-alpha-10: #ffffff; - --grey-dark-alpha-11: #ffffff; - --grey-dark-alpha-12: #ffffff; --smoke-dark-1: #131010; - --grey-light-2: #ffffff; - --grey-light-3: #ffffff; - --grey-light-4: #ffffff; - --grey-light-5: #ffffff; - --grey-light-6: #ffffff; - --grey-light-7: #ffffff; - --grey-light-8: #ffffff; - --grey-light-9: #ffffff; - --grey-light-10: #ffffff; - --grey-light-11: #ffffff; - --grey-light-12: #ffffff; --smoke-dark-2: #1b1818; --smoke-dark-3: #252121; --smoke-dark-4: #2d2828; @@ -59,19 +23,19 @@ --smoke-light-10: #848181; --smoke-light-11: #656363; --smoke-light-12: #211e1e; - --smoke-dark-alpha-1: #bb000003; - --smoke-dark-alpha-2: #f9b4b40b; - --smoke-dark-alpha-3: #f9caca16; - --smoke-dark-alpha-4: #ffd5d51e; - --smoke-dark-alpha-5: #fce2e226; - --smoke-dark-alpha-6: #fce2e231; - --smoke-dark-alpha-7: #fce8e83f; - --smoke-dark-alpha-8: #fff1f159; - --smoke-dark-alpha-9: #fff3f067; - --smoke-dark-alpha-10: #fff2f276; - --smoke-dark-alpha-11: #fff7f7b2; + --smoke-dark-alpha-1: #82383803; + --smoke-dark-alpha-2: #e6c6c60b; + --smoke-dark-alpha-3: #edd5d516; + --smoke-dark-alpha-4: #f2e1e11e; + --smoke-dark-alpha-5: #f5e8e826; + --smoke-dark-alpha-6: #f5e8e831; + --smoke-dark-alpha-7: #f7ecec3f; + --smoke-dark-alpha-8: #faf5f559; + --smoke-dark-alpha-9: #faf5f467; + --smoke-dark-alpha-10: #fbf5f576; + --smoke-dark-alpha-11: #fcf9f9b2; --smoke-light-alpha-1: #55000003; - --smoke-dark-alpha-12: #fffafaf0; + --smoke-dark-alpha-12: #fdfbfbf0; --smoke-light-alpha-2: #25000007; --smoke-light-alpha-3: #1100000f; --smoke-light-alpha-4: #0c000017; @@ -149,8 +113,8 @@ --cobalt-light-4: #daeaff; --cobalt-light-5: #c8e0ff; --cobalt-light-6: #b4d2ff; - --cobalt-light-7: #98bfff; --cobalt-dark-alpha-1: #0011f211; + --cobalt-light-7: #98bfff; --cobalt-dark-alpha-2: #0048fe1c; --cobalt-dark-alpha-3: #004dff49; --cobalt-dark-alpha-4: #064dfd6b; @@ -161,8 +125,8 @@ --cobalt-dark-alpha-9: #034cff; --cobalt-dark-alpha-10: #003bffed; --cobalt-dark-alpha-11: #89b5ff; - --cobalt-dark-alpha-12: #cde2ff; --cobalt-light-8: #73a4ff; + --cobalt-dark-alpha-12: #cde2ff; --cobalt-light-9: #034cff; --cobalt-light-10: #0443de; --cobalt-light-11: #1251ec; @@ -481,16 +445,4 @@ --mint-light-alpha-10: #0cc7006c; --mint-light-alpha-11: #016800cf; --mint-light-alpha-12: #022e00e2; - --grey-light-alpha-1: #ffffff; - --grey-light-alpha-2: #ffffff; - --grey-light-alpha-3: #ffffff; - --grey-light-alpha-4: #ffffff; - --grey-light-alpha-5: #ffffff; - --grey-light-alpha-6: #ffffff; - --grey-light-alpha-7: #ffffff; - --grey-light-alpha-8: #ffffff; - --grey-light-alpha-9: #ffffff; - --grey-light-alpha-10: #ffffff; - --grey-light-alpha-11: #ffffff; - --grey-light-alpha-12: #ffffff; } diff --git a/packages/ui/src/styles/index.css b/packages/ui/src/styles/index.css index 7ae6b73e4..94fa894d4 100644 --- a/packages/ui/src/styles/index.css +++ b/packages/ui/src/styles/index.css @@ -8,6 +8,7 @@ @import "../components/accordion.css" layer(components); @import "../components/button.css" layer(components); @import "../components/checkbox.css" layer(components); +@import "../components/diff.css" layer(components); @import "../components/collapsible.css" layer(components); @import "../components/dialog.css" layer(components); @import "../components/icon.css" layer(components); -- cgit v1.2.3