diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 000000000000..72154eb20270 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,7 @@ +## 2025-02-25 - Bun.serve default bindings + +**Vulnerability:** Ephemeral local HTTP servers used for OAuth callbacks (`packages/opencode/src/mcp/oauth-callback.ts` and `packages/opencode/src/plugin/codex.ts`) were omitting the `hostname` parameter in `Bun.serve`. This causes the server to bind to `0.0.0.0` (all interfaces) by default instead of `127.0.0.1` (localhost). + +**Learning:** In a local-first application, assuming `Bun.serve` binds to localhost by default is a critical mistake. It binds to all network interfaces, exposing local OAuth flows, webhooks, or inter-process communication servers to the entire local network, which breaks the local-only security boundary. + +**Prevention:** Every time `Bun.serve` is used to spawn a local server, `hostname: "127.0.0.1"` MUST be explicitly passed to prevent exposing the service to the network, unless mDNS or explicit external networking is intentionally enabled and authenticated. diff --git a/AI_REVIEW.md b/AI_REVIEW.md index aaac8d8556f7..b945f42d1e7c 100644 --- a/AI_REVIEW.md +++ b/AI_REVIEW.md @@ -48,7 +48,6 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + ## 📝 Files Changed (37 selected) - ### ✨ `packages/ui/src/assets/icons/provider/evroc.svg` **[ADDED]** **Status:** ✅ **NEW FILE** - This file has been newly created @@ -61,11 +60,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 2 + 3 + 4 No newline at end of file - 1 5 + 1 5 ``` - ### 📄 `packages/ui/src/assets/icons/provider/meganova.svg` **Type:** Source File 📄 @@ -81,11 +79,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 1 + 2 + 7 3 - 8 4 + 8 4 ``` - ### ✨ `packages/ui/src/assets/icons/provider/opencode-go.svg` **[ADDED]** **Status:** ✅ **NEW FILE** - This file has been newly created @@ -97,11 +94,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 1 + 2 + 3 + - 4 + 4 ``` - ### 📄 `packages/ui/src/assets/icons/provider/opencode.svg` **Type:** Source File 📄 @@ -112,11 +108,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 2 - 3 2 4 3 - 5 4 + 5 4 ``` - ### 📄 `packages/ui/src/components/accordion.css` **Type:** Stylesheet 🎨 @@ -124,18 +119,17 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + ```diff @@ -52,7 +52,7 @@ 52 52 letter-spacing: var(--letter-spacing-normal); - 53 53 + 53 53 54 54 &:hover { 55 - background-color: var(--surface-base-hover); 55 + background-color: var(--card-bg-hover); 56 56 } 57 57 &:active { 58 58 background-color: var(--surface-base-active); - 59 59 + 59 59 ``` - ### 📄 `packages/ui/src/components/button.css` **Type:** Stylesheet 🎨 @@ -163,17 +157,17 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 19 + transform 100ms var(--ease-smooth), 20 + filter var(--duration-fast) var(--ease-smooth), 21 + opacity var(--duration-fast) var(--ease-smooth); - 19 22 + 19 22 20 23 &[data-variant="primary"] { 21 24 background-color: var(--button-primary-base); 22 25 border-color: var(--border-weak-base); 23 26 color: var(--icon-invert-base); 27 + box-shadow: var(--glow-accent); - 24 28 + 24 28 25 29 [data-slot="icon-svg"] { 26 30 color: var(--icon-invert-base); @@ -28,17 +32,25 @@ - 28 32 + 28 32 29 33 &:hover:not(:disabled) { 30 34 background-color: var(--icon-strong-hover); 31 - box-shadow: var(--shadow-sm); @@ -198,25 +192,25 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 41 52 background-color: var(--icon-strong-disabled); 53 + box-shadow: var(--glow-none); 42 54 opacity: 0.6; - 43 55 + 43 55 44 56 [data-slot="icon-svg"] { @@ -54,11 +66,11 @@ - 54 66 + 54 66 55 67 [data-slot="icon-svg"] { 56 68 color: var(--icon-base); 57 - transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1); 69 + transition: color var(--duration-fast) var(--ease-smooth); 58 70 } - 59 71 + 59 71 60 72 &:hover:not(:disabled) { 61 - background-color: var(--surface-base-hover); 73 + background-color: var(--card-bg-hover); - 62 74 + 62 74 63 75 [data-slot="icon-svg"] { 64 76 color: var(--icon-hover); @@ -89,14 +101,18 @@ 89 101 } - 90 102 + 90 102 91 103 &[data-variant="secondary"] { 92 - border: transparent; 104 + border: 1px solid var(--border-accent-base); @@ -225,7 +219,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 95 107 box-shadow: var(--shadow-xs-border-base); 108 + backdrop-filter: blur(8px); 109 + -webkit-backdrop-filter: blur(8px); - 96 110 + 96 110 97 111 &:hover:not(:disabled) { 98 112 background-color: var(--button-secondary-hover); 113 + border-color: var(--border-accent-hover); @@ -234,11 +228,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 100 116 } 101 117 &:focus:not(:disabled) { 102 118 background-color: var(--button-secondary-base); -103 119 +103 119 ``` - ### 📄 `packages/ui/src/components/card.css` **Type:** Stylesheet 🎨 @@ -266,7 +259,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 12 14 border-radius: var(--radius-lg); 13 15 padding: 6px 12px; 14 16 overflow: clip; - 15 17 + 15 17 16 18 &:hover { 17 - border-color: var(--border-weaker-hover); 18 - box-shadow: var(--shadow-xs); @@ -274,13 +267,12 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 20 + box-shadow: var(--card-glow); 21 + transform: translateY(-2px); 19 22 } - 20 23 + 20 23 21 24 &[data-variant="error"] { - 22 25 + 22 25 ``` - ### 📄 `packages/ui/src/components/checkbox.css` **Type:** Stylesheet 🎨 @@ -292,13 +284,12 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 91 91 background-color: var(--surface-weak); 92 + box-shadow: var(--glow-accent); 92 93 } - 93 94 + 93 94 94 95 &[data-checked]:hover:not([data-disabled], [data-readonly]) [data-slot="checkbox-checkbox-control"], - 95 96 + 95 96 ``` - ### 📄 `packages/ui/src/components/collapsible.css` **Type:** Stylesheet 🎨 @@ -323,12 +314,11 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 29 + } 23 30 user-select: none; 24 31 color: var(--text-base); - 25 32 - 26 33 + 25 32 + 26 33 ``` - ### 📄 `packages/ui/src/components/context-menu.css` **Type:** Stylesheet 🎨 @@ -352,11 +342,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 10 12 padding: 4px; 11 13 z-index: 100; 12 14 transform-origin: var(--kb-menu-content-transform-origin); - 13 15 + 13 15 ``` - ### 📄 `packages/ui/src/components/dialog.css` **Type:** Stylesheet 🎨 @@ -373,12 +362,12 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 8 + backdrop-filter: blur(8px); 9 + -webkit-backdrop-filter: blur(8px); 10 10 } - 11 11 + 11 11 12 12 [data-component="dialog"] { @@ -47,18 +47,17 @@ 47 47 display: none; 48 48 } - 49 49 + 49 49 50 - /* padding: 8px; */ 51 - /* padding: 8px 8px 0 8px; */ 52 50 border-radius: var(--radius-xl); @@ -392,7 +381,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 57 - 0 0 0 1px var(--border-weaker-base); 55 + box-shadow: var(--dialog-shadow); 56 + border: 1px solid var(--border-accent-base); - 58 57 + 58 57 59 58 [data-slot="dialog-header"] { 60 59 display: flex; 61 - padding: 16px 20px; @@ -403,7 +392,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + @@ -67,13 +66,13 @@ 67 66 [data-slot="dialog-title"] { 68 67 color: var(--text-strong); - 69 68 + 69 68 70 - /* text-16-medium */ 69 + /* text-17-semibold */ 71 70 font-family: var(--font-family-sans); @@ -420,17 +409,17 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 79 78 } @@ -140,10 +139,10 @@ 140 139 } -141 140 +141 140 142 141 [data-component="dialog"][data-transition] [data-slot="dialog-content"] { 143 - animation: contentHide 120ms cubic-bezier(0.4, 0, 1, 1) forwards; 142 + animation: contentHide 150ms ease-in forwards; -144 143 +144 143 145 144 &[data-expanded] { 146 - animation: contentShow 250ms cubic-bezier(0.16, 1, 0.3, 1); 145 + animation: contentShow 300ms var(--ease-snappy); 147 146 } 148 147 } -149 148 +149 148 @@ -176,7 +175,7 @@ 176 175 @keyframes contentShow { 177 176 from { @@ -448,11 +437,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 192 + transform: scale(0.95) translateY(4px); 194 193 } 195 194 } -196 195 +196 195 ``` - ### 📄 `packages/ui/src/components/dock-surface.css` **Type:** Stylesheet 🎨 @@ -479,7 +467,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 12 + border-color var(--duration-slow) var(--ease-smooth), 13 + box-shadow var(--duration-slow) var(--ease-smooth); 11 14 } - 12 15 + 12 15 13 16 [data-dock-surface="shell"]:focus-within { 14 - box-shadow: 15 - var(--shadow-xs-border), @@ -488,7 +476,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 17 + border-color: var(--border-accent-focus); 18 + box-shadow: var(--prompt-glow); 18 19 } - 19 20 + 19 20 20 21 [data-dock-surface="tray"] { 21 22 background-color: var(--background-base); 22 - border: 1px solid var(--border-weak-base); @@ -501,13 +489,12 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 27 - transition: border-color 200ms cubic-bezier(0.4, 0, 0.2, 1); 28 + transition: border-color var(--duration-normal) var(--ease-smooth); 28 29 } - 29 30 + 29 30 30 31 [data-dock-surface="tray"][data-dock-attach="top"] { - 31 32 + 31 32 ``` - ### 📄 `packages/ui/src/components/dropdown-menu.css` **Type:** Stylesheet 🎨 @@ -531,12 +518,11 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 12 + box-shadow: var(--popover-shadow); 11 13 z-index: 50; 12 14 transform-origin: var(--kb-menu-content-transform-origin); - 13 15 - 14 16 + 13 15 + 14 16 ``` - ### 📄 `packages/ui/src/components/hover-card.css` **Type:** Stylesheet 🎨 @@ -553,20 +539,19 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 14 + backdrop-filter: blur(16px); 15 + -webkit-backdrop-filter: blur(16px); 14 16 pointer-events: auto; - 15 17 + 15 17 16 - border: 1px solid color-mix(in oklch, var(--border-base) 50%, transparent); 18 + border: 1px solid var(--border-accent-base); 17 19 background-clip: padding-box; 18 - box-shadow: var(--shadow-md); 20 + box-shadow: var(--popover-shadow); - 19 21 + 19 21 20 22 transform-origin: var(--kb-hovercard-content-transform-origin); - 21 23 - 22 24 + 21 23 + 22 24 ``` - ### 📄 `packages/ui/src/components/icon-button.css` **Type:** Stylesheet 🎨 @@ -591,15 +576,15 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 13 + box-shadow var(--duration-fast) var(--ease-smooth), 14 + transform 80ms var(--ease-smooth), 15 + filter var(--duration-fast) var(--ease-smooth); - 14 16 + 14 16 15 17 &[data-variant="primary"] { 16 18 background-color: var(--icon-strong-base); 19 + box-shadow: var(--glow-accent); - 17 20 + 17 20 18 21 [data-slot="icon-svg"] { 19 22 /* color: var(--icon-weak-base); */ @@ -29,6 +32,9 @@ - 29 32 + 29 32 30 33 &:hover:not(:disabled) { 31 34 background-color: var(--icon-strong-hover); 35 + box-shadow: var(--glow-accent-hover); @@ -610,18 +595,17 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 34 40 background-color: var(--icon-strong-focus); @@ -88,7 +94,7 @@ 88 94 } - 89 95 + 89 95 90 96 &:hover:not(:disabled) { 91 - background-color: var(--surface-base-hover); 97 + background-color: var(--card-bg-hover); - 92 98 + 92 98 93 99 [data-slot="icon-svg"] { 94 100 color: var(--icon-hover); - 95 101 + 95 101 ``` - ### 📄 `packages/ui/src/components/keybind.css` **Type:** Stylesheet 🎨 @@ -636,14 +620,13 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 9 9 background: var(--surface-base); 10 - box-shadow: var(--shadow-xxs-border); 10 + border: 1px solid var(--border-accent-base); - 11 11 + 11 11 12 12 /* text-12-regular */ 13 13 font-family: var(--font-family-sans); - 14 14 + 14 14 ``` - ### 📄 `packages/ui/src/components/logo.css` **Type:** Stylesheet 🎨 @@ -660,11 +643,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 8 +[data-component="logo-mark"]:hover { 9 + filter: drop-shadow(0 0 12px var(--accent-base)); 4 10 } - 5 11 + 5 11 ``` - ### 📄 `packages/ui/src/components/popover.css` **Type:** Stylesheet 🎨 @@ -684,14 +666,13 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 13 14 background-clip: padding-box; 14 - box-shadow: var(--shadow-lg); 15 + box-shadow: var(--popover-shadow); - 15 16 + 15 16 16 17 transform-origin: var(--kb-popover-content-transform-origin); - 17 18 - 18 19 + 17 18 + 18 19 ``` - ### 📄 `packages/ui/src/components/progress.css` **Type:** Stylesheet 🎨 @@ -707,13 +688,12 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 46 + box-shadow: 0 0 12px -2px var(--accent-base); 47 + transition: width var(--duration-normal) var(--ease-smooth); 47 48 } - 48 49 + 48 49 49 50 &[data-indeterminate] [data-slot="progress-fill"] { - 50 51 + 50 51 ``` - ### 📄 `packages/ui/src/components/provider-icons/sprite.svg` **Type:** Source File 📄 @@ -774,11 +754,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 865 857 866 858 :first-child > [data-component="markdown"]:first-child { 93 95 margin-top: 0; - 94 96 + 94 96 ``` - ### 📄 `packages/ui/src/components/spinner.css` **Type:** Stylesheet 🎨 @@ -874,11 +850,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 3 3 flex-shrink: 0; 4 4 width: 18px; 5 5 aspect-ratio: 1; - 6 6 + 6 6 ``` - ### 📄 `packages/ui/src/components/switch.css` **Type:** Stylesheet 🎨 @@ -890,13 +865,12 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 97 97 background-color: var(--icon-strong-base); 98 + box-shadow: var(--glow-accent); 98 99 } - 99 100 + 99 100 100 101 &[data-checked] [data-slot="switch-thumb"] { -101 102 +101 102 ``` - ### 📄 `packages/ui/src/components/tag.css` **Type:** Stylesheet 🎨 @@ -905,7 +879,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + @@ -4,10 +4,11 @@ 4 4 justify-content: center; 5 5 user-select: none; - 6 6 + 6 6 7 - border-radius: var(--radius-xs); 8 - border: 0.5px solid var(--border-weak-base); 7 + border-radius: var(--radius-full); @@ -913,14 +887,13 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 9 9 background: var(--surface-raised-base); 10 10 color: var(--text-base); 11 + transition: all var(--duration-normal) var(--ease-smooth); - 11 12 + 11 12 12 13 &[data-size="normal"] { 13 14 height: 18px; - 14 15 + 14 15 ``` - ### 📄 `packages/ui/src/components/text-field.css` **Type:** Stylesheet 🎨 @@ -933,7 +906,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 53 + transition: 54 + border-color var(--duration-normal) var(--ease-smooth), 55 + box-shadow var(--duration-normal) var(--ease-smooth); - 53 56 + 53 56 54 57 &:focus-within:not(:has([data-readonly])) { 55 - border-color: transparent; 56 - /* border/shadow-xs/select */ @@ -946,13 +919,12 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 58 + border-color: var(--border-accent-focus); 59 + box-shadow: var(--glow-accent-focus); 63 60 } - 64 61 + 64 61 65 62 &:has([data-invalid]) { - 66 63 + 66 63 ``` - ### 📄 `packages/ui/src/components/toast.css` **Type:** Stylesheet 🎨 @@ -964,7 +936,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 40 40 pointer-events: auto; 41 - transition: all 150ms ease-out; 41 + transition: all var(--duration-fast) var(--ease-smooth); - 42 42 + 42 42 43 43 border-radius: var(--radius-lg); 44 - border: 1px solid var(--border-weak-base); 44 + border: 1px solid var(--border-accent-base); @@ -974,14 +946,13 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 46 48 color: var(--text-invert-base); 47 - box-shadow: var(--shadow-md); 49 + box-shadow: var(--popover-shadow); - 48 50 + 48 50 49 51 [data-slot="toast-inner"] { 50 52 display: flex; - 51 53 + 51 53 ``` - ### 📄 `packages/ui/src/components/tooltip.css` **Type:** Stylesheet 🎨 @@ -1000,7 +971,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 25 26 padding: 4px 10px; 26 - border: 1px solid var(--border-weak-base, rgba(0, 0, 0, 0.07)); 27 + border: 1px solid var(--border-accent-base); - 27 28 + 27 28 28 - box-shadow: var(--shadow-md); 29 + box-shadow: var(--popover-shadow); 29 30 pointer-events: none !important; @@ -1010,12 +981,11 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 33 + transform var(--duration-fast) var(--ease-smooth); 31 34 transform: translate3d(0, 0, 0); 32 35 transform-origin: var(--kb-tooltip-content-transform-origin); - 33 36 - 34 37 + 33 36 + 34 37 ``` - ### 📄 `packages/ui/src/styles/aurora.css` **Type:** Stylesheet 🎨 @@ -1050,11 +1020,11 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 24 + Typography + spacing (theme.css): 25 + --font-family-display, --text-xs through --text-4xl, 26 + --space-*, --gap-*, --padding-*, --max-prose-width - 4 27 + 4 27 5 - This is NOT a color theme — it's a full component redesign. 6 - Every element: radius, spacing, shadows, glass, glow, motion. 7 28 ================================================================ */ - 8 29 + 8 29 9 -/* ============================================================ 10 - 0. AURORA FOUNDATIONS — Variables, fonts, reduced-motion 11 - ============================================================ */ @@ -2225,11 +2195,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 34 + "JetBrains Mono Nerd Font Mono", "JetBrains Mono", "GeistMono Nerd Font Mono", "SF Mono", "Fira Code", 35 + "CaskaydiaCove Nerd Font Mono", "IBM Plex Mono", ui-monospace, monospace; 1173 36 } -1174 37 +1174 37 ``` - ### 📄 `packages/ui/src/styles/base.css` **Type:** Stylesheet 🎨 @@ -2275,11 +2244,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 431 + scroll-behavior: auto !important; 432 + } 433 +} -398 434 +398 434 ``` - ### 📄 `packages/ui/src/styles/theme.css` **Type:** Stylesheet 🎨 @@ -2298,7 +2266,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 8 + "Geist", "Inter", "SF Pro Display", system-ui, "Apple Color Emoji", "Segoe UI Emoji", sans-serif; 9 + /* Math-capable fallback (KaTeX/MathJax will use their own fonts; this covers inline math Unicode) */ 10 + --font-family-math: "Cambria Math", "STIX Two Math", "Latin Modern Math", math, serif; - 6 11 + 6 11 12 + /* ─── LEGACY FONT SIZES (kept for backward compat) ─── */ 7 13 --font-size-small: 13px; 8 14 --font-size-base: 14px; @@ -2342,7 +2310,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 51 + --letter-spacing-wider: 0.05em; 19 52 --letter-spacing-tightest: -0.3199999928474426; 20 53 --paragraph-spacing-base: 0; - 21 54 + 21 54 55 + /* ─── PROSE WIDTH ─── */ 56 + --max-prose-width: 72ch; 57 + @@ -2383,12 +2351,12 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 92 + --padding-card-sm: var(--space-3); 93 + --padding-card-lg: var(--space-6); 94 + --padding-input: var(--space-2-5) var(--space-3); - 23 95 + 23 95 24 96 --breakpoint-sm: 40rem; 25 97 --breakpoint-md: 48rem; @@ -42,10 +114,61 @@ 42 114 --container-7xl: 80rem; - 43 115 + 43 115 44 116 --radius-xs: 0.125rem; 45 - --radius-sm: 0.25rem; 46 - --radius-md: 0.375rem; @@ -2449,14 +2417,13 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 169 + --thinking-color: var(--icon-interactive-base); 170 + --thinking-glow: none; 171 + --progress-gradient: var(--border-active); - 49 172 + 49 172 50 173 --shadow-xs: 51 174 0 1px 2px -0.5px light-dark(hsl(0 0% 0% / 0.05), hsl(0 0% 0% / 0.08)), - 52 175 + 52 175 ``` - ### 📄 `packages/ui/src/theme/desktop-theme.schema.json` **Type:** Configuration/Data File 📋 @@ -2481,11 +2448,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 82 94 } 83 95 } 84 96 }, - 85 97 + 85 97 ``` - ### 📄 `packages/ui/src/theme/index.ts` **Type:** TypeScript Source File 📘 @@ -2497,13 +2463,12 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 9 9 CssVarRef, 10 + TokenValue, 10 11 } from "./types" - 11 12 + 11 12 12 13 export { - 13 14 + 13 14 ``` - ### 📄 `packages/ui/src/theme/resolve.ts` **Type:** TypeScript Source File 📘 @@ -2513,13 +2478,13 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 1 +import { generateNeutralScale, generateScale, hexToOklch, hexToRgb, oklchToHex, withAlpha } from "./color" 1 2 import type { ColorValue, DesktopTheme, HexColor, ResolvedTheme, ThemeVariant } from "./types" 2 -import { generateNeutralScale, generateScale, hexToOklch, oklchToHex, withAlpha } from "./color" - 3 3 + 3 3 4 4 export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): ResolvedTheme { 5 5 const { seeds, overrides = {} } = variant @@ -14,6 +14,14 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res 14 14 const diffAdd = generateScale(seeds.diffAdd, isDark) 15 15 const diffDelete = generateScale(seeds.diffDelete, isDark) - 16 16 + 16 16 17 + // Accent seeds with fallbacks 18 + const accentSeed = seeds.accent ?? seeds.interactive 19 + const accentSecondarySeed = seeds.accentSecondary ?? seeds.info @@ -2529,12 +2494,12 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 23 + const accentTertiary = generateScale(accentTertiarySeed, isDark) 24 + 17 25 const neutralAlpha = generateNeutralAlphaScale(neutral, isDark) - 18 26 + 18 26 19 27 const tokens: ResolvedTheme = {} @@ -290,6 +298,207 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res 290 298 tokens["avatar-text-cyan"] = isDark ? "#369eff" : "#0894b3" 291 299 tokens["avatar-text-lime"] = isDark ? "#c4f042" : "#5d770d" -292 300 +292 300 301 + // ─── ACCENT TOKENS ─── 302 + tokens["accent-base"] = accent[8] 303 + tokens["accent-hover"] = accent[9] @@ -2739,11 +2704,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 293 502 for (const [key, value] of Object.entries(overrides)) { 294 503 tokens[key] = value 295 504 } -296 505 +296 505 ``` - ### 📄 `packages/ui/src/theme/themes/aurora.json` **Type:** Configuration/Data File 📋 @@ -2773,11 +2737,10 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 63 69 }, 64 70 "overrides": { 65 71 "background-base": "#0A0A0F", - 66 72 + 66 72 ``` - ### 📄 `packages/ui/src/theme/types.ts` **Type:** TypeScript Source File 📘 @@ -2794,13 +2757,13 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 23 + /** Tertiary accent (e.g. attention, warm highlights). Falls back to `warning`. */ 24 + accentTertiary?: HexColor 19 25 } - 20 26 + 20 26 21 27 export interface ThemeVariant { 22 28 seeds: ThemeSeedColors 23 - overrides?: Record 29 + overrides?: Record 24 30 } - 25 31 + 25 31 26 32 export interface DesktopTheme { @@ -43,6 +49,12 @@ export type TokenCategory = 43 49 | "markdown" @@ -2812,25 +2775,26 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + 55 + | "motion" 56 + | "radius" 57 + | "message" - 46 58 + 46 58 47 59 export type ThemeToken = string - 48 60 + 48 60 @@ -50,4 +62,7 @@ export type CssVarRef = `var(--${string})` - 50 62 + 50 62 51 63 export type ColorValue = HexColor | CssVarRef - 52 64 + 52 64 53 -export type ResolvedTheme = Record 65 +/** Any CSS-valid token value — hex colors, rgba(), box-shadow strings, easing functions, etc. */ 66 +export type TokenValue = string 67 + 68 +export type ResolvedTheme = Record - 54 69 + 54 69 ``` ## 🤖 Comprehensive Review Checklist ### ✅ Code Quality & Standards + - [ ] **Syntax & Formatting**: Consistent indentation, proper spacing - [ ] **Naming Conventions**: Clear, descriptive variable/function names - [ ] **Code Structure**: Logical organization, appropriate function size @@ -2838,6 +2802,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + - [ ] **Type Safety**: Proper typing (if applicable) ### 🔍 Logic & Functionality + - [ ] **Algorithm Correctness**: Logic implements requirements correctly - [ ] **Edge Case Handling**: Boundary conditions properly addressed - [ ] **Error Handling**: Appropriate try-catch blocks and error messages @@ -2845,6 +2810,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + - [ ] **Memory Management**: Proper cleanup, no memory leaks ### 🐛 Potential Issues & Bugs + - [ ] **Runtime Errors**: No null/undefined dereferencing - [ ] **Type Mismatches**: Consistent data types throughout - [ ] **Race Conditions**: Proper async/await handling @@ -2852,6 +2818,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + - [ ] **Off-by-one Errors**: Array/loop bounds correctly handled ### 🔒 Security Considerations + - [ ] **Input Validation**: User inputs properly sanitized - [ ] **XSS Prevention**: No unsafe HTML injection - [ ] **Authentication**: Proper access controls if applicable @@ -2859,6 +2826,7 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + - [ ] **Dependency Security**: No known vulnerable packages ### 📱 User Experience & Accessibility + - [ ] **Responsive Design**: Works on different screen sizes - [ ] **Loading States**: Proper feedback during operations - [ ] **Error Messages**: User-friendly error communication @@ -2868,28 +2836,34 @@ packages/ui/src/assets/icons/provider/evroc.svg | 3 + ### 💡 Improvement Suggestions #### Code Organization + - [ ] Consider extracting complex logic into separate functions - [ ] Evaluate if constants should be moved to configuration - [ ] Check for code duplication opportunities #### Performance Optimizations + - [ ] Identify opportunities for memoization - [ ] Consider lazy loading for heavy operations - [ ] Evaluate database query efficiency (if applicable) #### Testing Recommendations + - [ ] Unit tests for core functionality - [ ] Integration tests for API endpoints - [ ] Edge case testing scenarios #### Documentation Needs + - [ ] API documentation updates - [ ] Code comments for complex algorithms - [ ] README updates if public interfaces changed ### 📝 Review Notes -*Add your specific feedback, suggestions, and observations here:* + +_Add your specific feedback, suggestions, and observations here:_ --- -*Individual file review generated by AI Visual Code Review v2.0* -*Generated: 2026-02-28T14:16:31.613Z* + +_Individual file review generated by AI Visual Code Review v2.0_ +_Generated: 2026-02-28T14:16:31.613Z_ diff --git a/bun.lock b/bun.lock index 8df1d6456c2b..0edc32b54d21 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 1, "workspaces": { "": { "name": "opencode", diff --git a/docs/09-temp/cline-subagent-research.md b/docs/09-temp/cline-subagent-research.md index 76d7684e9929..7e56fed69510 100644 --- a/docs/09-temp/cline-subagent-research.md +++ b/docs/09-temp/cline-subagent-research.md @@ -17,16 +17,13 @@ - **CLI Subagent Command Transformation**: `src/integrations/cli-subagents/subagent_command.ts` - `isSubagentCommand()` — identifies simplified cline commands - `transformClineCommand()` — injects `--json -y` flags for autonomous execution - - **Agent Client Protocol (ACP)**: `cli/src/acp/AcpAgent.ts` - Bridges ClineAgent with AgentSideConnection for stdio-based communication - Handles permission requests, forwards session events - - **ClineAgent**: `cli/src/agent/ClineAgent.ts` - Implements ACP agent interface - Translates ACP requests into core Controller operations - Manages authentication, session modes, processes user prompts - - **Message Translator**: `cli/src/agent/messageTranslator.ts` - Converts ClineMessage objects to ACP SessionUpdate messages - Computes deltas for streaming (avoids duplicate content) @@ -40,12 +37,14 @@ ## Comparison with OpenCode's Subagent System OpenCode already has subagents (`TaskTool` in `packages/opencode/src/tool/task.ts`): + - Subagents are spawned via the `task` tool - Each subagent gets its own child session - Subagent types: explore, plan, general (configurable per agent) - Results returned as tool output to parent session **Gaps to investigate:** + - Does Cline support parallel subagents? (OpenCode does via plan mode Phase 1) - How does Cline's ACP protocol compare to opencode's Bus event system? - Can we adopt Cline's streaming delta pattern for subagent updates? @@ -53,6 +52,7 @@ OpenCode already has subagents (`TaskTool` in `packages/opencode/src/tool/task.t ## Tonight's Session Summary (2026-02-24, 2:37 AM - 4:57 AM) ### 6 PRs Submitted to opencode (sst/opencode): + 1. **#14820** — Streaming content duplication fix (global-sdk.tsx voided Set) 2. **#14821** — Font size settings (CSS vars + terminal + UI stepper) 3. **#14826** — ContextOverflowError auto-recovery (processor.ts) @@ -61,6 +61,7 @@ OpenCode already has subagents (`TaskTool` in `packages/opencode/src/tool/task.t 6. **#14835** — Wide mode setting (full-width chat toggle) ### Issues Created: + - #14822, #14823, #14824, #14825, #14830, #14834 ### All branches merged into `origin/dev` on fork (PrakharMNNIT/opencode) diff --git a/docs/09-temp/codex-queue-steer-architecture.md b/docs/09-temp/codex-queue-steer-architecture.md index f2012489aa4f..5c6386daaab8 100644 --- a/docs/09-temp/codex-queue-steer-architecture.md +++ b/docs/09-temp/codex-queue-steer-architecture.md @@ -9,9 +9,9 @@ Codex implements a **dual-input model** that lets users interact with the agent **during** an active turn, not just between turns: -| Action | Keybinding | Behavior | When Turn Active | -|--------|-----------|----------|-----------------| -| **Queue** | `Enter` | Enqueue message for next turn boundary | Message waits in queue, displayed in UI | +| Action | Keybinding | Behavior | When Turn Active | +| --------- | ------------------------------- | ----------------------------------------- | ---------------------------------------- | +| **Queue** | `Enter` | Enqueue message for next turn boundary | Message waits in queue, displayed in UI | | **Steer** | `⌘Enter` / `Enter` (steer-mode) | Inject input into active turn immediately | Message sent to model in current context | --- @@ -201,14 +201,14 @@ When `NoActiveTurn` occurs, the app-server falls back — the input that failed ### Critical Difference -| Aspect | Queue | Steer | -|--------|-------|-------| -| **Timing** | After turn ends | During active turn | -| **Turn boundary** | Creates new turn | Same turn continues | -| **Model sees it** | On next turn start | At next loop iteration | -| **Cancels response** | No (waits) | No (appends to context) | -| **UI display** | Queued messages widget | Injected into chat transcript | -| **Fallback** | N/A | Falls back to queue if no active turn | +| Aspect | Queue | Steer | +| -------------------- | ---------------------- | ------------------------------------- | +| **Timing** | After turn ends | During active turn | +| **Turn boundary** | Creates new turn | Same turn continues | +| **Model sees it** | On next turn start | At next loop iteration | +| **Cancels response** | No (waits) | No (appends to context) | +| **UI display** | Queued messages widget | Injected into chat transcript | +| **Fallback** | N/A | Falls back to queue if no active turn | --- @@ -285,11 +285,13 @@ Steer is gated behind `Feature::Steer` in the TUI: ## Implications for OpenCode ### What OpenCode Currently Has + - Session/turn model with `processor.ts` handling model interaction - Parallel agents via `task.ts` tool - No mid-turn input injection ### What Queue/Steer Would Add + 1. **Pending input buffer** on the session/turn state 2. **Steer RPC** that pushes to the buffer while model is running 3. **Loop-boundary drain** that checks for pending input after each model response @@ -298,6 +300,7 @@ Steer is gated behind `Feature::Steer` in the TUI: 6. **Fallback path**: steer → queue if no active turn ### Key Implementation Points + - `steer_input()` is a **lock-based, non-cancelling** approach — it doesn't abort the model stream - Pending input is consumed at the **top of the agentic loop**, not mid-stream - The model sees steered input as additional conversation items on its next iteration diff --git a/docs/09-temp/escape-key-ux-research.md b/docs/09-temp/escape-key-ux-research.md index 37575a6243bb..80bc9d8b9542 100644 --- a/docs/09-temp/escape-key-ux-research.md +++ b/docs/09-temp/escape-key-ux-research.md @@ -4,20 +4,24 @@ **Status:** TODO — brainstorm in next session ## Problem + Pressing Escape accidentally during AI response immediately stops the response with no confirmation. No visual feedback in chat that response was interrupted. ## Current Behavior + - Escape → immediately cancels the LLM response - Shows a notification/warning toast - No visual indicator in the chat thread that the message was interrupted - No confirmation dialog before cancelling ## User's Proposed Improvements + 1. **Confirmation before cancel** — Alert/dialog: "Are you sure you want to interrupt?" 2. **Visual interruption indicator** — Show in chat that the message was interrupted (red line, badge, etc.) 3. **Better UX** — Maybe double-tap Escape to cancel, or Escape once to show warning ## Files to Investigate + - `packages/app/src/pages/session.tsx` — handleKeyDown, Escape handling - `packages/app/src/components/prompt-input.tsx` — Escape key handling in input - `packages/opencode/src/session/prompt.ts` — cancel() function @@ -25,6 +29,7 @@ Pressing Escape accidentally during AI response immediately stops the response w - `packages/app/src/pages/session/use-session-commands.tsx` — session.cancel command ## Design Questions + 1. Should Escape require double-tap? (like VS Code terminal) 2. Should there be a small "Esc to cancel" indicator during streaming? 3. Should interrupted messages have a visual indicator (red border/badge)? diff --git a/docs/09-temp/issues.md b/docs/09-temp/issues.md index 263f75fe81c8..a08fa72d42ea 100644 --- a/docs/09-temp/issues.md +++ b/docs/09-temp/issues.md @@ -11,17 +11,21 @@ ### Priority: P0 — Stop The Bleeding ### What is the issue? + The session processor retries failed API calls in an infinite `while(true)` loop with **no maximum retry count**. When an error is classified as "retryable" by `retry.ts`, the processor will retry it forever — user observed **2,244 identical retries over 3.5 hours** before manual abort. ### What is the bug? + `packages/opencode/src/session/processor.ts` line ~53 has a `while(true)` loop. When the catch block determines an error is retryable via `SessionRetry.retryable(error)`, it increments `attempt` and `continue`s the loop. There is **no guard** like `if (attempt >= MAX_RETRIES) break`. ### Where it can happen? + - Any API call that returns a retryable error (transient network issues, rate limits, Bedrock context overflow misclassified as retryable) - Most critically: Bedrock "prompt is too long" errors that get misclassified as retryable by the catch-all in `retry.ts` (see Issue #2) - Affects both parent sessions and subagent sessions independently ### What any agent needs to look for? + ``` File: packages/opencode/src/session/processor.ts Location: The while(true) loop (~line 53) @@ -29,6 +33,7 @@ Pattern: Look for the catch block that calls SessionRetry.retryable() and does ` ``` ### How to make the fix? + Add a `MAX_RETRIES` constant and guard before the `continue`: ```typescript @@ -48,6 +53,7 @@ if (attempt >= MAX_RETRIES) { The error should be stored on `input.assistantMessage.error` so the session stops and the UI shows the error. Make sure the status is set to idle after breaking. ### Testing + - Trigger a retryable error (e.g., rate limit) and verify it stops after 10 attempts - Verify the error message appears in the session UI - Verify the session status returns to "idle" (not stuck in "retry") @@ -59,10 +65,13 @@ The error should be stored on `input.assistantMessage.error` so the session stop ### Priority: P0 — Stop The Bleeding ### What is the issue? + When Amazon Bedrock returns an API error (e.g., "prompt is too long"), the `message()` function in `error.ts` receives `e.message = "undefined"` (the literal string, not the JS undefined value). The function only checks for empty string `""`, so it passes `"undefined"` through to `isOverflow()`, which fails to match any overflow pattern. This means **Bedrock context overflow errors are never detected as overflow**, preventing compaction from triggering. ### What is the bug? + `packages/opencode/src/provider/error.ts` function `message()` (~line 50-80): + ```typescript const msg = e.message if (msg === "") { @@ -70,14 +79,17 @@ if (msg === "") { // ... } ``` + When Bedrock SDK sets `e.message` to the literal string `"undefined"`, this check passes through. The actual error details are in `e.responseBody` but never extracted. ### Where it can happen? + - Any Bedrock API call that returns an error (context overflow, validation errors, throttling) - The Bedrock SDK wraps errors differently than the Anthropic direct SDK - Specifically observed with "prompt is too long: 208845 tokens > 200000 maximum" errors ### What any agent needs to look for? + ``` File: packages/opencode/src/provider/error.ts Location: The message() function, specifically the `if (msg === "")` check @@ -85,6 +97,7 @@ Also check: isOverflow() function and the OVERFLOW_PATTERNS regex ``` ### How to make the fix? + Extend the empty-message check to also handle `"undefined"`: ```typescript @@ -103,6 +116,7 @@ function message(providerID: string, e: APICallError) { This ensures the actual error body (which contains "prompt is too long") is used for overflow detection instead of the meaningless `"undefined"` string. ### Testing + - Mock a Bedrock APICallError with `message: "undefined"` and `responseBody: "prompt is too long: 208845 tokens > 200000 maximum"` - Verify `message()` returns the responseBody, not `"undefined"` - Verify `isOverflow()` correctly detects the overflow pattern from the responseBody @@ -114,10 +128,13 @@ This ensures the actual error body (which contains "prompt is too long") is used ### Priority: P0 — Stop The Bleeding ### What is the issue? + When a subagent (child session spawned by the `task` tool) fails with an error, the parent session shows it as **successfully completed with empty output**. The user sees a green ✅ checkmark for a task that actually errored. This is THE primary cause of "failures not reflected in main chat." ### What is the bug? + `packages/opencode/src/tool/task.ts` line ~145: + ```typescript const result = await SessionPrompt.prompt({...}) const text = result.parts.findLast((x) => x.type === "text")?.text ?? "" @@ -126,11 +143,13 @@ const text = result.parts.findLast((x) => x.type === "text")?.text ?? "" `result.info` contains an `.error` field when the child session errored (set by `processor.ts` at `input.assistantMessage.error = error`). But `task.ts` **never checks `result.info.error`** — it only looks for text parts. When the child errored, there are no text parts, so `text = ""`, and the parent receives `\n\n` as a "successful" empty result. ### Where it can happen? + - Any subagent failure: context overflow, API error, tool execution error, rate limit - Parallel subagents: if 1 of 3 subagents fails, parent sees 3 "completed" tasks with one having empty output - The parent LLM may then hallucinate that the task completed or silently move on ### What any agent needs to look for? + ``` File: packages/opencode/src/tool/task.ts Location: After the `SessionPrompt.prompt()` call, before building the output @@ -139,6 +158,7 @@ Also check: packages/opencode/src/session/prompt.ts for the return type of promp ``` ### How to make the fix? + Add an error check immediately after the `SessionPrompt.prompt()` call: ```typescript @@ -168,6 +188,7 @@ const text = result.parts.findLast((x) => x.type === "text")?.text ?? "" **Important**: Check the actual type of `result.info` to use proper typing instead of `(result.info as any).error`. Look at how `processor.ts` sets the error on `input.assistantMessage.error` to understand the shape. ### Testing + - Trigger a subagent error (e.g., invalid tool call, context overflow) - Verify the parent session shows "ERROR: ..." in the task result, not empty - Verify the parent LLM receives the error and can report it to the user @@ -179,9 +200,11 @@ const text = result.parts.findLast((x) => x.type === "text")?.text ?? "" ### Priority: P0 — This Sprint ### What is the issue? + The `models-snapshot.ts` file (auto-generated from models.dev) lists Claude Opus 4.6 on Bedrock with `context: 1,000,000`. This is the model's capability WITH the `context-1m` beta header. However, the Bedrock provider handler in `provider.ts` **never sends the 1M beta header**, so Bedrock actually enforces a 200K limit. The result: UI shows "20% context usage" when the user is actually at 100% of the real limit, and compaction never triggers. ### What is the bug? + Two bugs combine: 1. **`models-snapshot.ts`** lists Opus 4.6 Bedrock models at 1M context (reflects model capability, not runtime limit) @@ -190,9 +213,10 @@ Two bugs combine: - Override the context limit to 200K when 1M beta is NOT active **Affected models in snapshot**: + ``` amazon-bedrock / anthropic.claude-opus-4-6-v1: context=1,000,000 ❌ -amazon-bedrock / us.anthropic.claude-opus-4-6-v1: context=1,000,000 ❌ +amazon-bedrock / us.anthropic.claude-opus-4-6-v1: context=1,000,000 ❌ amazon-bedrock / eu.anthropic.claude-opus-4-6-v1: context=1,000,000 ❌ amazon-bedrock / global.anthropic.claude-opus-4-6-v1: context=1,000,000 ❌ ``` @@ -200,12 +224,14 @@ amazon-bedrock / global.anthropic.claude-opus-4-6-v1: context=1,000,000 ❌ All other Bedrock Claude models correctly show 200K. ### Where it can happen? + - Any user running Claude Opus 4.6 via Amazon Bedrock - Compaction threshold is calculated from `model.limit.context` → 1M → threshold ~900K - Bedrock rejects at 200K → 700K token gap where compaction never fires but API always rejects - Combined with Issue #1 (infinite retries), this causes the 3.5-hour freeze ### What any agent needs to look for? + ``` File: packages/opencode/src/provider/provider.ts Location: The "amazon-bedrock" entry in CUSTOM_LOADERS (~line 211) @@ -217,6 +243,7 @@ DO NOT edit models-snapshot.ts directly — it is auto-generated by build.ts ``` ### How to make the fix? + **Option A (Recommended)**: Add provider-level context limit override in the model resolution logic. When provider is "amazon-bedrock" and model is Claude, cap context at 200K unless a 1M configuration is explicitly enabled. Look at where models are resolved and limits are applied. The fix should go in `provider.ts` where models are loaded/resolved, adding a context limit override: @@ -232,6 +259,7 @@ if (providerID === "amazon-bedrock" && modelData.limit?.context > 200000) { **Option B (Future)**: Implement Cline's `:1m` suffix pattern — user explicitly opts into 1M context, which triggers adding `anthropic_beta: ["context-1m-2025-08-07"]` via `additionalModelRequestFields`. ### Testing + - Configure Bedrock with Opus 4.6 - Verify UI shows context limit as 200K (not 1M) - Verify compaction triggers before hitting Bedrock's actual 200K limit @@ -244,10 +272,13 @@ if (providerID === "amazon-bedrock" && modelData.limit?.context > 200000) { ### Priority: P0 — This Sprint ### What is the issue? + The `task` tool calls `SessionPrompt.prompt()` with **no timeout or deadline**. If a subagent gets stuck (infinite retry storm, permission hang, or any other blocking issue), the parent tool call never resolves. The parent session appears frozen with a spinning "running" indicator forever. ### What is the bug? + `packages/opencode/src/tool/task.ts`: + ```typescript const result = await SessionPrompt.prompt({ messageID, @@ -261,17 +292,20 @@ const result = await SessionPrompt.prompt({ ``` This Promise can hang indefinitely if the child session encounters: + - Infinite retry loop (Issue #1 before fix) - Permission hang (Issue #6) - Slow API responses that never complete ### Where it can happen? + - Any subagent execution, but especially: - When subagent hits context overflow with retries - When subagent needs permission and user is watching parent - When API provider is slow or unresponsive ### What any agent needs to look for? + ``` File: packages/opencode/src/tool/task.ts Location: The SessionPrompt.prompt() call @@ -281,6 +315,7 @@ Also check: ctx parameter in execute() — does it carry an abort signal? ``` ### How to make the fix? + Wrap the `SessionPrompt.prompt()` call with an AbortController timeout: ```typescript @@ -311,6 +346,7 @@ try { Check if `SessionPrompt.prompt()` already accepts an `abort` parameter. If not, trace how `processor.ts` passes its abort signal and ensure the plumbing exists. ### Testing + - Trigger a subagent that would hang (e.g., long-running task) - Verify it times out after the configured deadline - Verify the parent receives a timeout error message, not silent hang @@ -323,10 +359,13 @@ Check if `SessionPrompt.prompt()` already accepts an `abort` parameter. If not, ### Priority: P0 — This Sprint ### What is the issue? + When a subagent's tool requires permission (e.g., file write, command execution), the permission prompt appears **only in the child session**. If the user is watching the parent session, they never see the prompt. The child session hangs forever waiting for permission, which blocks the parent's tool call. ### What is the bug? + `packages/opencode/src/permission/next.ts` lines ~143-156: + ```typescript export function ask(input: AskInput) { return new Promise((resolve, reject) => { @@ -341,11 +380,13 @@ export function ask(input: AskInput) { `grep -c "abort" next.ts` returns **0** — there is zero abort signal awareness in the entire file. ### Where it can happen? + - Any subagent tool call that requires permission - Parallel subagents: one hangs on permission → parent hangs → all other parallel results blocked - Even with auto-approve policies, edge cases (new tools, destructive operations) may still prompt ### What any agent needs to look for? + ``` File: packages/opencode/src/permission/next.ts Location: The ask() function (exported, ~line 143) @@ -356,6 +397,7 @@ Also check: packages/opencode/src/session/prompt.ts for where permissions are re ``` ### How to make the fix? + Add AbortSignal support to the `ask()` function: ```typescript @@ -365,13 +407,13 @@ export function ask(input: AskInput & { abort?: AbortSignal }) { if (input.abort?.aborted) { return reject(new Error("Permission request aborted")) } - + // Listen for abort const onAbort = () => { reject(new Error("Permission request aborted")) } input.abort?.addEventListener("abort", onAbort, { once: true }) - + // ... existing permission logic ... // Clean up abort listener in resolve/reject paths }) @@ -379,11 +421,13 @@ export function ask(input: AskInput & { abort?: AbortSignal }) { ``` **Important**: The abort signal must be plumbed from `processor.ts` through the tool execution chain to `next.ts`. Trace the call path: + ``` processor.ts (has abort) → tool execution → specific tool → permission check → next.ts ask() ``` ### Testing + - Trigger a subagent that needs permission - Abort the parent session while permission is pending - Verify the child permission promise rejects @@ -396,10 +440,13 @@ processor.ts (has abort) → tool execution → specific tool → permission che ### Priority: P1 — Robustness ### What is the issue? + The `retryable()` function in `retry.ts` has a catch-all at line ~96 that makes **any error with a parseable JSON response body** retryable. This means Bedrock 400 errors ("prompt is too long"), which should NOT be retried, get classified as retryable — fueling the infinite retry storm. ### What is the bug? + `packages/opencode/src/session/retry.ts` line ~96: + ```typescript // After checking specific patterns (rate limit, overloaded, etc.)... return JSON.stringify(json) // ← ANY remaining JSON error = retryable @@ -408,11 +455,13 @@ return JSON.stringify(json) // ← ANY remaining JSON error = retryable The Bedrock "prompt is too long" error response is valid JSON with `"isRetryable": false` in the body, but the catch-all ignores this field and returns the body as a retryable error message. ### Where it can happen? + - Any API error that returns a JSON response body - Specifically: Bedrock validation errors (400), authentication errors, quota errors - Combined with Issue #1 (no max retries), this creates infinite retry storms ### What any agent needs to look for? + ``` File: packages/opencode/src/session/retry.ts Location: The retryable() function, specifically the catch-all after all pattern checks @@ -422,6 +471,7 @@ Also check: Whether the JSON body contains "isRetryable" or HTTP status fields ``` ### How to make the fix? + Replace the blanket catch-all with HTTP status-aware classification: ```typescript @@ -437,6 +487,7 @@ return JSON.stringify(json) ``` Also check for the `isRetryable` field that Bedrock includes: + ```typescript if ((json as any).isRetryable === false) return undefined ``` @@ -444,6 +495,7 @@ if ((json as any).isRetryable === false) return undefined **Note**: This fix is SAFER when combined with Issue #1 (MAX_RETRIES), since any misclassification is bounded by the retry cap. ### Testing + - Send a Bedrock 400 "prompt is too long" error → verify NOT retried - Send a 429 rate limit error → verify IS retried - Send a 500 server error → verify IS retried (up to MAX_RETRIES) @@ -456,10 +508,13 @@ if ((json as any).isRetryable === false) return undefined ### Priority: P1 — Robustness ### What is the issue? + When a tool execution errors, the tool-error handler in `processor.ts` rebuilds the tool state but **drops the `title` and `metadata` fields**. This means the UI loses the tool's display name and any navigation metadata (like `sessionId` for subagent links). ### What is the bug? + `packages/opencode/src/session/processor.ts` lines ~207-218, the `"tool-error"` case: + ```typescript case "tool-error": { const match = toolcalls[value.toolCallId] @@ -483,11 +538,13 @@ case "tool-error": { ``` ### Where it can happen? + - Any tool that errors during execution - Most visible for task tool errors — the `sessionId` metadata (used for navigating to child sessions) is lost - Also affects batch tool parts and any tool with custom title/metadata ### What any agent needs to look for? + ``` File: packages/opencode/src/session/processor.ts Location: The "tool-error" case in the stream event handler @@ -496,6 +553,7 @@ The "tool-result" case preserves title and metadata, but "tool-error" does not ``` ### How to make the fix? + Add `title` and `metadata` preservation to the error state: ```typescript @@ -521,6 +579,7 @@ case "tool-error": { ``` ### Testing + - Trigger a tool error (e.g., file read on non-existent path) - Verify the error part in the UI shows the tool title - Trigger a subagent error → verify the sessionId metadata is preserved in the error part @@ -532,24 +591,30 @@ case "tool-error": { ### Priority: P2 — Nice to Have ### What is the issue? + When batch tool calls fail, the output summary only says `"Executed X/Y tools successfully. Z failed."` without including **which tools failed or why**. The LLM receiving this output cannot diagnose or intelligently retry the failures. ### What is the bug? + `packages/opencode/src/tool/batch.ts` output message: + ```typescript -const outputMessage = failedCalls > 0 - ? `Executed ${successfulCalls}/${results.length} tools successfully. ${failedCalls} failed.` - : `All ${successfulCalls} tools executed successfully.` +const outputMessage = + failedCalls > 0 + ? `Executed ${successfulCalls}/${results.length} tools successfully. ${failedCalls} failed.` + : `All ${successfulCalls} tools executed successfully.` ``` Note: Individual tool-call parts ARE written to the database with their errors (via `Session.updatePart` in the catch block), so the UI shows them. But the **summary message returned to the LLM** lacks details. ### Where it can happen? + - Any batch execution where one or more tools fail - The LLM sees the summary but not the individual error details - Can cause the LLM to blindly retry the same failing operations ### What any agent needs to look for? + ``` File: packages/opencode/src/tool/batch.ts Location: The outputMessage construction after Promise.all results @@ -557,22 +622,25 @@ Pattern: The results array has { success, tool, error? } for each call ``` ### How to make the fix? + Include per-tool error details in the output: ```typescript -const outputMessage = failedCalls > 0 - ? [ - `Executed ${successfulCalls}/${results.length} tools successfully. ${failedCalls} failed.`, - "", - "Failed tools:", - ...results - .filter((r) => !r.success) - .map((r) => `- ${r.tool}: ${r.error instanceof Error ? r.error.message : String(r.error)}`), - ].join("\n") - : `All ${successfulCalls} tools executed successfully.\n\nKeep using the batch tool for optimal performance in your next response!` +const outputMessage = + failedCalls > 0 + ? [ + `Executed ${successfulCalls}/${results.length} tools successfully. ${failedCalls} failed.`, + "", + "Failed tools:", + ...results + .filter((r) => !r.success) + .map((r) => `- ${r.tool}: ${r.error instanceof Error ? r.error.message : String(r.error)}`), + ].join("\n") + : `All ${successfulCalls} tools executed successfully.\n\nKeep using the batch tool for optimal performance in your next response!` ``` ### Testing + - Execute a batch with one intentionally failing tool (e.g., read non-existent file) - Verify the output includes the tool name and error message - Verify the LLM can see which tool failed and why diff --git a/docs/09-temp/ui-overhaul-plan.md b/docs/09-temp/ui-overhaul-plan.md index 70d205acaf1a..1c9cc3b8fb9b 100644 --- a/docs/09-temp/ui-overhaul-plan.md +++ b/docs/09-temp/ui-overhaul-plan.md @@ -4,6 +4,7 @@ **Status:** ✅ PHASE 1 COMPLETE ## User Requirements + - UI looks "very bad" — needs visual polish and tactile feel ✅ - More themes and theme customization ✅ - Better UI rendering quality ✅ @@ -15,12 +16,14 @@ ## Phase 1 Changes (Completed) ### 1. Font Rendering (`base.css`) + - Added `-webkit-font-smoothing: antialiased` for crisp text on macOS - Added `-moz-osx-font-smoothing: grayscale` for Firefox - Added `text-rendering: optimizeLegibility` for better kerning - Added `scroll-behavior: smooth` for smooth scrolling ### 2. Animation System (`animations.css`) + - Added CSS custom property easing tokens (`--ease-out-expo`, `--ease-spring`, etc.) - Added duration tokens (`--duration-instant` through `--duration-slower`) - Added new keyframes: `fadeIn`, `fadeInScale`, `slideInFromRight/Left/Bottom` @@ -29,6 +32,7 @@ - Added `prefers-reduced-motion: reduce` media query for accessibility ### 3. Utilities (`utilities.css`) + - Added `::selection` styling with theme-aware color - Added global transition defaults for all interactive elements - Added `:focus-visible` ring with theme color @@ -36,6 +40,7 @@ - Suppressed focus ring for components that handle their own ### 4. Shadow/Depth System (`theme.css`) + - Refined `--shadow-xs` with slightly stronger presence - Added new `--shadow-sm` level for subtle elevation - Enhanced `--shadow-md` with deeper, more dramatic depth @@ -43,6 +48,7 @@ - Added new `--shadow-xl` for maximum elevation (modals, floating panels) ### 5. Button Micro-Interactions (`button.css`) + - Added explicit transition for bg-color, border, box-shadow, transform, opacity - Primary: hover now lifts with `--shadow-sm`, active presses with `scale(0.98)` - Ghost: icon color transitions on hover, active presses with `scale(0.97)` @@ -50,11 +56,13 @@ - Disabled states now use `opacity: 0.6` for clearer visual feedback ### 6. Card Polish (`card.css`) + - Upgraded border-radius from `--radius-md` to `--radius-lg` - Added full transition for bg-color, border-color, box-shadow, transform - Hover state now shows subtle border highlight and `--shadow-xs` elevation ### 7. Dialog Animations (`dialog.css`) + - Overlay now uses `backdrop-filter: blur(4px)` for frosted glass effect - Overlay opacity increased from 0.2 to 0.35 for better focus - Content now uses combined `scale(0.96) + translateY(4px)` entrance @@ -63,6 +71,7 @@ - Overlay entrance/exit now animated separately ### 8. Icon Button Interactions (`icon-button.css`) + - Added explicit transitions for bg-color, box-shadow, transform - Ghost variant: icon color now transitions on hover (to `--icon-hover`) - Active state now scales to `0.92` for satisfying tactile press @@ -70,6 +79,7 @@ - Disabled state uses `opacity: 0.5` ### 9. New Themes (3 premium additions) + - **Rosé Pine** — Dreamy, soft palette with purple/rose accents. Very popular community theme. - **Kanagawa** — Japanese-inspired warm palette. Distinctive golden/purple tones based on "The Great Wave." - **Everforest** — Calming green/earth tones nature-inspired palette. Easy on the eyes for long sessions. @@ -77,6 +87,7 @@ All themes include full light + dark variants with seeds, borders, surfaces, text, syntax highlighting, and markdown colors. ## Files Modified + - `packages/ui/src/styles/base.css` — Font rendering - `packages/ui/src/styles/animations.css` — Animation system - `packages/ui/src/styles/utilities.css` — Selection, focus, transitions, scrollbars @@ -91,4 +102,5 @@ All themes include full light + dark variants with seeds, borders, surfaces, tex - `packages/ui/src/theme/default-themes.ts` — Theme registration ## Build Status + ✅ `vite build` passes with zero errors (7.98s) diff --git a/docs/09-temp/ui-redesign-spec.md b/docs/09-temp/ui-redesign-spec.md index 718b5ee2ecfb..159c29b61b8c 100644 --- a/docs/09-temp/ui-redesign-spec.md +++ b/docs/09-temp/ui-redesign-spec.md @@ -1,9 +1,11 @@ # UI Redesign Spec ## Reference Design + See HTML mockup provided by user. Key elements: ### Sidebar + - "New Session" button with icon, primary color border - "RECENT CHATS" section header (uppercase, tracking-wider) - Chat items with icon + title + timestamp @@ -11,11 +13,13 @@ See HTML mockup provided by user. Key elements: - Bottom: plan usage bar ### Message Timeline + - Assistant: Robot icon (32x32 rounded square) + "OPENCODE AI" label (uppercase, primary color, bold) - User: Timestamp + "You" label (accent-cyan color, bold) - User message: Glass panel, rounded-2xl with rounded-tr-none ### Thinking Block + - Collapsible `` with: - Cyan pulsing dot + "Thinking process..." text - Expand/collapse arrow @@ -23,6 +27,7 @@ See HTML mockup provided by user. Key elements: - Border-top separator ### Prompt Input + - Glass panel with backdrop-blur - Model selector pills ("GPT-4o", "Web Search") - Textarea @@ -30,8 +35,10 @@ See HTML mockup provided by user. Key elements: - Bottom bar: keyboard shortcuts + sync status ### Right Activity Bar + - Vertical icon strip: Extensions, Source Control, History - Bottom: Settings + user avatar ### Settings (from screenshot) + - Already looks reasonable, minor polish needed diff --git a/docs/plans/2025-02-26-aurora-design-system.md b/docs/plans/2025-02-26-aurora-design-system.md index 9dd67f391618..7524e0b68084 100644 --- a/docs/plans/2025-02-26-aurora-design-system.md +++ b/docs/plans/2025-02-26-aurora-design-system.md @@ -26,13 +26,13 @@ ### Design Requirements Gathered -| Aspect | Choice | -|--------|--------| -| **Scope** | Unified design language (Web Console + Terminal UI) | -| **Tone** | Luxury Minimal | -| **Color** | Dark-first luxury with luminous accents | -| **Motion** | Confident, tactile, functional | -| **Reference** | Future-forward (Tesla/Rivian interiors) | +| Aspect | Choice | +| ------------- | --------------------------------------------------- | +| **Scope** | Unified design language (Web Console + Terminal UI) | +| **Tone** | Luxury Minimal | +| **Color** | Dark-first luxury with luminous accents | +| **Motion** | Confident, tactile, functional | +| **Reference** | Future-forward (Tesla/Rivian interiors) | ### Core Identity @@ -51,13 +51,13 @@ ### Design Principles -| Principle | Description | Implementation | -|-----------|-------------|----------------| -| **Light as Material** | UI elements emit light rather than receive it | Glows, gradients, luminous borders | -| **Depth through Transparency** | Layers visible through glassmorphism | backdrop-blur, low-opacity backgrounds | -| **Confident Motion** | Every animation serves purpose and feels physical | Spring physics, 200-300ms durations | -| **Chromatic Restraint** | Rich palette but used sparingly | Monochrome base, color for meaning | -| **Unified Language** | Same DNA across Web and TUI | Shared color tokens, adapted to medium | +| Principle | Description | Implementation | +| ------------------------------ | ------------------------------------------------- | -------------------------------------- | +| **Light as Material** | UI elements emit light rather than receive it | Glows, gradients, luminous borders | +| **Depth through Transparency** | Layers visible through glassmorphism | backdrop-blur, low-opacity backgrounds | +| **Confident Motion** | Every animation serves purpose and feels physical | Spring physics, 200-300ms durations | +| **Chromatic Restraint** | Rich palette but used sparingly | Monochrome base, color for meaning | +| **Unified Language** | Same DNA across Web and TUI | Shared color tokens, adapted to medium | --- @@ -70,6 +70,7 @@ Three design approaches were explored before settling on Aurora: **Concept:** Premium materials meet precision engineering. Think machined aluminum bezels, carbon fiber textures, and surgical-grade steel accents. **Web Console:** + ``` ┌─────────────────────────────────────────────────────────────┐ │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ @@ -90,6 +91,7 @@ Three design approaches were explored before settling on Aurora: ``` **TUI Translation:** + ``` ┌─ SESSION: Project Analysis ─────────────── ◈ ────┐ │ │ @@ -108,11 +110,13 @@ Three design approaches were explored before settling on Aurora: ``` **Pros:** + - Distinctive, memorable aesthetic - Strong brand identity ("the tool that feels engineered") - Warm accent prevents cold/sterile feeling **Cons:** + - Carbon texture could feel dated if not executed perfectly - Copper might clash with some terminal color schemes - More complex to implement subtle material effects @@ -126,6 +130,7 @@ Three design approaches were explored before settling on Aurora: **Concept:** Pure light and energy. No physical materials—just gradients, glows, and luminous color that feels alive. Like looking at code through a prism of pure digital light. **Web Console:** + ``` ┌─────────────────────────────────────────────────────────────┐ │ │ @@ -157,6 +162,7 @@ Three design approaches were explored before settling on Aurora: ``` **Light Theme Variant:** + ``` ┌─────────────────────────────────────────────────────────────┐ │ Background: Soft pearl (#FAFAFA) with subtle iridescence │ @@ -167,6 +173,7 @@ Three design approaches were explored before settling on Aurora: ``` **TUI Translation:** + ``` ╭───────────────────────────────────────────────────────╮ │ ● opencode ◐ processing │ @@ -192,17 +199,20 @@ Three design approaches were explored before settling on Aurora: ``` **Key Differentiators:** + - **Depth through light, not shadow** — Elements glow from within rather than casting shadows - **Living gradients** — Subtle color shifts that feel organic, not static - **Ethereal presence** — UI feels like it exists in digital space, weightless **Pros:** + - Truly unique aesthetic (few tools look like this) - Perfectly digital - no physical material metaphors - Light/dark themes can share the same luminous DNA - Scalable: subtle for everyday use, dramatic for hero moments **Cons:** + - Risk of "gaming aesthetic" if not carefully restrained - Gradient animations need to be VERY subtle or becomes distracting - Performance consideration for animated gradients @@ -228,46 +238,46 @@ Since Approach B (Aurora) was selected immediately, a third approach was not ful :root[data-theme="aurora-dark"] { /* ─── VOID BACKGROUNDS ─── */ - --void-deepest: #050508; /* True dark, almost black */ - --void-deep: #0A0A0F; /* Primary background */ - --void-base: #0F0F14; /* Card backgrounds */ - --void-elevated: #14141A; /* Elevated surfaces */ - --void-hover: #1A1A22; /* Hover states */ + --void-deepest: #050508; /* True dark, almost black */ + --void-deep: #0a0a0f; /* Primary background */ + --void-base: #0f0f14; /* Card backgrounds */ + --void-elevated: #14141a; /* Elevated surfaces */ + --void-hover: #1a1a22; /* Hover states */ /* ─── SURFACE GLASS ─── */ - --glass-subtle: rgba(255, 255, 255, 0.02); - --glass-light: rgba(255, 255, 255, 0.04); - --glass-medium: rgba(255, 255, 255, 0.06); - --glass-strong: rgba(255, 255, 255, 0.08); + --glass-subtle: rgba(255, 255, 255, 0.02); + --glass-light: rgba(255, 255, 255, 0.04); + --glass-medium: rgba(255, 255, 255, 0.06); + --glass-strong: rgba(255, 255, 255, 0.08); /* ─── LUMINOUS SPECTRUM ─── */ - --aurora-cyan: #00D4FF; /* Primary accent */ - --aurora-cyan-soft: #00A3CC; /* Cyan muted */ - --aurora-cyan-glow: rgba(0, 212, 255, 0.15); + --aurora-cyan: #00d4ff; /* Primary accent */ + --aurora-cyan-soft: #00a3cc; /* Cyan muted */ + --aurora-cyan-glow: rgba(0, 212, 255, 0.15); - --aurora-violet: #A78BFA; /* Secondary accent */ - --aurora-violet-soft:#8B6ED9; - --aurora-violet-glow:rgba(167, 139, 250, 0.15); + --aurora-violet: #a78bfa; /* Secondary accent */ + --aurora-violet-soft: #8b6ed9; + --aurora-violet-glow: rgba(167, 139, 250, 0.15); - --aurora-rose: #FF6B9D; /* Tertiary / attention */ - --aurora-rose-soft: #D94A7B; - --aurora-rose-glow: rgba(255, 107, 157, 0.15); + --aurora-rose: #ff6b9d; /* Tertiary / attention */ + --aurora-rose-soft: #d94a7b; + --aurora-rose-glow: rgba(255, 107, 157, 0.15); - --aurora-amber: #FFBB33; /* Warning / warm accent */ - --aurora-green: #4ADE80; /* Success */ - --aurora-red: #F87171; /* Error / danger */ + --aurora-amber: #ffbb33; /* Warning / warm accent */ + --aurora-green: #4ade80; /* Success */ + --aurora-red: #f87171; /* Error / danger */ /* ─── TEXT HIERARCHY ─── */ - --text-primary: #F5F5F7; /* Bright white */ - --text-secondary: #A1A1AA; /* Muted gray */ - --text-tertiary: #71717A; /* Subtle gray */ - --text-disabled: #3F3F46; /* Very dim */ + --text-primary: #f5f5f7; /* Bright white */ + --text-secondary: #a1a1aa; /* Muted gray */ + --text-tertiary: #71717a; /* Subtle gray */ + --text-disabled: #3f3f46; /* Very dim */ /* ─── BORDER LUMINANCE ─── */ - --border-subtle: rgba(255, 255, 255, 0.06); - --border-default: rgba(255, 255, 255, 0.10); - --border-strong: rgba(255, 255, 255, 0.15); - --border-glow: var(--aurora-cyan); + --border-subtle: rgba(255, 255, 255, 0.06); + --border-default: rgba(255, 255, 255, 0.1); + --border-strong: rgba(255, 255, 255, 0.15); + --border-glow: var(--aurora-cyan); } ``` @@ -280,46 +290,46 @@ Since Approach B (Aurora) was selected immediately, a third approach was not ful :root[data-theme="aurora-light"] { /* ─── PEARL BACKGROUNDS ─── */ - --void-deepest: #FFFFFF; - --void-deep: #FAFAFA; - --void-base: #F4F4F5; - --void-elevated: #FFFFFF; - --void-hover: #E4E4E7; + --void-deepest: #ffffff; + --void-deep: #fafafa; + --void-base: #f4f4f5; + --void-elevated: #ffffff; + --void-hover: #e4e4e7; /* ─── SURFACE FROST ─── */ - --glass-subtle: rgba(0, 0, 0, 0.02); - --glass-light: rgba(0, 0, 0, 0.04); - --glass-medium: rgba(0, 0, 0, 0.06); - --glass-strong: rgba(0, 0, 0, 0.08); + --glass-subtle: rgba(0, 0, 0, 0.02); + --glass-light: rgba(0, 0, 0, 0.04); + --glass-medium: rgba(0, 0, 0, 0.06); + --glass-strong: rgba(0, 0, 0, 0.08); /* ─── LUMINOUS SPECTRUM (deeper for contrast) ─── */ - --aurora-cyan: #0891B2; /* Deeper cyan */ - --aurora-cyan-soft: #06B6D4; - --aurora-cyan-glow: rgba(8, 145, 178, 0.10); + --aurora-cyan: #0891b2; /* Deeper cyan */ + --aurora-cyan-soft: #06b6d4; + --aurora-cyan-glow: rgba(8, 145, 178, 0.1); - --aurora-violet: #7C3AED; /* Richer violet */ - --aurora-violet-soft:#8B5CF6; - --aurora-violet-glow:rgba(124, 58, 237, 0.10); + --aurora-violet: #7c3aed; /* Richer violet */ + --aurora-violet-soft: #8b5cf6; + --aurora-violet-glow: rgba(124, 58, 237, 0.1); - --aurora-rose: #DB2777; /* Deeper rose */ - --aurora-rose-soft: #EC4899; - --aurora-rose-glow: rgba(219, 39, 119, 0.10); + --aurora-rose: #db2777; /* Deeper rose */ + --aurora-rose-soft: #ec4899; + --aurora-rose-glow: rgba(219, 39, 119, 0.1); - --aurora-amber: #D97706; - --aurora-green: #16A34A; - --aurora-red: #DC2626; + --aurora-amber: #d97706; + --aurora-green: #16a34a; + --aurora-red: #dc2626; /* ─── TEXT HIERARCHY ─── */ - --text-primary: #18181B; - --text-secondary: #52525B; - --text-tertiary: #A1A1AA; - --text-disabled: #D4D4D8; + --text-primary: #18181b; + --text-secondary: #52525b; + --text-tertiary: #a1a1aa; + --text-disabled: #d4d4d8; /* ─── BORDER LUMINANCE ─── */ - --border-subtle: rgba(0, 0, 0, 0.06); - --border-default: rgba(0, 0, 0, 0.10); - --border-strong: rgba(0, 0, 0, 0.15); - --border-glow: var(--aurora-cyan); + --border-subtle: rgba(0, 0, 0, 0.06); + --border-default: rgba(0, 0, 0, 0.1); + --border-strong: rgba(0, 0, 0, 0.15); + --border-glow: var(--aurora-cyan); } ``` @@ -335,9 +345,9 @@ export const auroraDark = { backgroundMenu: RGBA.fromHex("#1A1A22"), // Aurora spectrum - primary: RGBA.fromHex("#00D4FF"), // Cyan - secondary: RGBA.fromHex("#A78BFA"), // Violet - accent: RGBA.fromHex("#FF6B9D"), // Rose + primary: RGBA.fromHex("#00D4FF"), // Cyan + secondary: RGBA.fromHex("#A78BFA"), // Violet + accent: RGBA.fromHex("#FF6B9D"), // Rose // Semantic success: RGBA.fromHex("#4ADE80"), @@ -355,13 +365,13 @@ export const auroraDark = { borderSubtle: RGBA.fromHex("#14141A"), // Syntax highlighting (aurora-themed) - syntaxKeyword: RGBA.fromHex("#A78BFA"), // Violet - syntaxFunction: RGBA.fromHex("#00D4FF"), // Cyan - syntaxString: RGBA.fromHex("#4ADE80"), // Green - syntaxNumber: RGBA.fromHex("#FF6B9D"), // Rose - syntaxComment: RGBA.fromHex("#71717A"), // Muted - syntaxVariable: RGBA.fromHex("#F5F5F7"), // White - syntaxType: RGBA.fromHex("#FFBB33"), // Amber + syntaxKeyword: RGBA.fromHex("#A78BFA"), // Violet + syntaxFunction: RGBA.fromHex("#00D4FF"), // Cyan + syntaxString: RGBA.fromHex("#4ADE80"), // Green + syntaxNumber: RGBA.fromHex("#FF6B9D"), // Rose + syntaxComment: RGBA.fromHex("#71717A"), // Muted + syntaxVariable: RGBA.fromHex("#F5F5F7"), // White + syntaxType: RGBA.fromHex("#FFBB33"), // Amber syntaxOperator: RGBA.fromHex("#A1A1AA"), syntaxPunctuation: RGBA.fromHex("#71717A"), @@ -386,13 +396,11 @@ export const auroraDark = { :root { /* ─── PRIMARY: Code & Interface ─── */ - --font-mono: "JetBrains Mono", "SF Mono", "Fira Code", - "Cascadia Code", monospace; + --font-mono: "JetBrains Mono", "SF Mono", "Fira Code", "Cascadia Code", monospace; /* ─── DISPLAY: Headers & Hero Text ─── */ /* Option A: Geometric (Future-forward) */ - --font-display: "Geist", "Inter", "SF Pro Display", - system-ui, sans-serif; + --font-display: "Geist", "Inter", "SF Pro Display", system-ui, sans-serif; /* Option B: More distinctive (if we want stronger brand) */ /* --font-display: "Space Grotesk", "Outfit", sans-serif; */ @@ -408,36 +416,36 @@ export const auroraDark = { /* ─── MODULAR SCALE: 1.250 (Major Third) ─── */ :root { - --text-xs: 0.64rem; /* 10.24px - Labels, captions */ - --text-sm: 0.8rem; /* 12.8px - Small UI text */ - --text-base: 1rem; /* 16px - Body text */ - --text-md: 1.25rem; /* 20px - Large body */ - --text-lg: 1.563rem; /* 25px - Section headers */ - --text-xl: 1.953rem; /* 31.25px - Page headers */ - --text-2xl: 2.441rem; /* 39px - Hero subheads */ - --text-3xl: 3.052rem; /* 48.8px - Hero headlines */ - --text-4xl: 3.815rem; /* 61px - Display text */ + --text-xs: 0.64rem; /* 10.24px - Labels, captions */ + --text-sm: 0.8rem; /* 12.8px - Small UI text */ + --text-base: 1rem; /* 16px - Body text */ + --text-md: 1.25rem; /* 20px - Large body */ + --text-lg: 1.563rem; /* 25px - Section headers */ + --text-xl: 1.953rem; /* 31.25px - Page headers */ + --text-2xl: 2.441rem; /* 39px - Hero subheads */ + --text-3xl: 3.052rem; /* 48.8px - Hero headlines */ + --text-4xl: 3.815rem; /* 61px - Display text */ /* ─── LINE HEIGHTS ─── */ - --leading-none: 1; - --leading-tight: 1.25; - --leading-snug: 1.375; + --leading-none: 1; + --leading-tight: 1.25; + --leading-snug: 1.375; --leading-normal: 1.5; --leading-relaxed: 1.625; - --leading-loose: 1.75; + --leading-loose: 1.75; /* ─── LETTER SPACING ─── */ --tracking-tighter: -0.05em; - --tracking-tight: -0.025em; - --tracking-normal: 0; - --tracking-wide: 0.025em; - --tracking-wider: 0.05em; + --tracking-tight: -0.025em; + --tracking-normal: 0; + --tracking-wide: 0.025em; + --tracking-wider: 0.05em; /* ─── FONT WEIGHTS ─── */ - --weight-normal: 400; - --weight-medium: 500; + --weight-normal: 400; + --weight-medium: 500; --weight-semibold: 600; - --weight-bold: 700; + --weight-bold: 700; } ``` @@ -493,7 +501,7 @@ export const auroraDark = { font-family: var(--font-mono); font-size: var(--text-sm); line-height: var(--leading-normal); - font-variant-ligatures: contextual; /* Enable code ligatures */ + font-variant-ligatures: contextual; /* Enable code ligatures */ } .text-label { @@ -515,58 +523,58 @@ export const auroraDark = { ═══════════════════════════════════════════════════════════ */ :root { - --space-px: 1px; - --space-0: 0; - --space-0.5: 0.125rem; /* 2px */ - --space-1: 0.25rem; /* 4px */ - --space-1.5: 0.375rem; /* 6px */ - --space-2: 0.5rem; /* 8px */ - --space-2.5: 0.625rem; /* 10px */ - --space-3: 0.75rem; /* 12px */ - --space-3.5: 0.875rem; /* 14px */ - --space-4: 1rem; /* 16px */ - --space-5: 1.25rem; /* 20px */ - --space-6: 1.5rem; /* 24px */ - --space-7: 1.75rem; /* 28px */ - --space-8: 2rem; /* 32px */ - --space-9: 2.25rem; /* 36px */ - --space-10: 2.5rem; /* 40px */ - --space-11: 2.75rem; /* 44px */ - --space-12: 3rem; /* 48px */ - --space-14: 3.5rem; /* 56px */ - --space-16: 4rem; /* 64px */ - --space-20: 5rem; /* 80px */ - --space-24: 6rem; /* 96px */ - --space-28: 7rem; /* 112px */ - --space-32: 8rem; /* 128px */ + --space-px: 1px; + --space-0: 0; + --space-0.5: 0.125rem; /* 2px */ + --space-1: 0.25rem; /* 4px */ + --space-1.5: 0.375rem; /* 6px */ + --space-2: 0.5rem; /* 8px */ + --space-2.5: 0.625rem; /* 10px */ + --space-3: 0.75rem; /* 12px */ + --space-3.5: 0.875rem; /* 14px */ + --space-4: 1rem; /* 16px */ + --space-5: 1.25rem; /* 20px */ + --space-6: 1.5rem; /* 24px */ + --space-7: 1.75rem; /* 28px */ + --space-8: 2rem; /* 32px */ + --space-9: 2.25rem; /* 36px */ + --space-10: 2.5rem; /* 40px */ + --space-11: 2.75rem; /* 44px */ + --space-12: 3rem; /* 48px */ + --space-14: 3.5rem; /* 56px */ + --space-16: 4rem; /* 64px */ + --space-20: 5rem; /* 80px */ + --space-24: 6rem; /* 96px */ + --space-28: 7rem; /* 112px */ + --space-32: 8rem; /* 128px */ /* ─── SEMANTIC SPACING ─── */ - --gap-xs: var(--space-1); /* 4px - Inline elements */ - --gap-sm: var(--space-2); /* 8px - Tight groups */ - --gap-md: var(--space-4); /* 16px - Default gap */ - --gap-lg: var(--space-6); /* 24px - Section spacing */ - --gap-xl: var(--space-8); /* 32px - Major sections */ - --gap-2xl: var(--space-12); /* 48px - Page sections */ + --gap-xs: var(--space-1); /* 4px - Inline elements */ + --gap-sm: var(--space-2); /* 8px - Tight groups */ + --gap-md: var(--space-4); /* 16px - Default gap */ + --gap-lg: var(--space-6); /* 24px - Section spacing */ + --gap-xl: var(--space-8); /* 32px - Major sections */ + --gap-2xl: var(--space-12); /* 48px - Page sections */ /* ─── COMPONENT PADDING ─── */ - --padding-button: var(--space-2) var(--space-4); + --padding-button: var(--space-2) var(--space-4); --padding-button-sm: var(--space-1.5) var(--space-3); --padding-button-lg: var(--space-3) var(--space-6); - --padding-card: var(--space-5); - --padding-card-sm: var(--space-3); - --padding-card-lg: var(--space-6); + --padding-card: var(--space-5); + --padding-card-sm: var(--space-3); + --padding-card-lg: var(--space-6); - --padding-input: var(--space-2.5) var(--space-3); + --padding-input: var(--space-2.5) var(--space-3); /* ─── BORDER RADIUS ─── */ - --radius-none: 0; - --radius-sm: 0.25rem; /* 4px - Small elements */ - --radius-md: 0.5rem; /* 8px - Buttons, inputs */ - --radius-lg: 0.75rem; /* 12px - Cards */ - --radius-xl: 1rem; /* 16px - Large cards */ - --radius-2xl: 1.5rem; /* 24px - Modals */ - --radius-full: 9999px; /* Pills, avatars */ + --radius-none: 0; + --radius-sm: 0.25rem; /* 4px - Small elements */ + --radius-md: 0.5rem; /* 8px - Buttons, inputs */ + --radius-lg: 0.75rem; /* 12px - Cards */ + --radius-xl: 1rem; /* 16px - Large cards */ + --radius-2xl: 1.5rem; /* 24px - Modals */ + --radius-full: 9999px; /* Pills, avatars */ } ``` @@ -581,36 +589,36 @@ export const auroraDark = { :root { /* ─── DURATION ─── */ - --duration-instant: 50ms; - --duration-fast: 150ms; - --duration-normal: 250ms; - --duration-slow: 350ms; - --duration-slower: 500ms; - --duration-slowest: 700ms; + --duration-instant: 50ms; + --duration-fast: 150ms; + --duration-normal: 250ms; + --duration-slow: 350ms; + --duration-slower: 500ms; + --duration-slowest: 700ms; /* ─── EASING (CSS) ─── */ - --ease-linear: linear; - --ease-in: cubic-bezier(0.4, 0, 1, 1); - --ease-out: cubic-bezier(0, 0, 0.2, 1); - --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --ease-linear: linear; + --ease-in: cubic-bezier(0.4, 0, 1, 1); + --ease-out: cubic-bezier(0, 0, 0.2, 1); + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); /* ─── SPRING EASING (For Motion library) ─── */ - --spring-bounce: cubic-bezier(0.34, 1.56, 0.64, 1); - --spring-smooth: cubic-bezier(0.22, 1, 0.36, 1); - --spring-snappy: cubic-bezier(0.16, 1, 0.3, 1); + --spring-bounce: cubic-bezier(0.34, 1.56, 0.64, 1); + --spring-smooth: cubic-bezier(0.22, 1, 0.36, 1); + --spring-snappy: cubic-bezier(0.16, 1, 0.3, 1); /* ─── SEMANTIC TRANSITIONS ─── */ - --transition-colors: color var(--duration-fast) var(--ease-out), - background-color var(--duration-fast) var(--ease-out), - border-color var(--duration-fast) var(--ease-out); + --transition-colors: + color var(--duration-fast) var(--ease-out), background-color var(--duration-fast) var(--ease-out), + border-color var(--duration-fast) var(--ease-out); - --transition-opacity: opacity var(--duration-normal) var(--ease-out); + --transition-opacity: opacity var(--duration-normal) var(--ease-out); --transition-transform: transform var(--duration-normal) var(--spring-smooth); - --transition-all: all var(--duration-normal) var(--spring-smooth); + --transition-all: all var(--duration-normal) var(--spring-smooth); - --transition-glow: box-shadow var(--duration-slow) var(--ease-out); + --transition-glow: box-shadow var(--duration-slow) var(--ease-out); } ``` @@ -638,8 +646,12 @@ export const auroraDark = { ```css /* ─── ENTRY ANIMATIONS ─── */ @keyframes aurora-fade-in { - from { opacity: 0; } - to { opacity: 1; } + from { + opacity: 0; + } + to { + opacity: 1; + } } @keyframes aurora-scale-in { @@ -666,7 +678,8 @@ export const auroraDark = { /* ─── GLOW PULSE (for loading/processing) ─── */ @keyframes aurora-pulse { - 0%, 100% { + 0%, + 100% { opacity: 1; box-shadow: 0 0 0 0 var(--aurora-cyan-glow); } @@ -688,7 +701,8 @@ export const auroraDark = { /* ─── GRADIENT DRIFT (for hero backgrounds) ─── */ @keyframes aurora-drift { - 0%, 100% { + 0%, + 100% { background-position: 0% 50%; } 50% { @@ -912,7 +926,7 @@ This is the MOST IMPORTANT component—the main chat input: ### 7.5 Message Bubbles -``` +```` ┌─────────────────────────────────────────────────────────────┐ │ AURORA MESSAGE BUBBLES │ ├─────────────────────────────────────────────────────────────┤ @@ -960,7 +974,7 @@ This is the MOST IMPORTANT component—the main chat input: │ • Typing indicator: 3 dots with staggered pulse │ │ │ └─────────────────────────────────────────────────────────────┘ -``` +```` --- @@ -1057,7 +1071,7 @@ This is the MOST IMPORTANT component—the main chat input: ## Part 8: TUI (Terminal) Component Translations -The terminal can't do true 3D or blur, but we can create the *feeling* of Aurora through: +The terminal can't do true 3D or blur, but we can create the _feeling_ of Aurora through: ### 8.1 Aurora TUI Character Palette @@ -1112,7 +1126,7 @@ The terminal can't do true 3D or blur, but we can create the *feeling* of Aurora ### 8.2 TUI Layout Templates -``` +```` ┌─────────────────────────────────────────────────────────────┐ │ AURORA TUI — MAIN SESSION VIEW │ ├─────────────────────────────────────────────────────────────┤ @@ -1152,7 +1166,7 @@ COLOR MAPPING: • Progress filled: aurora-cyan • Progress empty: border-subtle • Footer: text-muted -``` +```` ### 8.3 TUI Dialog Example @@ -1623,53 +1637,57 @@ Show a realistic diff of a TypeScript React component being refactored. ### Quick Reference Card -| Aspect | Specification | -|--------|---------------| -| **Primary Accent** | `#00D4FF` (Electric Cyan) | -| **Secondary** | `#A78BFA` (Soft Violet) | -| **Tertiary** | `#FF6B9D` (Rose) | -| **Dark Background** | `#0A0A0F` (Void) | -| **Light Background** | `#FAFAFA` (Pearl) | -| **Border Style** | Subtle glow, not hard edges | -| **Glass Effect** | `rgba(255,255,255,0.04)` + `blur(12px)` | -| **Border Radius** | `8px` buttons, `12px` cards, `24px` modals | -| **Animation Duration** | 150-350ms | -| **Easing** | Spring-based (`cubic-bezier(0.22, 1, 0.36, 1)`) | -| **Code Font** | JetBrains Mono | -| **UI Font** | Geist / Inter | +| Aspect | Specification | +| ---------------------- | ----------------------------------------------- | +| **Primary Accent** | `#00D4FF` (Electric Cyan) | +| **Secondary** | `#A78BFA` (Soft Violet) | +| **Tertiary** | `#FF6B9D` (Rose) | +| **Dark Background** | `#0A0A0F` (Void) | +| **Light Background** | `#FAFAFA` (Pearl) | +| **Border Style** | Subtle glow, not hard edges | +| **Glass Effect** | `rgba(255,255,255,0.04)` + `blur(12px)` | +| **Border Radius** | `8px` buttons, `12px` cards, `24px` modals | +| **Animation Duration** | 150-350ms | +| **Easing** | Spring-based (`cubic-bezier(0.22, 1, 0.36, 1)`) | +| **Code Font** | JetBrains Mono | +| **UI Font** | Geist / Inter | ### Component Mapping: Web → TUI -| Web Component | TUI Equivalent | -|---------------|----------------| -| Cyan glow border | Double-line border `═══` | -| Glassmorphism card | Rounded box `╭─╮ │ ╰─╯` | -| Hover lift effect | Highlight color change | -| Loading shimmer | Block gradient `░▒▓█` | -| Pulsing glow | Braille spinner `⠋⠙⠹...` or `◌◍◎●` | -| User cyan tint | Cyan foreground + `┃` pipe | -| Assistant violet border | Violet `│` left margin | +| Web Component | TUI Equivalent | +| ----------------------- | ---------------------------------- | +| Cyan glow border | Double-line border `═══` | +| Glassmorphism card | Rounded box `╭─╮ │ ╰─╯` | +| Hover lift effect | Highlight color change | +| Loading shimmer | Block gradient `░▒▓█` | +| Pulsing glow | Braille spinner `⠋⠙⠹...` or `◌◍◎●` | +| User cyan tint | Cyan foreground + `┃` pipe | +| Assistant violet border | Violet `│` left margin | ### Implementation Phases (Recommended) #### Phase 1: Theme Foundation + - [ ] Create `aurora-dark.json` and `aurora-light.json` theme files - [ ] Add to TUI theme selector - [ ] Update CSS custom properties for web console #### Phase 2: Core Components + - [ ] Buttons (primary, secondary, ghost, danger) - [ ] Input fields with focus glow - [ ] Cards with glass effect - [ ] Modals with backdrop blur #### Phase 3: Chat Interface + - [ ] Message bubbles (user/assistant) - [ ] Prompt input (hero component) - [ ] Loading/streaming states - [ ] Code blocks with Aurora syntax theme #### Phase 4: Motion Polish + - [ ] Spring animations library integration - [ ] Enter/exit transitions - [ ] Micro-interactions @@ -1680,15 +1698,18 @@ Show a realistic diff of a TypeScript React component being refactored. Based on analysis of the codebase, these are the key files to modify: **TUI Theme System:** + - `packages/opencode/src/cli/cmd/tui/context/theme.tsx` — Theme provider and color types - `packages/opencode/src/cli/cmd/tui/context/theme/` — Theme JSON files (add aurora-dark.json, aurora-light.json) **TUI Components:** + - `packages/opencode/src/cli/cmd/tui/routes/session/index.tsx` — Main session view - `packages/opencode/src/cli/cmd/tui/component/prompt/index.tsx` — Prompt input component - `packages/opencode/src/cli/cmd/tui/component/dialog-*.tsx` — All dialog components **Web Console:** + - `packages/console/app/src/style/token/color.css` — CSS color tokens - `packages/console/app/src/routes/index.css` — Landing page styles - `packages/console/app/src/component/` — Shared components @@ -1772,7 +1793,7 @@ The Aurora redesign is successful when: ## Part 11: Accessibility & Review Amendments -*This section addresses feedback from the UI/UX Pro Max review and adds critical accessibility requirements.* +_This section addresses feedback from the UI/UX Pro Max review and adds critical accessibility requirements._ ### 11.1 Motion Sickness Prevention (CRITICAL) @@ -1808,6 +1829,7 @@ The Aurora redesign is successful when: ``` **Guidelines:** + - ❌ **NEVER** use infinite animations on backgrounds or decorative elements - ✅ Continuous animation ONLY permitted during active loading states - ✅ Aurora drift effect should be opt-in, disabled by default @@ -1824,7 +1846,7 @@ The Aurora redesign is successful when: ```css /* Add to spacing system */ :root { - --max-prose-width: 70ch; /* 65-75 characters optimal */ + --max-prose-width: 70ch; /* 65-75 characters optimal */ } /* Apply to text containers */ @@ -1861,20 +1883,20 @@ The Aurora redesign is successful when: ```css :root[data-theme="aurora-light"] { /* ─── ADJUSTED GLASS OPACITIES ─── */ - --glass-subtle: rgba(0, 0, 0, 0.03); /* was 0.02 */ - --glass-light: rgba(0, 0, 0, 0.06); /* was 0.04 */ - --glass-medium: rgba(0, 0, 0, 0.09); /* was 0.06 */ - --glass-strong: rgba(0, 0, 0, 0.12); /* was 0.08 */ + --glass-subtle: rgba(0, 0, 0, 0.03); /* was 0.02 */ + --glass-light: rgba(0, 0, 0, 0.06); /* was 0.04 */ + --glass-medium: rgba(0, 0, 0, 0.09); /* was 0.06 */ + --glass-strong: rgba(0, 0, 0, 0.12); /* was 0.08 */ /* ─── STRONGER BORDERS ─── */ - --border-subtle: rgba(0, 0, 0, 0.08); /* was 0.06 */ - --border-default: rgba(0, 0, 0, 0.12); /* was 0.10 */ - --border-strong: rgba(0, 0, 0, 0.18); /* was 0.15 */ + --border-subtle: rgba(0, 0, 0, 0.08); /* was 0.06 */ + --border-default: rgba(0, 0, 0, 0.12); /* was 0.10 */ + --border-strong: rgba(0, 0, 0, 0.18); /* was 0.15 */ /* ─── SUBTLE SHADOWS (light mode only) ─── */ - --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); - --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.08); - --shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.10); + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); + --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.08); + --shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.1); } /* Apply shadows to cards in light mode only */ @@ -1885,13 +1907,13 @@ The Aurora redesign is successful when: **Contrast Verification:** -| Text | Background | Ratio | Status | -|------|------------|-------|--------| -| `#18181B` | `#FAFAFA` | 16.2:1 | ✅ Pass | -| `#52525B` | `#FAFAFA` | 7.4:1 | ✅ Pass | -| `#A1A1AA` | `#FAFAFA` | 3.0:1 | ⚠️ Large text only | -| `#F5F5F7` | `#0A0A0F` | 19.6:1 | ✅ Pass | -| `#A1A1AA` | `#0A0A0F` | 8.5:1 | ✅ Pass | +| Text | Background | Ratio | Status | +| --------- | ---------- | ------ | ------------------ | +| `#18181B` | `#FAFAFA` | 16.2:1 | ✅ Pass | +| `#52525B` | `#FAFAFA` | 7.4:1 | ✅ Pass | +| `#A1A1AA` | `#FAFAFA` | 3.0:1 | ⚠️ Large text only | +| `#F5F5F7` | `#0A0A0F` | 19.6:1 | ✅ Pass | +| `#A1A1AA` | `#0A0A0F` | 8.5:1 | ✅ Pass | --- @@ -1924,6 +1946,7 @@ a { ``` **Icon Standards:** + - ✅ **Required:** Lucide Icons (React: `lucide-react`, Web: `lucide`) - ✅ **Acceptable:** Heroicons, Phosphor Icons - ❌ **Forbidden:** Emoji as UI icons (OS rendering inconsistency) @@ -1936,24 +1959,28 @@ a { Before implementation, verify: #### Color & Contrast + - [ ] All body text has 4.5:1 minimum contrast ratio - [ ] All large text (18px+) has 3:1 minimum contrast ratio - [ ] Focus indicators are clearly visible (2px cyan outline) - [ ] Error states use red AND icon/text (not color alone) #### Motion & Animation + - [ ] `prefers-reduced-motion` media query implemented - [ ] No infinite animations on decorative elements - [ ] Loading animations can be paused or are under 5s - [ ] No flashing content (3 flashes per second limit) #### Interaction + - [ ] All interactive elements have `cursor: pointer` - [ ] Touch targets are minimum 44x44px - [ ] Keyboard navigation follows visual order - [ ] Focus states are distinct from hover states #### Typography + - [ ] Minimum 16px body text (mobile) - [ ] Line height minimum 1.5 for body text - [ ] Line length limited to 70ch for prose @@ -1963,15 +1990,15 @@ Before implementation, verify: ### Review Response Summary -| Feedback Item | Severity | Action Taken | -|---------------|----------|--------------| -| Motion sickness / `prefers-reduced-motion` | CRITICAL | Added §11.1 with full CSS implementation | -| Line length 65-75ch | HIGH | Added §11.2 with `--max-prose-width: 70ch` | -| Light mode glass contrast | CRITICAL | Added §11.3 with adjusted opacity values | -| `cursor-pointer` mandate | MEDIUM | Added §11.4 with interactive patterns | -| SVG icons only | MEDIUM | Added §11.4 with Lucide Icons mandate | -| WCAG compliance | — | Added §11.5 checklist | +| Feedback Item | Severity | Action Taken | +| ------------------------------------------ | -------- | ------------------------------------------ | +| Motion sickness / `prefers-reduced-motion` | CRITICAL | Added §11.1 with full CSS implementation | +| Line length 65-75ch | HIGH | Added §11.2 with `--max-prose-width: 70ch` | +| Light mode glass contrast | CRITICAL | Added §11.3 with adjusted opacity values | +| `cursor-pointer` mandate | MEDIUM | Added §11.4 with interactive patterns | +| SVG icons only | MEDIUM | Added §11.4 with Lucide Icons mandate | +| WCAG compliance | — | Added §11.5 checklist | --- -*Review incorporated from: UI/UX Pro Max analysis (2025-02-26)* +_Review incorporated from: UI/UX Pro Max analysis (2025-02-26)_ diff --git a/docs/plans/2025-02-27-aurora-design-review.md b/docs/plans/2025-02-27-aurora-design-review.md index e216f5e40fe9..f25eb52e54e5 100644 --- a/docs/plans/2025-02-27-aurora-design-review.md +++ b/docs/plans/2025-02-27-aurora-design-review.md @@ -19,14 +19,14 @@ The Aurora Design System spec is comprehensive and well-structured, but **virtua ### 1.1 Spec Assumes Manual CSS Variables — Codebase Uses Algorithmic Generation -| Aspect | Aurora Spec | Current Codebase | -|--------|-------------|------------------| -| **Token naming** | `--void-*`, `--glass-*`, `--aurora-*` | `--background-*`, `--surface-*`, `--text-*`, `--border-*` | -| **Color generation** | Manual hex values per token | OKLCH seed-based (9 seeds → 200+ tokens automatically) | -| **Theme format** | Custom JSON with `defs` + `theme` | Structured JSON with `seeds` + `overrides` per variant | -| **Theme count** | 2 (dark + light) | 20 Desktop themes + 33 TUI themes | -| **Color space** | Hex (#RRGGBB) | OKLCH (perceptually uniform) with hex fallback | -| **Dark/Light** | Separate token blocks | Single theme with `light` + `dark` variant objects | +| Aspect | Aurora Spec | Current Codebase | +| -------------------- | ------------------------------------- | --------------------------------------------------------- | +| **Token naming** | `--void-*`, `--glass-*`, `--aurora-*` | `--background-*`, `--surface-*`, `--text-*`, `--border-*` | +| **Color generation** | Manual hex values per token | OKLCH seed-based (9 seeds → 200+ tokens automatically) | +| **Theme format** | Custom JSON with `defs` + `theme` | Structured JSON with `seeds` + `overrides` per variant | +| **Theme count** | 2 (dark + light) | 20 Desktop themes + 33 TUI themes | +| **Color space** | Hex (#RRGGBB) | OKLCH (perceptually uniform) with hex fallback | +| **Dark/Light** | Separate token blocks | Single theme with `light` + `dark` variant objects | **Verdict:** The Aurora spec's CSS variable approach (`--void-deep`, `--aurora-cyan`, etc.) **cannot be used directly**. Instead, Aurora must be expressed as seed colors + overrides within the existing `DesktopTheme` schema for Desktop UI, and as a `defs` + `theme` mapping for TUI. @@ -36,10 +36,10 @@ The Aurora Design System spec is comprehensive and well-structured, but **virtua The codebase has **two independent theme systems** that must both receive Aurora: -| System | Location | Format | Count | -|--------|----------|--------|-------| -| **Desktop/Web UI** | `packages/ui/src/theme/themes/*.json` | `DesktopTheme` (seeds + overrides) | 20 themes | -| **TUI (Terminal)** | `packages/opencode/src/cli/cmd/tui/context/theme/*.json` | `defs` + `theme` mapping | 33 themes | +| System | Location | Format | Count | +| ------------------ | -------------------------------------------------------- | ---------------------------------- | --------- | +| **Desktop/Web UI** | `packages/ui/src/theme/themes/*.json` | `DesktopTheme` (seeds + overrides) | 20 themes | +| **TUI (Terminal)** | `packages/opencode/src/cli/cmd/tui/context/theme/*.json` | `defs` + `theme` mapping | 33 themes | The Aurora spec provides a single unified design language but **doesn't distinguish** between these two systems. Implementation must create separate theme files for each. @@ -54,6 +54,7 @@ The Aurora spec provides a single unified design language but **doesn't distingu **Current State:** No Aurora color tokens exist anywhere in the codebase. Searched for "aurora" across all packages — zero results (the "aura" theme is unrelated). **What Exists Instead:** + - 20 desktop themes with OKLCH-generated color scales - Primitive color palette in `colors.css` (~600+ CSS variables: gray, smoke, yuzu, cobalt, apple, ember, solaris, lilac, coral, mint, blue, ink, amber) - Semantic tokens in `theme.css` (~300+ variables) @@ -69,6 +70,7 @@ The Aurora spec provides a single unified design language but **doesn't distingu | `--text-primary: #F5F5F7` | `--text-base` | ✅ Auto-generated from neutral seed | **Action Required:** Create `aurora.json` theme files for both Desktop UI and TUI with: + - Desktop: Map Aurora hex colors to 9 OKLCH seeds + extensive overrides for exact color matching - TUI: Create `defs` + `theme` mapping with Aurora colors @@ -112,22 +114,23 @@ The Aurora spec provides a single unified design language but **doesn't distingu **Current State:** -| Feature | Aurora Spec | Current | Status | -|---------|-------------|---------|--------| -| Duration tokens | 6 levels (50-700ms) | 5 levels (75-450ms) | 🟡 Close | -| Spring easing | `cubic-bezier(0.22, 1, 0.36, 1)` | `--ease-spring: cubic-bezier(0.22, 1, 0.36, 1)` | ✅ Exact match | -| Smooth easing | `cubic-bezier(0.34, 1.56, 0.64, 1)` | `--ease-smooth: cubic-bezier(0.16, 1, 0.3, 1)` | 🟡 Similar | -| fadeIn keyframe | opacity 0→1 | `fadeIn` exists | ✅ Matches | -| scaleIn keyframe | scale(0.95) + opacity | `fadeInScale` exists | ✅ Matches | -| slideUp keyframe | translateY(8px) + opacity | `fadeUp` with translateY(5px) | 🟡 Close | -| Glow pulse | box-shadow aurora-cyan-glow | `subtleGlow` exists | 🟡 Different color | -| Shimmer | background-position sweep | `shimmer` exists | ✅ Matches | -| Aurora drift | slow gradient background shift | ❌ Not implemented | 🔴 Missing | -| `prefers-reduced-motion` | Full disable of all animations | Partial — only in dialog.css | 🟠 Incomplete | +| Feature | Aurora Spec | Current | Status | +| ------------------------ | ----------------------------------- | ----------------------------------------------- | ------------------ | +| Duration tokens | 6 levels (50-700ms) | 5 levels (75-450ms) | 🟡 Close | +| Spring easing | `cubic-bezier(0.22, 1, 0.36, 1)` | `--ease-spring: cubic-bezier(0.22, 1, 0.36, 1)` | ✅ Exact match | +| Smooth easing | `cubic-bezier(0.34, 1.56, 0.64, 1)` | `--ease-smooth: cubic-bezier(0.16, 1, 0.3, 1)` | 🟡 Similar | +| fadeIn keyframe | opacity 0→1 | `fadeIn` exists | ✅ Matches | +| scaleIn keyframe | scale(0.95) + opacity | `fadeInScale` exists | ✅ Matches | +| slideUp keyframe | translateY(8px) + opacity | `fadeUp` with translateY(5px) | 🟡 Close | +| Glow pulse | box-shadow aurora-cyan-glow | `subtleGlow` exists | 🟡 Different color | +| Shimmer | background-position sweep | `shimmer` exists | ✅ Matches | +| Aurora drift | slow gradient background shift | ❌ Not implemented | 🔴 Missing | +| `prefers-reduced-motion` | Full disable of all animations | Partial — only in dialog.css | 🟠 Incomplete | **Assessment:** ~35% implemented. Core timing and easing infrastructure is solid. Aurora-specific glow animations and the drift effect are missing. `prefers-reduced-motion` coverage needs expansion. **Action Required:** + 1. Add Aurora-specific keyframes (aurora-pulse with cyan glow, aurora-drift for backgrounds) 2. Expand `prefers-reduced-motion` to global scope per spec §11.1 3. The existing spring easing is already perfect — no changes needed @@ -139,6 +142,7 @@ The Aurora spec provides a single unified design language but **doesn't distingu **Spec:** `backdrop-filter: blur(12-20px)`, `rgba(255,255,255,0.02-0.08)` glass layers, luminous gradient borders **Current State:** + - `backdrop-blur` used only on dialog overlays (`dialog.css`) - No systematic glass card styling - No luminous/gradient borders @@ -147,6 +151,7 @@ The Aurora spec provides a single unified design language but **doesn't distingu **Assessment:** ~15% implemented. The CSS `backdrop-filter` property is used but not as a design system primitive. **Action Required:** + 1. Create `.glass-card` utility class with backdrop-blur + rgba background 2. Add `--glass-subtle/light/medium/strong` CSS variables 3. These should be Aurora-theme-specific additions, not global changes @@ -156,31 +161,37 @@ The Aurora spec provides a single unified design language but **doesn't distingu ### 2.6 Component Specifications #### Buttons 🔴 1/10 + **Spec:** 4 variants (Primary/Glowing, Secondary/Glass, Ghost, Danger) with glow effects **Current:** Button component exists with variants but no glow box-shadow effects **Gap:** Glow halos on hover/active states are the key Aurora differentiator — not implemented #### Cards 🔴 1/10 + **Spec:** Glass cards with backdrop-blur, luminous borders, hover lift + glow **Current:** Card component exists with basic styling, no glass treatment **Gap:** Entire glassmorphism card system missing #### Input Fields 🔴 1.5/10 + **Spec:** Glass background, cyan glow border on focus, error glow states **Current:** Input fields have focus rings but no glow shadow effects **Gap:** Focus glow (box-shadow with aurora-cyan-glow) not implemented #### Prompt Input (Hero Component) 🔴 0.5/10 + **Spec:** Double-line border with gradient, inner glow, expanding animation, attachment chips **Current:** Functional prompt input exists, no Aurora visual treatment **Gap:** This is the most impactful component to Aurora-ify #### Message Bubbles 🔴 1/10 + **Spec:** User (cyan tint + cyan left border) vs Assistant (glass + violet left border) **Current:** Messages have basic styling, no color-coded left borders **Gap:** Color-coded message differentiation not implemented #### Dialogs/Modals 🟡 3/10 + **Spec:** Glass modal, blurred backdrop, spring entry/exit, 0.95→1.0 scale **Current:** Dialogs have backdrop blur, fadeInScale animation, spring-like easing **Gap:** Glass effect on modal body missing, timing close but not exact @@ -189,37 +200,42 @@ The Aurora spec provides a single unified design language but **doesn't distingu ### 2.7 Accessibility (Spec §11) 🟠 25% IMPLEMENTED -| Requirement | Status | Notes | -|------------|--------|-------| -| `prefers-reduced-motion` | 🟡 Partial | Only in `dialog.css`, needs global scope | -| `--max-prose-width: 70ch` | 🔴 Missing | No line-length constraints on chat messages | -| Light mode glass contrast fix | 🔴 N/A | No glass effects to fix yet | -| `cursor: pointer` on interactives | 🟡 Partial | `utilities.css` sets cursor on buttons/links | -| Lucide Icons mandate | ✅ Present | `lucide-solid` is used in the app | -| WCAG 4.5:1 contrast ratios | 🟡 Unverified | Likely OK for existing themes, needs audit for Aurora | -| Touch targets 44x44px | 🟡 Unverified | Needs measurement | +| Requirement | Status | Notes | +| --------------------------------- | ------------- | ----------------------------------------------------- | +| `prefers-reduced-motion` | 🟡 Partial | Only in `dialog.css`, needs global scope | +| `--max-prose-width: 70ch` | 🔴 Missing | No line-length constraints on chat messages | +| Light mode glass contrast fix | 🔴 N/A | No glass effects to fix yet | +| `cursor: pointer` on interactives | 🟡 Partial | `utilities.css` sets cursor on buttons/links | +| Lucide Icons mandate | ✅ Present | `lucide-solid` is used in the app | +| WCAG 4.5:1 contrast ratios | 🟡 Unverified | Likely OK for existing themes, needs audit for Aurora | +| Touch targets 44x44px | 🟡 Unverified | Needs measurement | --- ## Part 3: Validation Against Design Principles ### 3.1 "Light as Material" — UI elements emit light rather than receive it + **Score: 🔴 0/10** No glow effects, no luminous borders, no light-emission metaphor anywhere in current CSS. This is the CORE Aurora identity and is completely missing. ### 3.2 "Depth through Transparency" — Layers visible through glassmorphism + **Score: 🔴 1/10** Only dialog backdrop uses blur. No card or surface transparency. ### 3.3 "Confident Motion" — Every animation serves purpose, feels physical + **Score: 🟡 4/10** Spring easing exists. Animations are purposeful. But Aurora-specific motion (glow as feedback, breathing pulse) is absent. ### 3.4 "Chromatic Restraint" — Rich palette used sparingly + **Score: 🟡 5/10** Existing themes use color sparingly. This principle is about application, not implementation — would be evaluated after Aurora colors exist. ### 3.5 "Unified Language" — Same DNA across Web and TUI + **Score: 🟡 3/10** Two separate theme systems exist. No Aurora in either. The existing "opencode" theme in TUI and "oc-1" in Desktop UI are not visually unified. @@ -228,6 +244,7 @@ Two separate theme systems exist. No Aurora in either. The existing "opencode" t ## Part 4: Implementation Roadmap ### Phase 0: Theme Files (Immediate — 1 day) + - [ ] Create `packages/ui/src/theme/themes/aurora.json` — Desktop UI Aurora theme - Map Aurora colors to 9 OKLCH seeds - Use overrides for exact void background colors (#050508, #0A0A0F, #0F0F14, #14141A, #1A1A22) @@ -239,6 +256,7 @@ Two separate theme systems exist. No Aurora in either. The existing "opencode" t - [ ] Verify theme renders in both Desktop and TUI ### Phase 1: CSS Enhancement Layer (Short-term — 2-3 days) + - [ ] Add `--glass-*` CSS custom properties (subtle, light, medium, strong) - [ ] Create `.glass-card` utility class (backdrop-blur + rgba + border) - [ ] Add Aurora glow keyframes (aurora-pulse, aurora-drift) @@ -246,6 +264,7 @@ Two separate theme systems exist. No Aurora in either. The existing "opencode" t - [ ] Add `--max-prose-width: 70ch` for chat message containers ### Phase 2: Component Enhancements (Medium-term — 1 week) + - [ ] Button glow variants (box-shadow on hover/active) - [ ] Card glass variant - [ ] Input focus glow (cyan box-shadow) @@ -254,6 +273,7 @@ Two separate theme systems exist. No Aurora in either. The existing "opencode" t - [ ] Prompt input hero treatment ### Phase 3: Polish & Accessibility (Ongoing) + - [ ] WCAG contrast audit for Aurora dark + light - [ ] Touch target audit (44x44px minimum) - [ ] Full `prefers-reduced-motion` test pass @@ -264,34 +284,37 @@ Two separate theme systems exist. No Aurora in either. The existing "opencode" t ## Part 5: Risk Assessment -| Risk | Severity | Mitigation | -|------|----------|------------| -| OKLCH seed generation produces wrong shades | 🟡 Medium | Use extensive `overrides` to force exact hex values | -| Glassmorphism performance on low-end devices | 🟠 High | Use `will-change` sparingly, offer "reduce transparency" setting | -| Aurora glow effects look "gaming" if overdone | 🟡 Medium | Keep glow subtle (15% opacity max), review in context | -| Two theme systems diverge visually | 🟡 Medium | Review both side-by-side during implementation | -| `backdrop-filter` not supported in all browsers | 🟢 Low | All modern browsers support it; graceful fallback to solid bg | +| Risk | Severity | Mitigation | +| ----------------------------------------------- | --------- | ---------------------------------------------------------------- | +| OKLCH seed generation produces wrong shades | 🟡 Medium | Use extensive `overrides` to force exact hex values | +| Glassmorphism performance on low-end devices | 🟠 High | Use `will-change` sparingly, offer "reduce transparency" setting | +| Aurora glow effects look "gaming" if overdone | 🟡 Medium | Keep glow subtle (15% opacity max), review in context | +| Two theme systems diverge visually | 🟡 Medium | Review both side-by-side during implementation | +| `backdrop-filter` not supported in all browsers | 🟢 Low | All modern browsers support it; graceful fallback to solid bg | --- ## Part 6: Conclusions ### What's Good About the Spec + 1. **Comprehensive** — Covers colors, typography, spacing, motion, components, TUI translations, accessibility 2. **Practical** — Includes Stitch prompts for prototyping, implementation phases, file touchpoints 3. **Accessibility-aware** — §11 amendments show thoughtful review ### What Needs Updating in the Spec + 1. **Token naming must match codebase** — Spec uses `--void-*`, `--aurora-*`; codebase uses `--background-*`, `--surface-*` 2. **Theme format must match codebase** — Spec provides raw CSS; needs translation to seed-based JSON 3. **Two theme systems acknowledged** — Spec should distinguish Desktop UI vs TUI implementations 4. **Typography scale is impractical** — Major Third produces sizes (48.8px, 61px) rarely needed in a code editor ### Bottom Line + **The Aurora vision is strong. The implementation is zero.** The codebase architecture is ready — it just needs the actual theme files created and registered. The glassmorphism/glow effects layer is the biggest new work beyond theme creation. --- -*Review completed: 2025-02-27* -*Methodology: Frontend-PE systematic design review + Backend-PE architecture verification* -*Files examined: 60+ across packages/ui, packages/app, packages/opencode* +_Review completed: 2025-02-27_ +_Methodology: Frontend-PE systematic design review + Backend-PE architecture verification_ +_Files examined: 60+ across packages/ui, packages/app, packages/opencode_ diff --git a/docs/plans/2025-02-27-aurora-frontendpe-evaluation.md b/docs/plans/2025-02-27-aurora-frontendpe-evaluation.md index 30d8dde8de06..db52b84c2364 100644 --- a/docs/plans/2025-02-27-aurora-frontendpe-evaluation.md +++ b/docs/plans/2025-02-27-aurora-frontendpe-evaluation.md @@ -2,10 +2,11 @@ > **Evaluator:** FrontendPE (Distinguished Principal Frontend Engineer) > **Date:** 2025-02-27 -> **Documents Reviewed:** -> - `docs/plans/2025-02-26-aurora-design-system.md` (The Spec) +> **Documents Reviewed:** +> +> - `docs/plans/2025-02-26-aurora-design-system.md` (The Spec) > - `docs/plans/2025-02-27-aurora-design-review.md` (Architecture Review) -> **Codebase:** Branch `prax-dev`, SolidJS + Tauri Desktop App +> **Codebase:** Branch `prax-dev`, SolidJS + Tauri Desktop App --- @@ -24,25 +25,27 @@ The Aurora Design System represents a genuinely distinctive aesthetic direction **This is the highest-ROI item.** The codebase already has two mature theme systems that are plug-and-play: **Desktop UI Theme** (`packages/ui/src/theme/themes/aurora.json`): + - 9 OKLCH seed colors map cleanly to Aurora's palette - Extensive `overrides` can force exact hex values where algorithmic generation drifts - Registration in `default-themes.ts` is trivial **Seed Mapping (Aurora → DesktopTheme):** -| Aurora Concept | Seed Slot | Dark Value | Light Value | -|---|---|---|---| -| Void backgrounds | `neutral` | `#0A0A0F` | `#FAFAFA` | -| Electric Cyan | `primary` | `#00D4FF` | `#0891B2` | -| Success Green | `success` | `#4ADE80` | `#16A34A` | -| Amber Warning | `warning` | `#FFBB33` | `#D97706` | -| Error Red | `error` | `#F87171` | `#DC2626` | -| Soft Violet | `info` | `#A78BFA` | `#7C3AED` | -| Cyan (interactive) | `interactive` | `#00D4FF` | `#0891B2` | -| Diff Green | `diffAdd` | `#4ADE80` | `#16A34A` | -| Diff Red | `diffRemove` | `#F87171` | `#DC2626` | +| Aurora Concept | Seed Slot | Dark Value | Light Value | +| ------------------ | ------------- | ---------- | ----------- | +| Void backgrounds | `neutral` | `#0A0A0F` | `#FAFAFA` | +| Electric Cyan | `primary` | `#00D4FF` | `#0891B2` | +| Success Green | `success` | `#4ADE80` | `#16A34A` | +| Amber Warning | `warning` | `#FFBB33` | `#D97706` | +| Error Red | `error` | `#F87171` | `#DC2626` | +| Soft Violet | `info` | `#A78BFA` | `#7C3AED` | +| Cyan (interactive) | `interactive` | `#00D4FF` | `#0891B2` | +| Diff Green | `diffAdd` | `#4ADE80` | `#16A34A` | +| Diff Red | `diffRemove` | `#F87171` | `#DC2626` | **Override Requirements (exact void scale):** + ```json "overrides": { "background-base": "#0A0A0F", @@ -54,6 +57,7 @@ The Aurora Design System represents a genuinely distinctive aesthetic direction ``` **TUI Theme** (`packages/opencode/src/cli/cmd/tui/context/theme/aurora.json`): + - The spec's Appendix JSON is almost directly usable - Just needs `$schema` field and dark/light variant wrapping @@ -77,6 +81,7 @@ The core Aurora identity — "elements emit light" — is achievable with pure C ``` These are **pure CSS, no JS, no perf cost, no animation frames**. They work on any element: + - Buttons: `box-shadow: var(--glow-cyan)` on hover - Inputs: `box-shadow: var(--glow-cyan-hover)` on focus - Cards: `box-shadow: var(--glow-violet)` on hover @@ -91,12 +96,12 @@ User messages get a `2px` cyan left border, assistant messages get violet. This ```css [data-theme="aurora"] [data-role="user"] { - border-left: 2px solid var(--aurora-cyan, #00D4FF); + border-left: 2px solid var(--aurora-cyan, #00d4ff); background: rgba(0, 212, 255, 0.05); } [data-theme="aurora"] [data-role="assistant"] { - border-left: 2px solid var(--aurora-violet, #A78BFA); + border-left: 2px solid var(--aurora-violet, #a78bfa); } ``` @@ -108,14 +113,14 @@ User messages get a `2px` cyan left border, assistant messages get violet. This The spec's keyframes are standard CSS and the codebase already has an `animation.css` with similar patterns: -| Aurora Keyframe | Existing Equivalent | Action | -|---|---|---| -| `aurora-fade-in` | `fadeIn` ✅ | Already exists | -| `aurora-scale-in` | `fadeInScale` ✅ | Already exists | -| `aurora-slide-up` | `fadeUp` ✅ | Already exists (5px vs 8px — negligible) | -| `aurora-pulse` | `subtleGlow` 🟡 | Needs cyan color variant | -| `aurora-shimmer` | `shimmer` ✅ | Already exists | -| `aurora-drift` | ❌ Missing | Add — but **disabled by default** | +| Aurora Keyframe | Existing Equivalent | Action | +| ----------------- | ------------------- | ---------------------------------------- | +| `aurora-fade-in` | `fadeIn` ✅ | Already exists | +| `aurora-scale-in` | `fadeInScale` ✅ | Already exists | +| `aurora-slide-up` | `fadeUp` ✅ | Already exists (5px vs 8px — negligible) | +| `aurora-pulse` | `subtleGlow` 🟡 | Needs cyan color variant | +| `aurora-shimmer` | `shimmer` ✅ | Already exists | +| `aurora-drift` | ❌ Missing | Add — but **disabled by default** | **Action:** Add `aurora-pulse` (cyan glow breathing) and `aurora-drift` (slow background gradient shift). Both should respect `prefers-reduced-motion`. @@ -129,7 +134,9 @@ The spec's §11.1 is correct and critical. Currently only `dialog.css` respects ```css @media (prefers-reduced-motion: reduce) { - *, *::before, *::after { + *, + *::before, + *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; @@ -150,7 +157,8 @@ The review correctly identified missing line-length constraints. Chat messages c --max-prose-width: 70ch; } -.message-content, .chat-message-body { +.message-content, +.chat-message-body { max-width: var(--max-prose-width); } ``` @@ -175,6 +183,7 @@ The review correctly identified missing line-length constraints. Chat messages c ``` **Caveats:** + 1. **Performance:** `backdrop-filter` triggers compositing layers. On a chat interface with 50+ messages, each with a glass card = 50+ compositing layers. This WILL cause jank on scroll. 2. **Mitigation:** Apply glass ONLY to: - The prompt input (1 element, always visible) @@ -193,7 +202,7 @@ The spec's light theme is feasible but the review correctly identified contrast ```css /* Light mode glass needs higher opacity */ ---glass-light: rgba(0, 0, 0, 0.06); /* not 0.04 */ +--glass-light: rgba(0, 0, 0, 0.06); /* not 0.04 */ --glass-medium: rgba(0, 0, 0, 0.09); /* not 0.06 */ ``` @@ -213,8 +222,9 @@ The spec's prompt input with double-line gradient border and inner glow is the s backdrop-filter: blur(16px); border: 1px solid rgba(255, 255, 255, 0.08); border-radius: var(--radius-lg); - transition: box-shadow 0.25s cubic-bezier(0.22, 1, 0.36, 1), - border-color 0.25s cubic-bezier(0.22, 1, 0.36, 1); + transition: + box-shadow 0.25s cubic-bezier(0.22, 1, 0.36, 1), + border-color 0.25s cubic-bezier(0.22, 1, 0.36, 1); } [data-theme="aurora"] .prompt-input-container:focus-within { @@ -226,6 +236,7 @@ The spec's prompt input with double-line gradient border and inner glow is the s ``` **Caveats:** + 1. The spec mentions a "gradient border" (cyan → purple → magenta). CSS gradient borders require the `border-image` hack or a pseudo-element overlay. Both work but add complexity. 2. The "micro-pulse on type" (`scale(1.002x)`) is imperceptible at that magnitude and adds a transform on every keystroke — skip it. 3. The "content slides up on send" animation is a nice touch but needs careful integration with SolidJS's reactivity system. @@ -251,6 +262,7 @@ The spec recommends JetBrains Mono for code. The app already ships 16 Nerd Font The spec describes a "subtle animated gradient aurora" on the background that slowly drifts. The review's §11.1 even flags this as a motion sickness risk. **Why Not:** + 1. **Motion sickness:** Even "very subtle" background animation is distracting for extended coding sessions (hours, not minutes) 2. **Performance:** A full-viewport CSS gradient animation running continuously consumes GPU cycles for zero functional benefit 3. **Battery drain:** On a Tauri desktop app, continuous animation = battery murder @@ -298,16 +310,17 @@ The spec uses gradient borders (cyan → purple → magenta) on cards. CSS gradi background: var(--void-base); } .gradient-border::before { - content: ''; + content: ""; position: absolute; inset: -1px; border-radius: inherit; - background: linear-gradient(135deg, #00D4FF, #A78BFA, #FF6B9D); + background: linear-gradient(135deg, #00d4ff, #a78bfa, #ff6b9d); z-index: -1; } ``` **Why Not:** + - Pseudo-element approach requires `overflow: visible` which breaks `backdrop-filter` clipping - `border-image` doesn't work with `border-radius` - For every card, you need an extra pseudo-element = double the DOM compositing @@ -333,38 +346,39 @@ The spec uses `--void-*`, `--glass-*`, `--aurora-*` naming. The codebase uses `- ## Part 4: Implementation Priority Matrix -| Priority | Item | Effort | Impact | Risk | -|----------|------|--------|--------|------| -| **P0** | Aurora theme JSON files (Desktop + TUI) | 1 day | 🔥🔥🔥🔥🔥 | Low | -| **P0** | `prefers-reduced-motion` global | 0.25 day | 🔥🔥🔥 (a11y) | None | -| **P1** | Glow box-shadows on focus/hover | 0.5 day | 🔥🔥🔥🔥🔥 | None | -| **P1** | Color-coded message left borders | 0.5 day | 🔥🔥🔥🔥 | None | -| **P1** | `--max-prose-width: 70ch` | 0.25 day | 🔥🔥🔥 | None | -| **P2** | Prompt input hero treatment (glass + glow) | 1 day | 🔥🔥🔥🔥🔥 | Low | -| **P2** | Aurora keyframes (pulse, drift-static) | 0.5 day | 🔥🔥🔥 | Low | -| **P3** | Glassmorphism on sidebar + dialogs | 1 day | 🔥🔥🔥 | Medium | -| **P3** | Light theme variant | 1 day | 🔥🔥 | Low | -| **P3** | JetBrains Mono as Aurora default | 0.25 day | 🔥🔥 | None | -| **SKIP** | Animated aurora background | — | — | High | -| **SKIP** | WebGL/shaders | — | — | Critical | -| **SKIP** | Parallax on messages | — | — | High | -| **SKIP** | Gradient borders on all elements | — | — | Medium | -| **SKIP** | Token rename | — | — | Critical | +| Priority | Item | Effort | Impact | Risk | +| -------- | ------------------------------------------ | -------- | ------------- | -------- | +| **P0** | Aurora theme JSON files (Desktop + TUI) | 1 day | 🔥🔥🔥🔥🔥 | Low | +| **P0** | `prefers-reduced-motion` global | 0.25 day | 🔥🔥🔥 (a11y) | None | +| **P1** | Glow box-shadows on focus/hover | 0.5 day | 🔥🔥🔥🔥🔥 | None | +| **P1** | Color-coded message left borders | 0.5 day | 🔥🔥🔥🔥 | None | +| **P1** | `--max-prose-width: 70ch` | 0.25 day | 🔥🔥🔥 | None | +| **P2** | Prompt input hero treatment (glass + glow) | 1 day | 🔥🔥🔥🔥🔥 | Low | +| **P2** | Aurora keyframes (pulse, drift-static) | 0.5 day | 🔥🔥🔥 | Low | +| **P3** | Glassmorphism on sidebar + dialogs | 1 day | 🔥🔥🔥 | Medium | +| **P3** | Light theme variant | 1 day | 🔥🔥 | Low | +| **P3** | JetBrains Mono as Aurora default | 0.25 day | 🔥🔥 | None | +| **SKIP** | Animated aurora background | — | — | High | +| **SKIP** | WebGL/shaders | — | — | Critical | +| **SKIP** | Parallax on messages | — | — | High | +| **SKIP** | Gradient borders on all elements | — | — | Medium | +| **SKIP** | Token rename | — | — | Critical | --- ## Part 5: Total Effort Estimate -| Phase | Items | Days | Cumulative | -|-------|-------|------|------------| -| **Phase 0** | Theme files + a11y global | 1.25 days | 1.25 days | -| **Phase 1** | Glow effects + message borders + prose width | 1.25 days | 2.5 days | -| **Phase 2** | Prompt input hero + keyframes | 1.5 days | 4 days | -| **Phase 3** | Glass on fixed elements + light theme + font | 2.25 days | 6.25 days | +| Phase | Items | Days | Cumulative | +| ----------- | -------------------------------------------- | --------- | ---------- | +| **Phase 0** | Theme files + a11y global | 1.25 days | 1.25 days | +| **Phase 1** | Glow effects + message borders + prose width | 1.25 days | 2.5 days | +| **Phase 2** | Prompt input hero + keyframes | 1.5 days | 4 days | +| **Phase 3** | Glass on fixed elements + light theme + font | 2.25 days | 6.25 days | **Total: ~6 working days for a complete, production-grade Aurora implementation.** This does NOT include: + - QA/cross-browser testing (add 1-2 days) - Upstream merge conflict resolution (varies) - TUI-specific component visual adjustments (add 1 day) @@ -373,7 +387,7 @@ This does NOT include: ## Part 6: The Wow Factor -The single most impactful Aurora technique is **glow as interactive feedback**. When every other code editor uses flat shadows or material elevation, Aurora makes elements *radiate*: +The single most impactful Aurora technique is **glow as interactive feedback**. When every other code editor uses flat shadows or material elevation, Aurora makes elements _radiate_: ``` Other tools: hover → darken background → feels heavy @@ -381,6 +395,7 @@ Aurora: hover → emit light → feels alive, weightless, digital ``` This is achieved with ONE CSS property (`box-shadow` with colored, spread, blurred shadows) that: + - Costs zero JS - Costs near-zero GPU (single compositing operation) - Works in both Tauri WebView and browsers @@ -393,18 +408,18 @@ This is achieved with ONE CSS property (`box-shadow` with colored, spread, blurr ## Appendix: Spec Corrections -| Spec Statement | Correction | -|---|---| -| "Create aurora-dark.json and aurora-light.json" | Single `aurora.json` with `light` and `dark` variant objects | -| "Use `--void-*` CSS variables" | Map to existing `--background-*` tokens via overrides | -| "Major Third type scale" | Keep existing 4-size scale (13/14/16/20) | -| "Animated aurora drift on background" | Static gradient on welcome screen only | -| "Glass cards on everything" | Glass on prompt input, sidebar, dialogs only | -| "Spring physics via JS library" | CSS `cubic-bezier(0.22, 1, 0.36, 1)` already matches — no library needed | -| "Gradient borders on cards" | Solid color borders + glow shadows instead | +| Spec Statement | Correction | +| ----------------------------------------------- | ------------------------------------------------------------------------ | +| "Create aurora-dark.json and aurora-light.json" | Single `aurora.json` with `light` and `dark` variant objects | +| "Use `--void-*` CSS variables" | Map to existing `--background-*` tokens via overrides | +| "Major Third type scale" | Keep existing 4-size scale (13/14/16/20) | +| "Animated aurora drift on background" | Static gradient on welcome screen only | +| "Glass cards on everything" | Glass on prompt input, sidebar, dialogs only | +| "Spring physics via JS library" | CSS `cubic-bezier(0.22, 1, 0.36, 1)` already matches — no library needed | +| "Gradient borders on cards" | Solid color borders + glow shadows instead | --- -*Evaluation completed: 2025-02-27* -*Methodology: FrontendPE Principal-level design review with codebase architecture validation* -*Conclusion: Aurora is a strong, implementable design system. Scope to ~6 days of work by cutting the impractical 15% and focusing on the high-impact 85%.* +_Evaluation completed: 2025-02-27_ +_Methodology: FrontendPE Principal-level design review with codebase architecture validation_ +_Conclusion: Aurora is a strong, implementable design system. Scope to ~6 days of work by cutting the impractical 15% and focusing on the high-impact 85%._ diff --git a/docs/plans/2025-02-28-aurora-complete-ui-overhaul.md b/docs/plans/2025-02-28-aurora-complete-ui-overhaul.md index ae5424c624aa..2368ed7e5561 100644 --- a/docs/plans/2025-02-28-aurora-complete-ui-overhaul.md +++ b/docs/plans/2025-02-28-aurora-complete-ui-overhaul.md @@ -6,6 +6,7 @@ **Date:** 2025-02-28 **Status:** Implementation Complete **Files Changed:** + - `packages/ui/src/styles/aurora.css` — 43-section component overhaul - `packages/app/src/index.css` — 22-section app structural overhaul - `packages/ui/src/theme/context.tsx` — Default theme → aurora @@ -94,6 +95,7 @@ ## 1. Design Philosophy ### OLD Design (oc-1 default) + - **Flat, utilitarian** — standard dark theme with sharp borders - **Shadows for depth** — traditional CSS box-shadows (sm, md, lg) - **No glassmorphism** — solid opaque backgrounds @@ -102,6 +104,7 @@ - **No ambient atmosphere** — plain backgrounds ### NEW Design (Aurora) + - **Digital luminescence** — elements emit light from within - **Glass morphism everywhere** — `backdrop-filter: blur()` on surfaces, transparency layers - **Glow system** — cyan/violet/rose/green/amber contextual glow on hover/focus/active @@ -116,49 +119,54 @@ ## 2. Foundation Tokens ### Easing Curves -| Token | Value | Usage | -|-------|-------|-------| -| `--ease-aurora` | `cubic-bezier(0.22, 1, 0.36, 1)` | General transitions | + +| Token | Value | Usage | +| --------------- | ----------------------------------- | ------------------- | +| `--ease-aurora` | `cubic-bezier(0.22, 1, 0.36, 1)` | General transitions | | `--ease-spring` | `cubic-bezier(0.34, 1.56, 0.64, 1)` | Bouncy interactions | -| `--ease-snappy` | `cubic-bezier(0.16, 1, 0.3, 1)` | Dialog open/close | +| `--ease-snappy` | `cubic-bezier(0.16, 1, 0.3, 1)` | Dialog open/close | ### Border Radius Scale -| Token | Value | Usage | -|-------|-------|-------| -| `--aurora-radius-sm` | `6px` | Inline code, keybinds, small badges | -| `--aurora-radius-md` | `10px` | Buttons, inputs, tabs, tooltips | -| `--aurora-radius-lg` | `14px` | Cards, code blocks, menus, toasts | -| `--aurora-radius-xl` | `18px` | Dialogs, prompt dock | -| `--aurora-radius-2xl` | `24px` | Hero elements | -| `100px` | pill | Tags, progress bars, scrollbar thumbs | + +| Token | Value | Usage | +| --------------------- | ------ | ------------------------------------- | +| `--aurora-radius-sm` | `6px` | Inline code, keybinds, small badges | +| `--aurora-radius-md` | `10px` | Buttons, inputs, tabs, tooltips | +| `--aurora-radius-lg` | `14px` | Cards, code blocks, menus, toasts | +| `--aurora-radius-xl` | `18px` | Dialogs, prompt dock | +| `--aurora-radius-2xl` | `24px` | Hero elements | +| `100px` | pill | Tags, progress bars, scrollbar thumbs | ### Glow System (Dark Mode) -| Token | Effect | -|-------|--------| -| `--glow-cyan` | `0 0 20px -5px rgba(0, 212, 255, 0.35)` | -| `--glow-cyan-hover` | `0 0 30px -5px rgba(0, 212, 255, 0.5), ring 1px` | + +| Token | Effect | +| ------------------- | ------------------------------------------------- | +| `--glow-cyan` | `0 0 20px -5px rgba(0, 212, 255, 0.35)` | +| `--glow-cyan-hover` | `0 0 30px -5px rgba(0, 212, 255, 0.5), ring 1px` | | `--glow-cyan-focus` | `0 0 35px -5px rgba(0, 212, 255, 0.55), ring 3px` | -| `--glow-violet` | `0 0 20px -5px rgba(167, 139, 250, 0.35)` | -| `--glow-rose` | `0 0 20px -5px rgba(255, 107, 157, 0.35)` | -| `--glow-green` | `0 0 20px -5px rgba(74, 222, 128, 0.35)` | -| `--glow-red` | `0 0 16px -5px rgba(248, 113, 113, 0.45)` | -| `--glow-amber` | `0 0 16px -5px rgba(255, 187, 51, 0.4)` | +| `--glow-violet` | `0 0 20px -5px rgba(167, 139, 250, 0.35)` | +| `--glow-rose` | `0 0 20px -5px rgba(255, 107, 157, 0.35)` | +| `--glow-green` | `0 0 20px -5px rgba(74, 222, 128, 0.35)` | +| `--glow-red` | `0 0 16px -5px rgba(248, 113, 113, 0.45)` | +| `--glow-amber` | `0 0 16px -5px rgba(255, 187, 51, 0.4)` | ### Glass Surfaces (Dark Mode) -| Token | Value | Usage | -|-------|-------|-------| -| `--aurora-glass` | `rgba(14, 14, 20, 0.85)` | Prompt dock, titlebar | -| `--aurora-glass-strong` | `rgba(7, 7, 16, 0.92)` | Dialogs, sidebar, menus | -| `--aurora-glass-subtle` | `rgba(20, 20, 30, 0.7)` | Hover states | + +| Token | Value | Usage | +| ----------------------- | ------------------------ | ----------------------- | +| `--aurora-glass` | `rgba(14, 14, 20, 0.85)` | Prompt dock, titlebar | +| `--aurora-glass-strong` | `rgba(7, 7, 16, 0.92)` | Dialogs, sidebar, menus | +| `--aurora-glass-subtle` | `rgba(20, 20, 30, 0.7)` | Hover states | ### Accent Colors -| Dark Mode | Light Mode | Role | -|-----------|------------|------| -| `#00D4FF` (cyan) | `#0891B2` (teal) | Primary accent, user identity | + +| Dark Mode | Light Mode | Role | +| ------------------ | ------------------ | ----------------------------- | +| `#00D4FF` (cyan) | `#0891B2` (teal) | Primary accent, user identity | | `#A78BFA` (violet) | `#7C3AED` (purple) | Secondary, assistant identity | -| `#FFBB33` (amber) | `#D97706` (amber) | Warning, permissions | -| `#FF6B9D` (rose) | `#DB2777` (pink) | Highlight, error accent | -| `#4ADE80` (green) | `#16A34A` (green) | Success | +| `#FFBB33` (amber) | `#D97706` (amber) | Warning, permissions | +| `#FF6B9D` (rose) | `#DB2777` (pink) | Highlight, error accent | +| `#4ADE80` (green) | `#16A34A` (green) | Success | --- @@ -167,6 +175,7 @@ ### 3.1 Button **OLD:** + - `border-radius: var(--radius-md)` (~8px) - Solid opaque backgrounds per variant (primary/ghost/secondary) - Standard `box-shadow: var(--shadow-sm)` on primary hover @@ -175,6 +184,7 @@ - Active: `transform: scale(0.97)` **NEW (Aurora):** + - `border-radius: 10px` (--aurora-radius-md) - **Primary:** Cyan glow halo on rest (`--glow-cyan`), intensifies on hover (`--glow-cyan-hover` with ring), focus gets 3px ring (`--glow-cyan-focus`). `translateY(-1px)` lift + `brightness(1.08)` on hover. Active: `scale(0.97)` with glow reset. - **Secondary:** 1px border `rgba(0, 212, 255, 0.08)`, `backdrop-filter: blur(8px)` glass effect. Hover: border brightens to `rgba(0, 212, 255, 0.2)`, cyan glow appears, `translateY(-1px)`. @@ -187,11 +197,13 @@ ### 3.2 Icon Button **OLD:** + - Circular or rounded square with `var(--radius-md)` - Ghost variant: transparent, hover → `var(--surface-base-hover)` - No glow effects **NEW (Aurora):** + - `border-radius: 10px` - Primary variant: same glow system as button primary - Ghost hover: fills with `rgba(0, 212, 255, 0.04)` tinted background @@ -200,12 +212,14 @@ ### 3.3 Card **OLD:** + - `border-radius: var(--radius-lg)` (~12px) - Solid background `var(--surface-base)` or similar - Shadow: `var(--shadow-xs-border-base)` - No glass, no glow on hover **NEW (Aurora):** + - `border-radius: 14px` (--aurora-radius-lg) - `background: rgba(255, 255, 255, 0.02)` — barely visible tint - `backdrop-filter: blur(12px)` — glass morphism @@ -216,11 +230,13 @@ ### 3.4 Tag **OLD:** + - `border-radius: var(--radius-sm)` (~4px) — square-ish chip - Color variants: filled bg per semantic color - Standard padding **NEW (Aurora):** + - `border-radius: 100px` — **full pill shape** - `border: 1px solid rgba(0, 212, 255, 0.08)` — aurora-tinted border - `transition: all 0.2s var(--ease-aurora)` — smooth interaction @@ -228,12 +244,14 @@ ### 3.5 Checkbox **OLD:** + - 16px square with `var(--radius-xs)` (~2px) - Checked: solid primary color bg - Shadow: subtle border shadow - No glow **NEW (Aurora):** + - Same size/radius (checkboxes stay small) - **Checked state:** adds cyan glow halo `--glow-cyan` — the checkbox literally glows when checked - Disabled state preserved @@ -241,11 +259,13 @@ ### 3.6 Switch **OLD:** + - 34×18px toggle pill - Checked: solid primary bg with white thumb - Standard transition **NEW (Aurora):** + - Same dimensions (switches are standard) - **Checked state:** cyan glow halo `--glow-cyan` — glowing toggle - Glow provides visual emphasis beyond just color change @@ -253,11 +273,13 @@ ### 3.7 Radio Group **OLD:** + - 16px circle with border - Checked: inner dot appears, primary border - No glow **NEW (Aurora):** + - Checked: border color → `--aurora-accent` (cyan) - Adds `--glow-cyan` halo — glowing selected radio - Consistent with checkbox/switch glow language @@ -265,12 +287,14 @@ ### 3.8 Text Field / Input **OLD:** + - `border-radius: var(--radius-md)` (~8px) - Border: `var(--border-weak-base)` - Focus: border changes to `var(--border-focus)`, standard focus ring - Shadow: `var(--shadow-xs-border-base)` → `var(--shadow-xs-border-focus)` **NEW (Aurora):** + - `border-radius: 10px` - Focus: border → `rgba(0, 212, 255, 0.35)`, shadow → `--glow-cyan-focus` (35px spread cyan glow + 3px ring) - Input literally **glows cyan** when focused @@ -280,31 +304,37 @@ ### 3.9 Inline Input **OLD:** + - Small inline input with `var(--radius-sm)` (~4px) - Minimal styling **NEW (Aurora):** + - `border-radius: 6px` — slightly larger - Inherits focus glow from global input rule ### 3.10 Select **OLD:** + - `var(--radius-md)` (~8px) - Standard dropdown appearance **NEW (Aurora):** + - `border-radius: 10px` - Dropdown content gets glass treatment (via popover rules) ### 3.11 Progress Bar **OLD:** + - `border-radius: var(--radius-sm)` (~4px) — barely rounded - Solid primary fill - Track: muted background **NEW (Aurora):** + - `border-radius: 100px` — **full pill shape** (both track and fill) - Fill: `linear-gradient(90deg, cyan, violet)` — **aurora gradient** - Fill glow: `0 0 12px -2px rgba(0, 212, 255, 0.4)` — the bar glows @@ -313,31 +343,37 @@ ### 3.12 Progress Circle **OLD:** + - SVG circle with stroke animation - Primary color stroke **NEW (Aurora):** + - No major visual change (SVG strokes don't benefit from glass/glow) - Color inherits from aurora accent tokens ### 3.13 Spinner **OLD:** + - Animated loading indicator - Color: `var(--icon-base)` or inherits **NEW (Aurora):** + - `color: var(--aurora-accent)` — **always cyan** (branded spinner) ### 3.14 Keybind Badge **OLD:** + - `border-radius: 2px` — nearly square - `box-shadow: var(--shadow-xxs-border)` — micro shadow - 20px height, 12px text - Muted appearance **NEW (Aurora):** + - `border-radius: 6px` — softer corners - `border: 1px solid rgba(0, 212, 255, 0.08)` — aurora-tinted border - Same size/text @@ -345,23 +381,27 @@ ### 3.15 Avatar **OLD:** + - `border-radius: var(--radius-sm)` (~4px) — squircle - Info-toned bg with monospace uppercase text - Sizes: 20/24/32px **NEW (Aurora):** + - No major change — avatars remain compact identity markers - Inherits aurora color tokens naturally ### 3.16 Tooltip **OLD:** + - `border-radius: var(--radius-md)` (~8px) - Solid raised surface bg - `box-shadow: var(--shadow-md)` - Standard fade animation **NEW (Aurora):** + - `border-radius: 10px` - `backdrop-filter: blur(12px)` — **frosted glass tooltip** - `border: 1px solid rgba(0, 212, 255, 0.08)` — aurora border @@ -370,12 +410,14 @@ ### 3.17 Toast **OLD:** + - `border-radius: var(--radius-lg)` (~12px) - Solid surface bg - Shadow for elevation - Slide-in animation **NEW (Aurora):** + - `border-radius: 14px` - `backdrop-filter: blur(16px)` — **frosted glass notification** - `border: 1px solid rgba(0, 212, 255, 0.08)` — aurora border @@ -387,6 +429,7 @@ ### 4.1 Dialog / Modal **OLD:** + - `border-radius: var(--radius-xl)` (~16px) - Solid bg: `var(--surface-raised-stronger-non-alpha)` with `background-clip: padding-box` - Shadow: `var(--shadow-lg-border-base)` + 1px ring via box-shadow @@ -396,6 +439,7 @@ - Header padding: `16px 20px` **NEW (Aurora):** + - `border-radius: 18px` (--aurora-radius-xl) — larger, softer - `background: rgba(7, 7, 16, 0.92)` — deep frosted glass (not opaque) - `backdrop-filter: blur(24px)` — heavy blur (6x more than old overlay) @@ -411,6 +455,7 @@ ### 4.2 Popover **OLD:** + - `border-radius: var(--radius-lg)` (~12px) - Solid bg with `background-clip: padding-box` - `box-shadow: var(--shadow-lg)` @@ -418,6 +463,7 @@ - Scale animation: `0.95` → `1` in 150ms **NEW (Aurora):** + - `border-radius: 14px` - `background: rgba(7, 7, 16, 0.92)` — frosted glass - `backdrop-filter: blur(20px)` — **glass effect added** @@ -427,28 +473,34 @@ ### 4.3 Dropdown Menu **OLD:** + - Same as popover: solid bg, standard shadow - `border-radius: var(--radius-lg)` **NEW (Aurora):** + - Same glass treatment as popover — frosted glass with `blur(20px)`, aurora glow shadow, cyan-tinted border ### 4.4 Context Menu **OLD:** + - Same solid surface pattern **NEW (Aurora):** + - Same frosted glass treatment — all menus are now glass ### 4.5 Hover Card **OLD:** + - `border-radius: var(--radius-lg)` (~12px) - Solid raised bg - Standard shadow **NEW (Aurora):** + - `border-radius: 14px` - `backdrop-filter: blur(16px)` — frosted glass - `border: 1px solid rgba(0, 212, 255, 0.08)` @@ -461,6 +513,7 @@ ### 5.1 Tabs **OLD:** + - Multiple variants: normal, alt, pill, settings, review - Active indicator: border-bottom with interactive color - Radius: `var(--radius-sm)` to `var(--radius-md)` depending on variant @@ -468,6 +521,7 @@ - Plain active underline **NEW (Aurora):** + - Container: `border-radius: 10px` - Alt variant active indicator: `border-bottom-color: var(--aurora-accent)` (cyan), `2px` width — **branded accent underline** - Trigger: `border-radius: 6px`, `transition: all 0.2s var(--ease-aurora)` @@ -475,23 +529,27 @@ ### 5.2 List / List Item **OLD:** + - Selected: `var(--surface-base-active)` bg - Hover: `var(--surface-base-hover)` bg - No accent border - Standard transitions **NEW (Aurora):** + - Selected: `rgba(0, 212, 255, 0.06)` tinted bg + `2px left border cyan` — **accent selection indicator** - Hover: `rgba(0, 212, 255, 0.04)` — subtle aurora tint ### 5.3 Accordion **OLD:** + - `border-radius: var(--radius-md)` (~8px) - Standard trigger hover: bg change - No special styling **NEW (Aurora):** + - `border-radius: 14px` - Trigger hover: `rgba(0, 212, 255, 0.04)` — aurora tint - `transition: all 0.2s var(--ease-aurora)` @@ -499,11 +557,13 @@ ### 5.4 Collapsible **OLD:** + - Trigger: no visible background, standard cursor - No hover feedback - Chevron rotation animation **NEW (Aurora):** + - Trigger: `border-radius: 10px`, `padding: 6px 10px`, `margin: -6px -10px` — click target larger than visible area - Hover: `rgba(0, 212, 255, 0.04)` bg — visible feedback - `transition: all 0.2s var(--ease-aurora)` @@ -512,10 +572,12 @@ ### 5.5 Message Nav **OLD:** + - Small navigation arrows for scrolling between messages - Standard icon button styling **NEW (Aurora):** + - Inherits icon button aurora treatment (glow on hover) --- @@ -525,11 +587,13 @@ ### 6.1 Sidebar **OLD:** + - `background: var(--background-strong)` - `border-right: 1px solid var(--border-weaker-base)` - Solid, opaque **NEW (Aurora):** + - `background: rgba(7, 7, 16, 0.92)` — **frosted glass panel** - `backdrop-filter: blur(20px)` — glass blur - `border-right: 1px solid rgba(0, 212, 255, 0.08)` — aurora-tinted separator @@ -538,11 +602,13 @@ ### 6.2 Titlebar / Session Header **OLD:** + - `background: var(--background-strong)` or transparent - `border-bottom: 1px solid var(--border-weaker-base)` - No blur, no glass **NEW (Aurora):** + - `background: rgba(14, 14, 20, 0.85)` — semi-transparent glass - `backdrop-filter: blur(20px)` — frosted glass header - `border-bottom: 1px solid rgba(0, 212, 255, 0.08)` — aurora edge @@ -551,11 +617,13 @@ ### 6.3 Resize Handle **OLD:** + - Thin divider line, `var(--border-weak-base)` color - Cursor: col-resize / row-resize - No hover feedback color **NEW (Aurora):** + - Hover: `background: var(--aurora-accent)` at `opacity: 0.5` — **cyan accent line** - Active: same cyan at `opacity: 0.8` - Clear visual feedback during resize @@ -563,10 +631,12 @@ ### 6.4 Scrollbar **OLD:** + - `scrollbar-width: none` — **completely hidden** (no visual scrollbar!) - `::-webkit-scrollbar { display: none }` **NEW (Aurora):** + - `scrollbar-width: thin` — **visible but minimal** - Track: transparent - Thumb: `rgba(0, 212, 255, 0.15)` — cyan-tinted, nearly invisible at rest @@ -584,6 +654,7 @@ This is the most important component — the central input area. **OLD:** + - `border-radius: var(--radius-xl)` (~16px) - `background: var(--surface-raised-stronger-non-alpha)` — solid opaque - `box-shadow: var(--shadow-lg)` — standard elevation @@ -591,6 +662,7 @@ This is the most important component — the central input area. - Focus: standard border color change **NEW (Aurora):** + - `border-radius: 18px` (--aurora-radius-xl) - `background: rgba(14, 14, 20, 0.85)` — **glass morphism** - `backdrop-filter: blur(24px)` — heavy glass blur @@ -602,22 +674,26 @@ This is the most important component — the central input area. - `transition: border-color 0.3s, box-shadow 0.35s var(--ease-aurora)` — smooth glow ramp **Send Button (inside dock):** + - OLD: Standard primary button - NEW: `border-radius: 14px`, `box-shadow: 0 0 20px rgba(0, 212, 255, 0.35)` — **always-glowing orb** - Hover: glow intensifies to `0 0 40px rgba(0, 212, 255, 0.55)`, `translateY(-1px) scale(1.03)` float **Tray Surface:** + - `border-radius: 18px` with aurora border ### 7.2 User Message **OLD:** + - No identity label - No special bubble styling - Content rendered as plain markdown - No border, no distinct visual treatment **NEW (Aurora):** + - **Identity label:** `"You"` label above message, right-aligned, `11px` uppercase `700 weight`, `letter-spacing: 0.5px`, colored with agent-ask accent - **Message bubble:** - `border-radius: 14px 2px 14px 14px` — **chat-bubble shape** (flat top-right for "speech" direction) @@ -631,11 +707,13 @@ This is the most important component — the central input area. ### 7.3 Assistant Message **OLD:** + - No identity label - Plain markdown rendering - Same as user visually **NEW (Aurora):** + - **Identity label:** `"⬡ OPENCODE AI"` — hexagon symbol + branded name - `11px`, `700 weight`, `letter-spacing: 1.5px`, uppercase - Color: `var(--icon-agent-plan-base)` — aurora accent @@ -648,12 +726,14 @@ This is the most important component — the central input area. ### 7.4 Thinking / Reasoning Block **OLD:** + - Plain collapsible text - Thinking text: `color: var(--text-weak)` / `var(--text-weaker)` - No visual indicator beyond text - No special container **NEW (Aurora):** + - **Container:** - `padding: 10px 16px` - `border-radius: 12px` @@ -675,12 +755,14 @@ This is the most important component — the central input area. ### 7.5 Session Turn Container **OLD:** + - `gap: 24px` between messages - Each message: `padding: 16px 0`, separated by `1px solid var(--border-weaker-base)` bottom border - Background: `var(--background-stronger)` - Hidden scrollbar **NEW (Aurora):** + - `padding: 20px 0` — more vertical breathing room - Border dividers: `var(--aurora-border)` color (cyan-tinted) - Background preserved from theme tokens @@ -689,20 +771,24 @@ This is the most important component — the central input area. ### 7.6 Message Part **OLD:** + - Generic container with some padding - No special border or radius **NEW (Aurora):** + - `border-radius: 10px` - Clean containment for tool results, file previews, etc. ### 7.7 Empty State **OLD:** + - Plain centered text on flat background - No atmosphere **NEW (Aurora):** + - `background: radial-gradient(ellipse 60% 40% at 50% 60%, rgba(0, 212, 255, 0.08), rgba(167, 139, 250, 0.04) 50%, transparent)` — **ambient aurora gradient** - Subtle cyan-to-violet radial glow centered below middle of screen - Creates a sense of depth and atmosphere in the empty state @@ -714,6 +800,7 @@ This is the most important component — the central input area. ### 8.1 Code Blocks (Markdown `pre`) **OLD:** + - `border-radius: var(--radius-lg)` (~12px) - `background: var(--surface-inset-strong)` — solid dark bg - `box-shadow: var(--shadow-xs-border-base)` — subtle border shadow @@ -721,6 +808,7 @@ This is the most important component — the central input area. - Copy button: hidden until hover **NEW (Aurora):** + - `border-radius: 14px` - `background: rgba(5, 5, 12, 0.7)` in dark / `rgba(232, 232, 242, 0.55)` in light — **slightly transparent** (glass hint) - `border: 1px solid rgba(0, 212, 255, 0.1)` — subtle aurora border @@ -731,10 +819,12 @@ This is the most important component — the central input area. ### 8.2 Inline Code **OLD:** + - `border-radius: var(--radius-xs)` (~2px) - Standard bg/color **NEW (Aurora):** + - `border-radius: 6px` — softer - `padding: 2px 7px` - `font-size: 0.88em` @@ -743,21 +833,25 @@ This is the most important component — the central input area. ### 8.3 Diff Changes **OLD:** + - Minimal styling, standard inset bg - Standard radius **NEW (Aurora):** + - `border-radius: 6px` — slightly rounded - Inherits aurora color tokens for additions/deletions ### 8.4 File Write/Edit/Patch Tools **OLD:** + - Collapsible trigger with no border - No hover card treatment - Plain bg **NEW (Aurora):** + - Wrapped in `1px solid rgba(0, 212, 255, 0.08)` border - `border-radius: 14px` - `overflow: hidden` — contained card @@ -767,10 +861,12 @@ This is the most important component — the central input area. ### 8.5 Basic Tool **OLD:** + - Plain container, minimal borders - No hover state **NEW (Aurora):** + - `border-radius: 10px` - `border: 1px solid rgba(0, 212, 255, 0.08)` - Hover: border → `rgba(0, 212, 255, 0.2)` — brightens @@ -778,10 +874,12 @@ This is the most important component — the central input area. ### 8.6 Bash Output **OLD:** + - Standard inset bg with basic radius - `max-height` scroll area **NEW (Aurora):** + - `border-radius: 14px` - `border: 1px solid rgba(0, 212, 255, 0.1)` — code-style border - `background: rgba(5, 5, 12, 0.7)` — deep void bg (matches code blocks) @@ -790,11 +888,13 @@ This is the most important component — the central input area. ### 8.7 Copy Button **OLD:** + - Hidden by default (`opacity: 0`) - Appears on hover over code block - Standard icon button (square, secondary variant) **NEW (Aurora):** + - **Always visible** (`opacity: 1`, `pointer-events: auto`) - `border-radius: 6px` - `padding: 2px 8px` — compact text-like button @@ -806,11 +906,13 @@ This is the most important component — the central input area. ### 8.8 Permission Prompt **OLD:** + - Standard tool wrapper with warning border - Basic radius - No glow **NEW (Aurora):** + - `border-radius: 14px` - `box-shadow: 0 0 0 1px rgba(255, 187, 51, 0.35)` ring + `0 0 24px -6px rgba(255, 187, 51, 0.25)` — **amber glow warning** - The permission prompt literally glows amber — unmissable visual signal @@ -822,11 +924,13 @@ This is the most important component — the central input area. ### 9.1 Markdown Renderer **OLD:** + - Standard markdown styling from theme tokens - No max-width constraint - Headings, bold, italic inherit from tokens **NEW (Aurora):** + - `max-width: 72ch` — **readable line length constraint** (prevents ultra-wide text) - `overflow-wrap: anywhere`, `word-break: break-word` — prevents horizontal overflow - All heading/strong/em/code colors from aurora tokens @@ -834,10 +938,12 @@ This is the most important component — the central input area. ### 9.2 Blockquotes **OLD:** + - Left border with theme color - Standard padding **NEW (Aurora):** + - `border-left-width: 3px` — thicker accent - `border-radius: 0 6px 6px 0` — **rounded right edge** (only blockquotes get this) - `padding: 8px 16px` @@ -846,11 +952,13 @@ This is the most important component — the central input area. ### 9.3 Ordered Lists **OLD:** + - Browser default numbered list - Standard list-style-type: decimal - Padding-left for indent **NEW (Aurora):** + - **Custom counter system** — `counter-reset: list-counter` - `list-style: none` — browser numbers removed - `::before` pseudo: `counter(list-counter) "."` @@ -861,10 +969,12 @@ This is the most important component — the central input area. ### 9.4 Links **OLD:** + - Colored with theme link color - Standard underline/hover **NEW (Aurora):** + - Color: `var(--markdown-link)` from aurora tokens - Hover: `text-shadow: 0 0 12px currentColor` — **link glows on hover** - `transition: text-shadow 0.2s, color 0.2s var(--ease-aurora)` @@ -872,10 +982,12 @@ This is the most important component — the central input area. ### 9.5 Headings **OLD:** + - Standard weight/size from markdown defaults - No tracking adjustment **NEW (Aurora):** + - `letter-spacing: -0.02em` — **tighter tracking** (modern typographic style) - Color from `var(--markdown-heading)` aurora tokens @@ -886,10 +998,12 @@ This is the most important component — the central input area. ### 10.1 Logo **OLD:** + - 16px mark with 4:5 aspect ratio - No visual effects **NEW (Aurora):** + - `filter: drop-shadow(0 0 6px var(--aurora-accent))` — **subtle cyan glow** - Hover: glow doubles to `0 0 12px` - `transition: filter 0.3s var(--ease-aurora)` @@ -898,10 +1012,12 @@ This is the most important component — the central input area. ### 10.2 Text Shimmer **OLD:** + - Per-character opacity shimmer: `text-weaker → text-weak → text-base → text-strong` - 1200ms cycle, 45ms stagger per character **NEW (Aurora):** + - `background-image: linear-gradient(90deg, cyan, violet, rose, violet, cyan)` — **aurora rainbow gradient** - `background-size: 300% 100%` — for smooth animation - Shimmer cycles through aurora accent colors instead of just opacity @@ -909,40 +1025,48 @@ This is the most important component — the central input area. ### 10.3 File Icon **OLD:** + - Standard SVG icon rendering - No hover effect **NEW (Aurora):** + - Hover: `filter: drop-shadow(0 0 4px var(--aurora-accent))` — **icon glows cyan on hover** - `transition: filter 0.2s var(--ease-aurora)` ### 10.4 Provider Icon **OLD:** + - Standard SVG rendering (provider logos: Anthropic, OpenAI, etc.) - No hover effect **NEW (Aurora):** + - `transition: filter 0.2s var(--ease-aurora)` — ready for hover effects - Subtle interaction preparation ### 10.5 App Icon **OLD:** + - Standard icon rendering - No special treatment **NEW (Aurora):** + - Inherits general aurora interaction patterns ### 10.6 Image Preview **OLD:** + - Full-screen overlay modal - `border-radius: var(--radius-lg)` (~12px) - Raised surface bg with heavy shadows **NEW (Aurora):** + - `border-radius: 14px` - `border: 1px solid rgba(0, 212, 255, 0.08)` — aurora border - `overflow: hidden` — clean edge containment @@ -950,10 +1074,12 @@ This is the most important component — the central input area. ### 10.7 Session Review **OLD:** + - Standard panel with inset bg - No distinct border treatment **NEW (Aurora):** + - `border-radius: 14px` - `border: 1px solid rgba(0, 212, 255, 0.08)` — aurora border - Clean card-like containment @@ -964,19 +1090,20 @@ This is the most important component — the central input area. ### Keyframes -| Animation | Purpose | Behavior | -|-----------|---------|----------| -| `aurora-breathe` | Thinking indicator glow | Opacity 0.7↔1.0, shadow 8px↔18px, 2s ease-in-out infinite | -| `aurora-breathe-light` | Light mode variant | Same pattern, teal instead of cyan, softer glow | -| `aurora-pulse` | Generic element pulse | Shadow 8px↔20px, 2s cycle | -| `aurora-shimmer` | Background position | -200% → 200%, for gradient animations | -| `aurora-dialog-in` | Dialog open | scale(0.92) translateY(8px) → scale(1), 300ms snappy | -| `aurora-dialog-out` | Dialog close | scale(1) → scale(0.95) translateY(4px), 150ms ease-in | -| `thinkingPulse` | App-level thinking pulse | opacity 1↔0.5, shadow 8px↔2px, 1.5s | +| Animation | Purpose | Behavior | +| ---------------------- | ------------------------ | ----------------------------------------------------------- | +| `aurora-breathe` | Thinking indicator glow | Opacity 0.7↔1.0, shadow 8px↔18px, 2s ease-in-out infinite | +| `aurora-breathe-light` | Light mode variant | Same pattern, teal instead of cyan, softer glow | +| `aurora-pulse` | Generic element pulse | Shadow 8px↔20px, 2s cycle | +| `aurora-shimmer` | Background position | -200% → 200%, for gradient animations | +| `aurora-dialog-in` | Dialog open | scale(0.92) translateY(8px) → scale(1), 300ms snappy | +| `aurora-dialog-out` | Dialog close | scale(1) → scale(0.95) translateY(4px), 150ms ease-in | +| `thinkingPulse` | App-level thinking pulse | opacity 1↔0.5, shadow 8px↔2px, 1.5s | ### Reduced Motion All animations respect `prefers-reduced-motion: reduce`: + - All `animation-duration` forced to `0.01ms` - All `transition-duration` forced to `0.01ms` - `animation-iteration-count: 1` — no looping @@ -990,6 +1117,7 @@ All animations respect `prefers-reduced-motion: reduce`: **Layout:** Centered content with logo at top, server status badge, list of recent projects (or empty state with "Open project" button). **Aurora Changes:** + - Logo: glowing cyan drop-shadow - Empty state: aurora radial gradient background - Project list items: glow selection (cyan left border + tinted bg) @@ -998,11 +1126,13 @@ All animations respect `prefers-reduced-motion: reduce`: ### 12.2 Session Page **Layout:** Three-zone layout: + 1. **Sidebar** (left) — session list, project info 2. **Main content** (center) — message thread with auto-scroll 3. **Prompt dock** (bottom) — fixed input area **Aurora Changes:** + - Sidebar: frosted glass panel with blur - Titlebar: glass header with blur - Messages: user bubbles with glass + cyan border, assistant with violet border + identity label @@ -1022,6 +1152,7 @@ All animations respect `prefers-reduced-motion: reduce`: **OLD:** No texture **NEW:** `body::before` pseudo-element: + - `position: fixed; inset: 0` — covers entire viewport - `opacity: 0.02` — barely perceptible - SVG noise pattern: `feTurbulence fractalNoise baseFrequency=0.9 numOctaves=4` @@ -1047,27 +1178,27 @@ All animations respect `prefers-reduced-motion: reduce`: ## Summary: Design Language Shift -| Aspect | OLD (oc-1) | NEW (Aurora) | -|--------|-----------|--------------| -| **Philosophy** | Flat, utilitarian | Luminescent, atmospheric | -| **Backgrounds** | Solid opaque | Glass morphism (blur + transparency) | -| **Borders** | Theme tokens (gray) | Cyan-tinted rgba borders | -| **Shadows** | CSS shadow tokens | Contextual glow halos | -| **Radius** | 4-16px (token-based) | 6-24px (larger, softer) | -| **Hover** | Background color change | Glow + float + border brighten | -| **Focus** | Standard ring | Cyan glow with 3px ring | -| **Active** | Scale down | Scale down + glow reset | -| **Colors** | Neutral grays | Cyan/violet/amber/rose semantic | -| **User identity** | None | Cyan border + "You" label | -| **Assistant identity** | None | Violet border + "⬡ OPENCODE AI" | -| **Thinking** | Plain text | Pulsing glow dot + card | -| **Code blocks** | Solid bg + shadow | Glass bg + aurora border | -| **Scrollbar** | Hidden | Visible, thin, cyan-tinted | -| **Animations** | 150ms ease | Spring/snappy easing | -| **Texture** | None | Film grain overlay | -| **Selection** | Default | Cyan-tinted | -| **Progress** | Solid fill | Aurora gradient + glow | -| **Tags** | Square chips | Full pill shape | -| **Menus/Dialogs** | Solid raised | Frosted glass + glow shadow | -| **Logo** | Static | Glowing drop-shadow | -| **Links** | Standard | Glow on hover | +| Aspect | OLD (oc-1) | NEW (Aurora) | +| ---------------------- | ----------------------- | ------------------------------------ | +| **Philosophy** | Flat, utilitarian | Luminescent, atmospheric | +| **Backgrounds** | Solid opaque | Glass morphism (blur + transparency) | +| **Borders** | Theme tokens (gray) | Cyan-tinted rgba borders | +| **Shadows** | CSS shadow tokens | Contextual glow halos | +| **Radius** | 4-16px (token-based) | 6-24px (larger, softer) | +| **Hover** | Background color change | Glow + float + border brighten | +| **Focus** | Standard ring | Cyan glow with 3px ring | +| **Active** | Scale down | Scale down + glow reset | +| **Colors** | Neutral grays | Cyan/violet/amber/rose semantic | +| **User identity** | None | Cyan border + "You" label | +| **Assistant identity** | None | Violet border + "⬡ OPENCODE AI" | +| **Thinking** | Plain text | Pulsing glow dot + card | +| **Code blocks** | Solid bg + shadow | Glass bg + aurora border | +| **Scrollbar** | Hidden | Visible, thin, cyan-tinted | +| **Animations** | 150ms ease | Spring/snappy easing | +| **Texture** | None | Film grain overlay | +| **Selection** | Default | Cyan-tinted | +| **Progress** | Solid fill | Aurora gradient + glow | +| **Tags** | Square chips | Full pill shape | +| **Menus/Dialogs** | Solid raised | Frosted glass + glow shadow | +| **Logo** | Static | Glowing drop-shadow | +| **Links** | Standard | Glow on hover | diff --git a/docs/plans/2025-02-28-opencode-installation-guide.md b/docs/plans/2025-02-28-opencode-installation-guide.md index 078d8225935a..0dc403d2b711 100644 --- a/docs/plans/2025-02-28-opencode-installation-guide.md +++ b/docs/plans/2025-02-28-opencode-installation-guide.md @@ -27,19 +27,21 @@ ## Prerequisites ### For CLI Usage -| Requirement | Version | Notes | -|---|---|---| -| **macOS / Linux / Windows** | Any modern | Windows via WSL recommended | -| **API Key** | — | At minimum one of: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`, or other supported provider key | + +| Requirement | Version | Notes | +| --------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------- | +| **macOS / Linux / Windows** | Any modern | Windows via WSL recommended | +| **API Key** | — | At minimum one of: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`, or other supported provider key | ### For Building from Source -| Requirement | Version | Notes | -|---|---|---| -| **Bun** | ≥ 1.3 | Runtime and package manager. Install: `curl -fsSL https://bun.sh/install \| bash` | -| **Node.js** | ≥ 20 | Required by some tooling (tsgo, etc.) | -| **Git** | Any | For cloning | -| **Rust + Cargo** | stable | Only for desktop app builds (Tauri) | -| **Xcode CLI Tools** | Latest | macOS only — `xcode-select --install` | + +| Requirement | Version | Notes | +| ------------------- | ------- | --------------------------------------------------------------------------------- | +| **Bun** | ≥ 1.3 | Runtime and package manager. Install: `curl -fsSL https://bun.sh/install \| bash` | +| **Node.js** | ≥ 20 | Required by some tooling (tsgo, etc.) | +| **Git** | Any | For cloning | +| **Rust + Cargo** | stable | Only for desktop app builds (Tauri) | +| **Xcode CLI Tools** | Latest | macOS only — `xcode-select --install` | --- @@ -54,6 +56,7 @@ curl -fsSL https://opencode.ai/install | bash ``` This script: + - Auto-detects your OS (macOS/Linux/Windows) and architecture (arm64/x64) - Detects musl vs glibc on Linux, x64 baseline (no AVX2) variants - Downloads the latest release from GitHub @@ -61,6 +64,7 @@ This script: - Adds the bin directory to your shell PATH (fish/zsh/bash/ash/sh) **Options:** + ```bash # Install a specific version curl -fsSL https://opencode.ai/install | bash -s -- --version 1.2.15 @@ -83,6 +87,7 @@ opencode --version ### Package Managers #### npm / Bun / pnpm / Yarn + ```bash # npm (global) npm i -g opencode-ai@latest @@ -98,32 +103,38 @@ yarn global add opencode-ai@latest ``` #### Homebrew (macOS / Linux) + ```bash brew install opencode-ai/tap/opencode ``` #### Scoop (Windows) + ```powershell scoop bucket add opencode https://github.com/anomalyco/scoop-bucket scoop install opencode ``` #### Chocolatey (Windows) + ```powershell choco install opencode-ai ``` #### Arch Linux (pacman / paru) + ```bash paru -S opencode-ai-bin ``` #### mise + ```bash mise use -g opencode-ai@latest ``` #### Nix + ```bash # Run directly nix run github:anomalyco/opencode @@ -155,6 +166,7 @@ bun link ``` **For the prax-dev branch:** + ```bash git clone https://github.com/PrakharMNNIT/opencode.git cd opencode @@ -175,6 +187,7 @@ The desktop app is a **Tauri 2** application that bundles the web UI with a nati ### Pre-built DMG (macOS) For the official release builds, download from [GitHub Releases](https://github.com/anomalyco/opencode/releases): + - **macOS:** `.dmg` file — mount, drag to Applications - **Windows:** `.exe` (NSIS installer) - **Linux:** `.deb`, `.rpm`, or `.AppImage` @@ -184,11 +197,12 @@ For the official release builds, download from [GitHub Releases](https://github. ### Building the Desktop App from Source #### Prerequisites (Additional) -| Requirement | Notes | -|---|---| -| **Rust (stable)** | `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \| sh` | -| **Tauri CLI** | Installed as a dev dependency, or: `cargo install tauri-cli` | -| **System libs** | macOS: Xcode CLI tools. Linux: `libwebkit2gtk-4.1-dev`, `libappindicator3-dev`, `librsvg2-dev`, `patchelf` | + +| Requirement | Notes | +| ----------------- | ---------------------------------------------------------------------------------------------------------- | +| **Rust (stable)** | `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \| sh` | +| **Tauri CLI** | Installed as a dev dependency, or: `cargo install tauri-cli` | +| **System libs** | macOS: Xcode CLI tools. Linux: `libwebkit2gtk-4.1-dev`, `libappindicator3-dev`, `librsvg2-dev`, `patchelf` | #### Build Steps @@ -285,6 +299,7 @@ opencode dev web ### 3. Theme Configuration OpenCode supports multiple themes, including the **Aurora** design system (added in this prax-dev branch): + - **Aurora Dark** — "Digital luminescence" — elements emit light into void - **Aurora Light** — "Prismatic refraction" — light refracts through crystal @@ -296,33 +311,34 @@ Themes can be selected in the settings dialog within the UI. The `prax-dev` branch is a customized development build with the following differences from upstream: -| Aspect | Upstream (`dev`) | Prax-Dev (`prax-dev`) | -|---|---|---| -| **Product Name** | "OpenCode" | "OpenCode Prax-Dev" | -| **Bundle ID** | `ai.opencode.desktop` | `ai.opencode.desktop.prax-dev` | -| **Icon Set** | `icons/prod/` | `icons/prax-dev/` | -| **Tauri Config** | `tauri.conf.json` | `tauri.conf.json` (modified for prax-dev) | -| **Aurora Theme** | Not yet merged | ✅ Full Aurora design system | -| **CLI Command** | `opencode` | `opencode` (same) | -| **Can Coexist** | — | Yes, different bundle ID allows side-by-side install | +| Aspect | Upstream (`dev`) | Prax-Dev (`prax-dev`) | +| ---------------- | --------------------- | ---------------------------------------------------- | +| **Product Name** | "OpenCode" | "OpenCode Prax-Dev" | +| **Bundle ID** | `ai.opencode.desktop` | `ai.opencode.desktop.prax-dev` | +| **Icon Set** | `icons/prod/` | `icons/prax-dev/` | +| **Tauri Config** | `tauri.conf.json` | `tauri.conf.json` (modified for prax-dev) | +| **Aurora Theme** | Not yet merged | ✅ Full Aurora design system | +| **CLI Command** | `opencode` | `opencode` (same) | +| **Can Coexist** | — | Yes, different bundle ID allows side-by-side install | ### Sidecar Binaries The desktop app bundles the CLI as a sidecar. Supported targets: -| Target Triple | Platform | -|---|---| -| `aarch64-apple-darwin` | macOS Apple Silicon | -| `x86_64-apple-darwin` | macOS Intel | -| `x86_64-pc-windows-msvc` | Windows x64 | -| `x86_64-unknown-linux-gnu` | Linux x64 | -| `aarch64-unknown-linux-gnu` | Linux ARM64 | +| Target Triple | Platform | +| --------------------------- | ------------------- | +| `aarch64-apple-darwin` | macOS Apple Silicon | +| `x86_64-apple-darwin` | macOS Intel | +| `x86_64-pc-windows-msvc` | Windows x64 | +| `x86_64-unknown-linux-gnu` | Linux x64 | +| `aarch64-unknown-linux-gnu` | Linux ARM64 | --- ## Troubleshooting ### CLI not found after installation + ```bash # Reload your shell config source ~/.zshrc # or ~/.bashrc @@ -332,6 +348,7 @@ export PATH="$HOME/.opencode/bin:$PATH" ``` ### API key errors + ```bash # Verify your key is set echo $ANTHROPIC_API_KEY @@ -341,6 +358,7 @@ opencode --help ``` ### Desktop app — sidecar not found + ```bash # Rebuild the sidecar cd packages/opencode && bun run build @@ -348,6 +366,7 @@ cd ../desktop && bun run predev ``` ### Build errors on macOS + ```bash # Ensure Xcode CLI tools are installed xcode-select --install @@ -357,6 +376,7 @@ rustup update stable ``` ### Build errors on Linux + ```bash # Install required system libraries (Ubuntu/Debian) sudo apt install -y \ @@ -369,6 +389,7 @@ sudo apt install -y \ ``` ### Nix build + ```bash # Build CLI via Nix nix build github:anomalyco/opencode#opencode @@ -379,4 +400,4 @@ nix build github:anomalyco/opencode#desktop --- -*Guide compiled: 2025-02-28 | Branch: prax-dev | CLI version: 1.2.15* +_Guide compiled: 2025-02-28 | Branch: prax-dev | CLI version: 1.2.15_ diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index cc4e98f7e626..60e654884601 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -1503,7 +1503,9 @@ export const PromptInput: Component = (props) => { {language.t("prompt.action.stop")} - {language.t("common.key.esc")} + + {language.t("common.key.esc")} + diff --git a/packages/app/src/components/session/session-context-tab.tsx b/packages/app/src/components/session/session-context-tab.tsx index eaeda49d4069..a1cb6edcb9b9 100644 --- a/packages/app/src/components/session/session-context-tab.tsx +++ b/packages/app/src/components/session/session-context-tab.tsx @@ -287,7 +287,8 @@ export function SessionContextTab() { const sdk = useSDK() const [compacting, setCompacting] = createSignal(false) const usage = () => c().usage ?? 0 - const color = () => usage() > 80 ? "var(--syntax-error)" : usage() > 60 ? "var(--syntax-warning)" : "var(--syntax-success)" + const color = () => + usage() > 80 ? "var(--syntax-error)" : usage() > 60 ? "var(--syntax-warning)" : "var(--syntax-success)" const compact = async () => { if (!params.id || compacting()) return @@ -325,10 +326,15 @@ export function SessionContextTab() { disabled={compacting() || visibleUserMessages().length === 0} onClick={compact} > - {compacting() ? language.t("command.session.compact") + "..." : language.t("command.session.compact")} + {compacting() + ? language.t("command.session.compact") + "..." + : language.t("command.session.compact")} - + { - + { - + {/* Logo with entrance animation */} - + {/* Server status badge */} - + BEDROCK_CONTEXT_CAP && - m.id.includes("anthropic") - ) { + if (provider.id === "amazon-bedrock" && m.limit.context > BEDROCK_CONTEXT_CAP && m.id.includes("anthropic")) { m.limit.context = BEDROCK_CONTEXT_CAP } diff --git a/packages/opencode/src/server/routes/session.ts b/packages/opencode/src/server/routes/session.ts index 9cad7c14db33..e66be5813324 100644 --- a/packages/opencode/src/server/routes/session.ts +++ b/packages/opencode/src/server/routes/session.ts @@ -972,7 +972,11 @@ export const SessionRoutes = lazy(() => "json", z.object({ text: z.string().min(1).meta({ description: "The message text to inject" }), - mode: z.enum(["queue", "steer"]).optional().default("queue").meta({ description: "queue waits for turn end, steer injects mid-turn" }), + mode: z + .enum(["queue", "steer"]) + .optional() + .default("queue") + .meta({ description: "queue waits for turn end, steer injects mid-turn" }), }), ), async (c) => { diff --git a/packages/opencode/src/session/retry.ts b/packages/opencode/src/session/retry.ts index 303a819b45e1..8956b6903145 100644 --- a/packages/opencode/src/session/retry.ts +++ b/packages/opencode/src/session/retry.ts @@ -96,7 +96,12 @@ export namespace SessionRetry { // Respect explicit isRetryable: false from provider SDKs (e.g. Bedrock) if (json.isRetryable === false) return undefined // 4xx errors are client errors — not retryable - const status = typeof json.status === "number" ? json.status : typeof json.statusCode === "number" ? json.statusCode : undefined + const status = + typeof json.status === "number" + ? json.status + : typeof json.statusCode === "number" + ? json.statusCode + : undefined if (status !== undefined && status >= 400 && status < 500) return undefined return JSON.stringify(json) } catch { diff --git a/packages/opencode/src/tool/task.ts b/packages/opencode/src/tool/task.ts index 0b783e423b8b..b5be8b13800d 100644 --- a/packages/opencode/src/tool/task.ts +++ b/packages/opencode/src/tool/task.ts @@ -144,9 +144,7 @@ export const TaskTool = Tool.define("task", async (ctx) => { if ((result.info as MessageV2.Assistant).error) { const errorObj = (result.info as MessageV2.Assistant).error! - const msg = ("data" in errorObj && "message" in errorObj.data) - ? errorObj.data.message - : errorObj.name + const msg = "data" in errorObj && "message" in errorObj.data ? errorObj.data.message : errorObj.name return { title: params.description, metadata: { sessionId: session.id, model }, diff --git a/packages/opencode/test/tool/write.test.ts b/packages/opencode/test/tool/write.test.ts index 695d48ccbbc7..ce1a4347a9f1 100644 --- a/packages/opencode/test/tool/write.test.ts +++ b/packages/opencode/test/tool/write.test.ts @@ -166,7 +166,8 @@ describe("tool.write", () => { // On Unix systems, check permissions if (process.platform !== "win32") { const stats = await fs.stat(filepath) - expect(stats.mode & 0o777).toBe(0o644) + // Expect either 644 or 664 depending on umask + expect([0o644, 0o664]).toContain(stats.mode & 0o777) } }, }) diff --git a/packages/ui/src/components/logo.tsx b/packages/ui/src/components/logo.tsx index 27f2ed258a30..dba20ef5ea1c 100644 --- a/packages/ui/src/components/logo.tsx +++ b/packages/ui/src/components/logo.tsx @@ -11,43 +11,68 @@ export const Mark = (props: { class?: string }) => { > - - + + - - - + + + - + - - - - - + + + + + - + - - + + - - - + + + - - - + + + - - + + ) @@ -65,97 +90,161 @@ export const Splash = (props: Pick, "ref" | "class">) => { > - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - + + - + - - + + - - - + + + - + - - + + - - + + - - - + + + - - + + {/* Core glow */} - - - - + + + + {/* Bracket emission glow */} - - + + {/* Crisp brackets */} - - - - + + + + {/* Central cursor/slash */} - + - - + + {/* Cursor blink dot */} - - + + {/* Particles */} - - - - + + + + @@ -172,12 +261,12 @@ export const Logo = (props: { class?: string }) => { > - - + + - - + + diff --git a/packages/ui/src/styles/animations.css b/packages/ui/src/styles/animations.css index f504d1b0812f..ba892af55a2e 100644 --- a/packages/ui/src/styles/animations.css +++ b/packages/ui/src/styles/animations.css @@ -161,36 +161,96 @@ animation: fadeUp 0.4s var(--ease-out-expo) forwards; opacity: 0; - &:nth-child(1) { animation-delay: 0.05s; } - &:nth-child(2) { animation-delay: 0.1s; } - &:nth-child(3) { animation-delay: 0.15s; } - &:nth-child(4) { animation-delay: 0.2s; } - &:nth-child(5) { animation-delay: 0.25s; } - &:nth-child(6) { animation-delay: 0.3s; } - &:nth-child(7) { animation-delay: 0.35s; } - &:nth-child(8) { animation-delay: 0.4s; } - &:nth-child(9) { animation-delay: 0.45s; } - &:nth-child(10) { animation-delay: 0.5s; } - &:nth-child(11) { animation-delay: 0.55s; } - &:nth-child(12) { animation-delay: 0.6s; } - &:nth-child(13) { animation-delay: 0.65s; } - &:nth-child(14) { animation-delay: 0.7s; } - &:nth-child(15) { animation-delay: 0.75s; } - &:nth-child(16) { animation-delay: 0.8s; } - &:nth-child(17) { animation-delay: 0.85s; } - &:nth-child(18) { animation-delay: 0.9s; } - &:nth-child(19) { animation-delay: 0.95s; } - &:nth-child(20) { animation-delay: 1s; } - &:nth-child(21) { animation-delay: 1.05s; } - &:nth-child(22) { animation-delay: 1.1s; } - &:nth-child(23) { animation-delay: 1.15s; } - &:nth-child(24) { animation-delay: 1.2s; } - &:nth-child(25) { animation-delay: 1.25s; } - &:nth-child(26) { animation-delay: 1.3s; } - &:nth-child(27) { animation-delay: 1.35s; } - &:nth-child(28) { animation-delay: 1.4s; } - &:nth-child(29) { animation-delay: 1.45s; } - &:nth-child(30) { animation-delay: 1.5s; } + &:nth-child(1) { + animation-delay: 0.05s; + } + &:nth-child(2) { + animation-delay: 0.1s; + } + &:nth-child(3) { + animation-delay: 0.15s; + } + &:nth-child(4) { + animation-delay: 0.2s; + } + &:nth-child(5) { + animation-delay: 0.25s; + } + &:nth-child(6) { + animation-delay: 0.3s; + } + &:nth-child(7) { + animation-delay: 0.35s; + } + &:nth-child(8) { + animation-delay: 0.4s; + } + &:nth-child(9) { + animation-delay: 0.45s; + } + &:nth-child(10) { + animation-delay: 0.5s; + } + &:nth-child(11) { + animation-delay: 0.55s; + } + &:nth-child(12) { + animation-delay: 0.6s; + } + &:nth-child(13) { + animation-delay: 0.65s; + } + &:nth-child(14) { + animation-delay: 0.7s; + } + &:nth-child(15) { + animation-delay: 0.75s; + } + &:nth-child(16) { + animation-delay: 0.8s; + } + &:nth-child(17) { + animation-delay: 0.85s; + } + &:nth-child(18) { + animation-delay: 0.9s; + } + &:nth-child(19) { + animation-delay: 0.95s; + } + &:nth-child(20) { + animation-delay: 1s; + } + &:nth-child(21) { + animation-delay: 1.05s; + } + &:nth-child(22) { + animation-delay: 1.1s; + } + &:nth-child(23) { + animation-delay: 1.15s; + } + &:nth-child(24) { + animation-delay: 1.2s; + } + &:nth-child(25) { + animation-delay: 1.25s; + } + &:nth-child(26) { + animation-delay: 1.3s; + } + &:nth-child(27) { + animation-delay: 1.35s; + } + &:nth-child(28) { + animation-delay: 1.4s; + } + &:nth-child(29) { + animation-delay: 1.45s; + } + &:nth-child(30) { + animation-delay: 1.5s; + } } /* ---- Reduced motion preference ---- */