From a4fd2444021f6a141ce711ec412ccecb315dbb2f Mon Sep 17 00:00:00 2001 From: Aaron Trowbridge Date: Wed, 8 Jul 2026 14:19:54 -0400 Subject: [PATCH 01/66] amicode: re-add Aero wordmark typeface (LICENSE-PENDING) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepend 'Aero' to the AMICODE wordmark/logo font-family chain (logo.tsx, wordmark-v2.tsx), matching each file's current fallback stack. Additive only. Re-applied fresh after the branch fast-forwarded 57 commits to current origin/local/amicode — this had been uncommitted WIP on the old base. Aero is donationware (Nirmal Biswas / Picatype, $25+) — bundled preview- only, must be licensed or replaced before any release. See AMICODE- PATCHES.md #15. --- AMICODE-PATCHES.md | 9 +++++++++ packages/app/public/assets/Aero-Regular.ttf | Bin 0 -> 44464 bytes packages/app/src/index.css | 15 ++++++++++++++- packages/ui/src/components/logo.tsx | 2 +- packages/ui/src/v2/components/wordmark-v2.tsx | 2 +- 5 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 packages/app/public/assets/Aero-Regular.ttf diff --git a/AMICODE-PATCHES.md b/AMICODE-PATCHES.md index 0834bb663..006c3ec01 100644 --- a/AMICODE-PATCHES.md +++ b/AMICODE-PATCHES.md @@ -225,6 +225,7 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build. - index.css: @font-face for both (JuliaMono full glyph set — Julia Unicode; Racing Sans One latin subset, font-display swap). logo.tsx + wordmark-v2.tsx: font-family 'Racing Sans One' first, weight 700→400. settings.tsx: monoDefault/monoFallback lead with JuliaMono. theme.css: --font-family-mono leads with JuliaMono. Terminal font DELIBERATELY unchanged (JetBrainsMono Nerd Font Mono via separate terminalFallback). - New assets (git-added — build breaks without them): public/assets/RacingSansOne-Regular.woff2 (21 KB) + JuliaMono-Regular.woff2 (946 KB). - Font build sha256: `2a15da111be516516fb1fbd1a4fb5ae02ad9bddd6373ede08cdf7b28c19d163a` (dist/opencode-local + vendored path, write-temp + mv -f swap; SUPERSEDES #13's a73d8583… — same code, fonts now committed). Verify (scratch port 14099): `GET /assets/RacingSansOne-Regular.woff2` → 200 font/woff2 21804 B; `GET /assets/JuliaMono-Regular.woff2` → 200 font/woff2 946516 B; "Racing Sans One" in built css + index/new-session chunks, "JuliaMono" in `index-Dwtxigfs.css`; `GET /amicode/problems` → 200; `GET /` → 200 `Amicode`; ui `bun test src` → 70 pass; typecheck green ui+app (no snapshots assert fonts, per Aaron — confirmed nothing went red). Bonus confirmation: KaTeX\_\* woff2 assets now in dist — the entity view's katex import (#13) pulls its font set into the embed. + 9. (home CTA fallback) — amicode(home): "Open chat" works on a fresh profile. - BUG: `startWithPrompt` (fork wiring for the Meet-Amico card, patch 5ef6b7e0e) dead-ended silently when the persisted client-side project list was empty (fresh browser profile @@ -245,3 +246,11 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build. reuses any server on port 3000 (`reuseExistingServer`) — run with `PLAYWRIGHT_PORT=` if something else (e.g. the harmoniqs website dev server) holds 3000. - Checks: `tsgo -b` clean; `bun run test:unit` 376 pass / 0 fail. + +15. (Aero wordmark, re-added) — amicode: AMICODE wordmark/logo font-family gains `'Aero'` ahead of the existing fallback chain, both call sites: logo.tsx (`'Aero', var(--font-family-sans, ...)`) and wordmark-v2.tsx (`'Aero', 'Racing Sans One', var(--font-family-mono, ...)`). Additive only — no fallback removed, no sizing/weight/spacing touched. This had been iterated as uncommitted working-tree WIP before the branch fast-forwarded 57 commits to current `origin/local/amicode` (e9b695191); re-applied fresh against the current font stacks (logo.tsx's had independently moved to a sans-serif stack + weight 750/letter-spacing 4 in the interim — preserved as-is, only the family list changed). + + > ⚠️ **LICENSE-PENDING — release blocker.** "Aero" by Nirmal Biswas (Picatype) is donationware, USD 25+ to nirmalbiswas@gmail.com. Bundled at `packages/app/public/assets/Aero-Regular.ttf` for PREVIEW ONLY; not locked as the official face. Before any release: pay the license and record proof here, or drop `'Aero'` from both `font-family` chains + remove its `@font-face` (index.css) + the asset. + + - index.css: new `@font-face` for "Aero" (truetype, weight 400, `font-display: swap`), inserted immediately before the existing Racing Sans One face; carries the license-pending comment inline. + - New asset (git-added): `public/assets/Aero-Regular.ttf` (44,464 B). + - Verify: `bun run typecheck` green in both `packages/ui` and `packages/app` (tsgo, no errors). Visual/glyph confirmation is manual-only (see build note under #16, which builds and vendors this alongside the paste fix). diff --git a/packages/app/public/assets/Aero-Regular.ttf b/packages/app/public/assets/Aero-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6b7b8e5ebd5d09a791b8ece4867c4a4f3f3f7c25 GIT binary patch literal 44464 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

wH|^K$CL_s{U%~vAz)Ni&c6VH?9hm! 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%0DE>YtjA_YWt^vcn~NQX<&P4 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 literal 0 HcmV?d00001 diff --git a/packages/app/src/index.css b/packages/app/src/index.css index 112af7528..6fb380f67 100644 --- a/packages/app/src/index.css +++ b/packages/app/src/index.css @@ -25,7 +25,20 @@ font-style: normal; } -/* amicode: brand wordmark typeface (AMICODE logo) */ +/* amicode: brand wordmark typeface (AMICODE logo). + LICENSE-PENDING: "Aero" by Nirmal Biswas (Picatype) is donationware — usage + requires a USD 25+ payment to nirmalbiswas@gmail.com. Bundled for PREVIEW ONLY + while the wordmark face is being chosen; MUST be licensed (or replaced) before + any release ships. Racing Sans One (OFL, commercial-clean) is kept as the + fallback / prior candidate. */ +@font-face { + font-family: "Aero"; + src: url("/assets/Aero-Regular.ttf") format("truetype"); + font-weight: 400; + font-style: normal; + font-display: swap; +} + @font-face { font-family: "Racing Sans One"; src: url("/assets/RacingSansOne-Regular.woff2") format("woff2"); diff --git a/packages/ui/src/components/logo.tsx b/packages/ui/src/components/logo.tsx index 8250cdad1..847c0e340 100644 --- a/packages/ui/src/components/logo.tsx +++ b/packages/ui/src/components/logo.tsx @@ -81,7 +81,7 @@ export const Logo = (props: { class?: string }) => { dominant-baseline="central" textLength="230" lengthAdjust="spacingAndGlyphs" - font-family="var(--font-family-sans, ui-sans-serif, system-ui, -apple-system, sans-serif)" + font-family="'Aero', var(--font-family-sans, ui-sans-serif, system-ui, -apple-system, sans-serif)" font-weight="750" letter-spacing="4" font-size="36" diff --git a/packages/ui/src/v2/components/wordmark-v2.tsx b/packages/ui/src/v2/components/wordmark-v2.tsx index 28f780122..8a2d5b0ab 100644 --- a/packages/ui/src/v2/components/wordmark-v2.tsx +++ b/packages/ui/src/v2/components/wordmark-v2.tsx @@ -19,7 +19,7 @@ export function WordmarkV2(props: Pick, "class">) { dominant-baseline="central" textLength="700" lengthAdjust="spacingAndGlyphs" - font-family="'Racing Sans One', var(--font-family-mono, ui-monospace, monospace)" + font-family="'Aero', 'Racing Sans One', var(--font-family-mono, ui-monospace, monospace)" font-weight="400" font-size="112" fill="currentColor" From c811798e96a9f0deacf7ae1b689ef1df198c3de1 Mon Sep 17 00:00:00 2001 From: Aaron Trowbridge Date: Wed, 8 Jul 2026 14:24:08 -0400 Subject: [PATCH 02/66] amicode: wire composer paste to the extension clipboard bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chat composer's handlePaste bailed out silently whenever event.clipboardData was empty — which it always is inside the sandboxed cross-origin webview iframe (navigator.clipboard is denied there too). Cmd/Ctrl+V into the composer was a no-op. The extension side of this bridge already ships on main (chat_panel.ts answers clipboard-request), and home-cards.tsx already proved the pattern for a different input. This wires the same bridge into the main composer: platform.readClipboardText (new, mirrors readClipboardImage) -> entry.tsx implements it via the postMessage bridge when framed -> attachments.ts falls back to it when clipboardData yields no text. See AMICODE-PATCHES.md #16 for the full trace + build/verify evidence. --- AMICODE-PATCHES.md | 10 +++++++ packages/app/src/components/prompt-input.tsx | 1 + .../components/prompt-input/attachments.ts | 23 +++++++++++----- packages/app/src/context/platform.tsx | 5 ++++ packages/app/src/entry.tsx | 26 +++++++++++++++++++ 5 files changed, 58 insertions(+), 7 deletions(-) diff --git a/AMICODE-PATCHES.md b/AMICODE-PATCHES.md index 006c3ec01..fa99ad582 100644 --- a/AMICODE-PATCHES.md +++ b/AMICODE-PATCHES.md @@ -254,3 +254,13 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build. - index.css: new `@font-face` for "Aero" (truetype, weight 400, `font-display: swap`), inserted immediately before the existing Racing Sans One face; carries the license-pending comment inline. - New asset (git-added): `public/assets/Aero-Regular.ttf` (44,464 B). - Verify: `bun run typecheck` green in both `packages/ui` and `packages/app` (tsgo, no errors). Visual/glyph confirmation is manual-only (see build note under #16, which builds and vendors this alongside the paste fix). + +16. (composer paste bridge) — amicode: wire the main chat composer's `handlePaste` (prompt-input/attachments.ts) to the extension-clipboard bridge that patch #14's PR already shipped for a different input (home-cards.tsx's `readClipboardViaBridge`/`pasteFallback`, commit `5a2f1dde44`) — that bridge answers `{source:"amicode", kind:"clipboard-request"}` on the extension side (`packages/extension/src/chat_panel.ts`, already on `main`), but nothing in the composer's own paste path ever sent it. Root cause matches home-cards.tsx's own comment verbatim: the chat iframe is sandboxed + cross-origin, so `navigator.clipboard` is denied and the native `paste` event's `clipboardData` arrives empty — Cmd/Ctrl+V into the composer was a silent no-op. + + - platform.tsx: new optional capability `readClipboardText?(): Promise`, alongside the existing `readClipboardImage?`. + - entry.tsx: implements it — no-op (`null`) when not framed (`window.parent === window`, i.e. plain browser/opencode.ai/desktop — zero behavior change there); when framed, posts `{source:"amicode", kind:"clipboard-request", nonce}` to `window.parent` and resolves on the matching `{kind:"clipboard", nonce}` reply or a 1500 ms timeout — same nonce/timeout/listener shape as the proven `readClipboardViaBridge`, just exposed as a platform capability instead of a component-local closure (this file already hosts the analogous `readClipboardImage`). + - attachments.ts: `handlePaste` no longer bails out when `event.clipboardData` is null (only when it's ALSO missing `readClipboardText`, i.e. non-amicode contexts are untouched); after the existing file/image-bridge attempts, falls back to `readClipboardText()` when there's still no plain text. + - prompt-input.tsx: threads `readClipboardText: platform.readClipboardText` into `createPromptAttachments`, mirroring `readClipboardImage`. + - No shared bridge module extracted — matches this codebase's existing precedent (home-cards.tsx's bridge is also component-local, not a shared util); two call sites isn't yet worth a shared abstraction. + - Tests: app `bun test` (66 files) → 376 pass / 0 fail, incl. the untouched `attachments.test.ts` (pure-function coverage only — `handlePaste` itself has no unit coverage in this codebase, matching home-cards.tsx's own bridge having none either; verification here is typecheck + build + the manual live-paste check below). Typecheck green in `packages/app`. + - Build sha256: `564a4cd783524e0f50541b716d9db4048b2e02430ebc77786a11a4504431c085` (vendored `linux-x64` binary, built via `fetch_opencode.mjs --local --any-ref` off commit `f971d3bd5d` — i.e. this build covers **#15 + #16 together**, same supersession pattern as #13→#14). Verify: smoke test `--version` → `1.17.3`; binary strings confirm both patches compiled in (`Aero` ×5, `clipboard-request` ×2, `readClipboardText` ×3); channel gate ON (`newLayoutDesigns` sentinel `=!0`). **Interactive Cmd/Ctrl+V-in-composer confirmation is live-VS-Code-only, NOT yet human-verified** — same deferred-to-dev-host-acceptance caveat as patches #12/#13. diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index 184c1c8aa..e99cc2bad 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1087,6 +1087,7 @@ export const PromptInput: Component = (props) => { }, addPart, readClipboardImage: platform.readClipboardImage, + readClipboardText: platform.readClipboardText, }) const fileAttachmentInput = () => ( diff --git a/packages/app/src/components/prompt-input/attachments.ts b/packages/app/src/components/prompt-input/attachments.ts index 2b1cd1826..8b5523ae5 100644 --- a/packages/app/src/components/prompt-input/attachments.ts +++ b/packages/app/src/components/prompt-input/attachments.ts @@ -32,6 +32,7 @@ type PromptAttachmentsInput = { focusEditor: () => void addPart: (part: ContentPart) => boolean readClipboardImage?: () => Promise + readClipboardText?: () => Promise } export function createPromptAttachments(input: PromptAttachmentsInput) { @@ -92,23 +93,25 @@ export function createPromptAttachments(input: PromptAttachmentsInput) { const handlePaste = async (event: ClipboardEvent) => { const clipboardData = event.clipboardData - if (!clipboardData) return + if (!clipboardData && !input.readClipboardText) return event.preventDefault() event.stopPropagation() - const files = Array.from(clipboardData.items).flatMap((item) => { - if (item.kind !== "file") return [] - const file = item.getAsFile() - return file ? [file] : [] - }) + const files = clipboardData + ? Array.from(clipboardData.items).flatMap((item) => { + if (item.kind !== "file") return [] + const file = item.getAsFile() + return file ? [file] : [] + }) + : [] if (files.length > 0) { await addAttachments(files) return } - const plainText = clipboardData.getData("text/plain") ?? "" + let plainText = clipboardData?.getData("text/plain") ?? "" // Desktop: Browser clipboard has no images and no text, try platform's native clipboard for images if (input.readClipboardImage && !plainText) { @@ -119,6 +122,12 @@ export function createPromptAttachments(input: PromptAttachmentsInput) { } } + // Amicode webview: the sandboxed cross-origin iframe gives us an empty + // clipboardData on paste — fall back to the extension-host bridge. + if (!plainText && input.readClipboardText) { + plainText = (await input.readClipboardText()) ?? "" + } + if (!plainText) return const text = normalizePaste(plainText) diff --git a/packages/app/src/context/platform.tsx b/packages/app/src/context/platform.tsx index f137c528d..ca39ca0b3 100644 --- a/packages/app/src/context/platform.tsx +++ b/packages/app/src/context/platform.tsx @@ -103,6 +103,11 @@ type PlatformBase = { /** Read image from clipboard (desktop only) */ readClipboardImage?(): Promise + /** Read text from clipboard via the extension-host bridge (amicode VS Code + * webview only) — a sandboxed cross-origin iframe can't reach + * navigator.clipboard or the native paste event's clipboardData */ + readClipboardText?(): Promise + /** Export collected diagnostic logs (desktop only) */ exportDebugLogs?(): Promise diff --git a/packages/app/src/entry.tsx b/packages/app/src/entry.tsx index c41c1bb4b..7df42cb0a 100644 --- a/packages/app/src/entry.tsx +++ b/packages/app/src/entry.tsx @@ -94,6 +94,31 @@ const restart: Platform["restart"] = async () => { window.location.reload() } +// Amicode webview: the chat iframe is sandboxed + cross-origin, so +// navigator.clipboard and the native paste event's clipboardData are both +// denied. Ask the extension host over the message bridge instead — it reads +// the OS clipboard and replies (same proven pattern as home-cards.tsx's +// readClipboardViaBridge). Framed contexts only; plain browser tabs use +// native paste and never call this. +const readClipboardText: Platform["readClipboardText"] = () => { + if (window.parent === window) return Promise.resolve(null) + return new Promise((resolve) => { + const nonce = Math.random().toString(36).slice(2) + const onMessage = (event: MessageEvent) => { + const data = event.data as { source?: string; kind?: string; nonce?: string; text?: string } | undefined + if (data?.source !== "amicode" || data.kind !== "clipboard" || data.nonce !== nonce) return + window.removeEventListener("message", onMessage) + resolve(typeof data.text === "string" ? data.text : null) + } + window.addEventListener("message", onMessage) + window.parent.postMessage({ source: "amicode", kind: "clipboard-request", nonce }, "*") + setTimeout(() => { + window.removeEventListener("message", onMessage) + resolve(null) + }, 1500) + }) +} + const root = document.getElementById("root") if (!(root instanceof HTMLElement) && import.meta.env.DEV) { throw new Error(getRootNotFoundError()) @@ -132,6 +157,7 @@ const platform: Platform = { return stored ? ServerConnection.Key.make(stored) : null }, setDefaultServer: writeDefaultServerUrl, + readClipboardText, } if (import.meta.env.VITE_SENTRY_DSN) { From b868a590261dd74dc0cd40144cb6cafefdcd821f Mon Sep 17 00:00:00 2001 From: Aaron Trowbridge Date: Wed, 8 Jul 2026 14:50:15 -0400 Subject: [PATCH 03/66] amicode: generalize the paste-clipboard bridge past the composer Live test (Aaron) found the prior commit's fix was composer-only: pasting into a generic "Connect " API-key dialog (dialog-connect- provider.tsx - one of many such plain-input settings forms) was still a silent no-op, since that input has no knowledge of the bridge. Same root cause, generalized properly: one document-level paste listener (installGlobalPasteFallback, entry.tsx) covers every form field and contenteditable in the app, activating only when the native paste event gave no text (the sandboxed-iframe symptom) - a no-op everywhere clipboardData already works. The composer's own handler stopPropagation()s on every paste it handles, so it never double-fires there. See AMICODE-PATCHES.md #16 addendum for the full trace + build evidence. --- AMICODE-PATCHES.md | 7 ++++++- packages/app/src/entry.tsx | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/AMICODE-PATCHES.md b/AMICODE-PATCHES.md index fa99ad582..200150512 100644 --- a/AMICODE-PATCHES.md +++ b/AMICODE-PATCHES.md @@ -263,4 +263,9 @@ Rebuilt with the exact T3 recipe (`OPENCODE_VERSION=1.17.3 bun run script/build. - prompt-input.tsx: threads `readClipboardText: platform.readClipboardText` into `createPromptAttachments`, mirroring `readClipboardImage`. - No shared bridge module extracted — matches this codebase's existing precedent (home-cards.tsx's bridge is also component-local, not a shared util); two call sites isn't yet worth a shared abstraction. - Tests: app `bun test` (66 files) → 376 pass / 0 fail, incl. the untouched `attachments.test.ts` (pure-function coverage only — `handlePaste` itself has no unit coverage in this codebase, matching home-cards.tsx's own bridge having none either; verification here is typecheck + build + the manual live-paste check below). Typecheck green in `packages/app`. - - Build sha256: `564a4cd783524e0f50541b716d9db4048b2e02430ebc77786a11a4504431c085` (vendored `linux-x64` binary, built via `fetch_opencode.mjs --local --any-ref` off commit `f971d3bd5d` — i.e. this build covers **#15 + #16 together**, same supersession pattern as #13→#14). Verify: smoke test `--version` → `1.17.3`; binary strings confirm both patches compiled in (`Aero` ×5, `clipboard-request` ×2, `readClipboardText` ×3); channel gate ON (`newLayoutDesigns` sentinel `=!0`). **Interactive Cmd/Ctrl+V-in-composer confirmation is live-VS-Code-only, NOT yet human-verified** — same deferred-to-dev-host-acceptance caveat as patches #12/#13. + - Build sha256 (composer-only build, superseded by the generalization below): `564a4cd783524e0f50541b716d9db4048b2e02430ebc77786a11a4504431c085`. + - **ADDENDUM (same session, live-test finding — Aaron): composer-only wiring wasn't enough.** Confirmed live: pasting into a _different_ editable (the generic "Connect \" API-key dialog, `dialog-connect-provider.tsx` — one of many such generic settings/connect forms) was still a silent no-op, because that dialog is a plain `` with no knowledge of the bridge at all. This is the exact gap patch #14's era of WIP had already found and fixed once before (composer-only interception "left every OTHER editable broken (provider API-key field, etc.)") — generalized properly this time instead of re-patching one dialog at a time: + - entry.tsx: new `installGlobalPasteFallback()`, installed once at app mount (alongside `readClipboardText`, not a separate file — same feature area). A single `document`-level `paste` listener: activates only when the event target is a form field or `contenteditable` AND the native `event.clipboardData` gave no plain text (the sandboxed-iframe symptom) — everywhere clipboardData already works (plain browser tabs, desktop) it's a pure no-op, no framed-check needed at the call site. On activation: fetches text via the same `readClipboardText` bridge, then splices it into ``/`