Spaces:
Paused
Paused
Pi Web: no blue anywhere (neutral gray accent, underlined links, neutral code keywords and diagrams)
Browse files- piweb/hf-design.patch +616 -119
piweb/hf-design.patch
CHANGED
|
@@ -1,16 +1,17 @@
|
|
| 1 |
diff --git a/AGENTS.md b/AGENTS.md
|
| 2 |
-
index a5c8983..
|
| 3 |
--- a/AGENTS.md
|
| 4 |
+++ b/AGENTS.md
|
| 5 |
-
@@ -138,6 +138,
|
| 6 |
extension-ui-queue.ts FIFO queues for extension dialogs and custom panels, by request id; isBlockingExtensionUiRequest() (which method a run waits on)
|
| 7 |
markdown.ts shared markdown helpers
|
| 8 |
markdown-blocks.ts splitMarkdownBlocks(): where a streaming reply may be cut into blocks parsed apart
|
| 9 |
+ step-summary.ts a turn's steps counted by kind (ran, read, edited, …) for their one summary line above the answer
|
|
|
|
| 10 |
highlight-queue.ts code fences past a task's highlight budget get their colors in idle slices
|
| 11 |
prism-languages.ts loadPrismLanguages(): every Prism language in a chunk of its own, loaded when a highlighter first needs it
|
| 12 |
gfm-autolink-email-loader.cjs bundler loader: remark-gfm's email regex without a lookbehind literal
|
| 13 |
-
@@ -174,7 +
|
| 14 |
components/
|
| 15 |
AppShell.tsx layout, URL state, tab management
|
| 16 |
SessionSidebar.tsx toolbar row (Sessions | Files tabs, or the Pi Web brand with the files below them; new, search): tree, archive, menus, toast; files section (resizable, foldable; one header row with the file keys and changed-files count, the picker's row) or tab (picker boxes + file buttons), FileExplorer
|
|
@@ -20,7 +21,7 @@ index a5c8983..0f7f118 100644
|
|
| 20 |
SessionTree.tsx virtualized rows of the session tree and the archive view
|
| 21 |
SidebarMenu.tsx sidebar popup menu (bottom sheet on phones), portaled to body: filter field, item secondary actions, the files tab's classic look
|
| 22 |
ProjectWorktreePicker.tsx project and worktree boxes + menus: the files' (one row below the sessions, stacked in the tab) and the new-session bar's
|
| 23 |
-
@@ -274,3 +
|
| 24 |
--accent --user-bg --tool-bg
|
| 25 |
--font-mono
|
| 26 |
```
|
|
@@ -41,6 +42,53 @@ index a5c8983..0f7f118 100644
|
|
| 41 |
+`.ui-btn` classes in `app/globals.css` (`--sm`, `--xs`, `--lg`, `--icon`, `--primary`, …); the
|
| 42 |
+transcript's pieces (`.turn-user`, `.turn-reply`, `.steps-summary`, `.tool-call-row`, `.composer`)
|
| 43 |
+are styled there too.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 44 |
diff --git a/app/favicon.ico b/app/favicon.ico
|
| 45 |
index 2da6b7dc00c64c59ef23cff153a6051478ad9315..b407f41443e3e94f4204a1862227e0d457f7d823 100644
|
| 46 |
GIT binary patch
|
|
@@ -3080,10 +3128,10 @@ index 0000000..8d003fe
|
|
| 3080 |
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
| 3081 |
+OTHER DEALINGS IN THE FONT SOFTWARE.
|
| 3082 |
diff --git a/app/globals.css b/app/globals.css
|
| 3083 |
-
index d6b5535..
|
| 3084 |
--- a/app/globals.css
|
| 3085 |
+++ b/app/globals.css
|
| 3086 |
-
@@ -24,46 +24,
|
| 3087 |
--font-mono-font: var(--font-mono);
|
| 3088 |
}
|
| 3089 |
|
|
@@ -3110,10 +3158,11 @@ index d6b5535..69eff6c 100644
|
|
| 3110 |
+ --text: #2e2e2e;
|
| 3111 |
+ --text-muted: #525252;
|
| 3112 |
+ --text-dim: #666666;
|
| 3113 |
-
+ --accent: #
|
| 3114 |
-
+ --accent-hover: #
|
| 3115 |
--accent-contrast: #ffffff;
|
| 3116 |
- --user-bg: #eff6ff;
|
|
|
|
| 3117 |
+ --user-bg: #f5f5f5;
|
| 3118 |
--assistant-bg: #ffffff;
|
| 3119 |
- --tool-bg: #f9fafb;
|
|
@@ -3131,7 +3180,7 @@ index d6b5535..69eff6c 100644
|
|
| 3131 |
+ --danger-soft: rgba(193, 0, 7, 0.07);
|
| 3132 |
+ --success: #008236;
|
| 3133 |
+ --warning: #bb4d00;
|
| 3134 |
-
+ --focus-ring: #
|
| 3135 |
+ --scrim: rgba(10, 10, 10, 0.32);
|
| 3136 |
+ --border: transparent;
|
| 3137 |
+ --shadow-pop: 0 10px 30px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.06);
|
|
@@ -3163,9 +3212,10 @@ index d6b5535..69eff6c 100644
|
|
| 3163 |
+ --text: #d4d4d4;
|
| 3164 |
+ --text-muted: #a3a3a3;
|
| 3165 |
+ --text-dim: #8a8a8a;
|
| 3166 |
-
+ --accent: #
|
| 3167 |
-
+ --accent-hover: #
|
| 3168 |
+ --accent-contrast: #0a0a0a;
|
|
|
|
| 3169 |
+ --user-bg: #1a1a1a;
|
| 3170 |
+ --assistant-bg: #0a0a0a;
|
| 3171 |
+ --tool-bg: #1a1a1a;
|
|
@@ -3179,7 +3229,7 @@ index d6b5535..69eff6c 100644
|
|
| 3179 |
+ --danger-soft: rgba(255, 100, 103, 0.10);
|
| 3180 |
+ --success: #05df72;
|
| 3181 |
+ --warning: #ffb900;
|
| 3182 |
-
+ --focus-ring: #
|
| 3183 |
+ --scrim: rgba(0, 0, 0, 0.6);
|
| 3184 |
+ --border: transparent;
|
| 3185 |
+ --shadow-pop: 0 12px 36px rgba(0, 0, 0, 0.6), 0 2px 8px rgba(0, 0, 0, 0.4);
|
|
@@ -3219,7 +3269,7 @@ index d6b5535..69eff6c 100644
|
|
| 3219 |
}
|
| 3220 |
|
| 3221 |
[data-theme="mist"] {
|
| 3222 |
-
@@ -72,7 +
|
| 3223 |
--bg-panel: #e9f0ee;
|
| 3224 |
--bg-hover: #e0eae7;
|
| 3225 |
--bg-selected: #d7e4df;
|
|
@@ -3228,7 +3278,7 @@ index d6b5535..69eff6c 100644
|
|
| 3228 |
--text: #202e2b;
|
| 3229 |
--text-muted: #455f56;
|
| 3230 |
--text-dim: #52685f;
|
| 3231 |
-
@@ -83,6 +
|
| 3232 |
--assistant-bg: #f4f8f7;
|
| 3233 |
--tool-bg: #ecf3f0;
|
| 3234 |
--bg-subtle: rgba(24,65,50,0.04);
|
|
@@ -3247,7 +3297,7 @@ index d6b5535..69eff6c 100644
|
|
| 3247 |
}
|
| 3248 |
|
| 3249 |
[data-theme="rose"] {
|
| 3250 |
-
@@ -91,7 +
|
| 3251 |
--bg-panel: #f3edef;
|
| 3252 |
--bg-hover: #eee3e7;
|
| 3253 |
--bg-selected: #e8dce1;
|
|
@@ -3256,7 +3306,7 @@ index d6b5535..69eff6c 100644
|
|
| 3256 |
--text: #34282e;
|
| 3257 |
--text-muted: #65505a;
|
| 3258 |
--text-dim: #705b65;
|
| 3259 |
-
@@ -102,6 +
|
| 3260 |
--assistant-bg: #fcf7f8;
|
| 3261 |
--tool-bg: #f7eef2;
|
| 3262 |
--bg-subtle: rgba(89,39,62,0.04);
|
|
@@ -3275,7 +3325,7 @@ index d6b5535..69eff6c 100644
|
|
| 3275 |
}
|
| 3276 |
|
| 3277 |
html[data-theme="pine"],
|
| 3278 |
-
@@ -111,7 +
|
| 3279 |
--bg-panel: #212b28;
|
| 3280 |
--bg-hover: #2b3632;
|
| 3281 |
--bg-selected: #35433c;
|
|
@@ -3284,7 +3334,7 @@ index d6b5535..69eff6c 100644
|
|
| 3284 |
--text: #e6ede8;
|
| 3285 |
--text-muted: #c3d0c6;
|
| 3286 |
--text-dim: #afc2b5;
|
| 3287 |
-
@@ -122,6 +
|
| 3288 |
--assistant-bg: #19201f;
|
| 3289 |
--tool-bg: #222d29;
|
| 3290 |
--bg-subtle: rgba(210,236,219,0.04);
|
|
@@ -3303,7 +3353,7 @@ index d6b5535..69eff6c 100644
|
|
| 3303 |
}
|
| 3304 |
|
| 3305 |
/* WebKit reports dvh/innerHeight without the status-bar strip for installed
|
| 3306 |
-
@@ -135,28 +
|
| 3307 |
}
|
| 3308 |
}
|
| 3309 |
|
|
@@ -3336,7 +3386,7 @@ index d6b5535..69eff6c 100644
|
|
| 3336 |
}
|
| 3337 |
|
| 3338 |
/* Theme toggle: circular wipe via View Transitions API.
|
| 3339 |
-
@@ -218,9 +
|
| 3340 |
margin-bottom: 22px;
|
| 3341 |
}
|
| 3342 |
|
|
@@ -3348,16 +3398,26 @@ index d6b5535..69eff6c 100644
|
|
| 3348 |
}
|
| 3349 |
|
| 3350 |
.web-login-brand h1 {
|
| 3351 |
-
@@ -247,
|
| 3352 |
min-width: 0;
|
| 3353 |
padding: 10px 10px 10px 14px;
|
| 3354 |
border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
|
| 3355 |
- border-radius: 14px;
|
| 3356 |
+ border-radius: var(--radius-xl);
|
| 3357 |
background: var(--bg);
|
| 3358 |
-
|
|
|
|
| 3359 |
transition: border-color 0.15s, box-shadow 0.15s;
|
| 3360 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3361 |
min-height: 32px;
|
| 3362 |
padding: 7px 14px;
|
| 3363 |
border: none;
|
|
@@ -3366,16 +3426,80 @@ index d6b5535..69eff6c 100644
|
|
| 3366 |
background: var(--accent);
|
| 3367 |
color: var(--accent-contrast);
|
| 3368 |
cursor: pointer;
|
| 3369 |
-
@@ -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3370 |
flex-shrink: 0;
|
| 3371 |
padding: 0 8px;
|
| 3372 |
border: 1px solid #343a46;
|
| 3373 |
- border-radius: 5px;
|
| 3374 |
+ border-radius: var(--radius-sm);
|
| 3375 |
background: transparent;
|
| 3376 |
-
|
|
|
|
| 3377 |
cursor: pointer;
|
| 3378 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3379 |
background-color: color-mix(in srgb, var(--text-dim) 45%, transparent);
|
| 3380 |
background-clip: padding-box;
|
| 3381 |
border: 2px solid transparent;
|
|
@@ -3384,7 +3508,7 @@ index d6b5535..69eff6c 100644
|
|
| 3384 |
}
|
| 3385 |
/* Only ever set background-color here: the `background` shorthand resets
|
| 3386 |
background-clip and would snap the thumb to the full 10px on hover. */
|
| 3387 |
-
@@ -538,7 +
|
| 3388 |
}
|
| 3389 |
::-webkit-scrollbar-thumb {
|
| 3390 |
border-width: 1px;
|
|
@@ -3393,7 +3517,7 @@ index d6b5535..69eff6c 100644
|
|
| 3393 |
}
|
| 3394 |
}
|
| 3395 |
|
| 3396 |
-
@@ -703,9 +
|
| 3397 |
|
| 3398 |
.extension-widget-placement-icon {
|
| 3399 |
display: block;
|
|
@@ -3406,7 +3530,7 @@ index d6b5535..69eff6c 100644
|
|
| 3406 |
fill: currentColor;
|
| 3407 |
}
|
| 3408 |
|
| 3409 |
-
@@ -826,8 +
|
| 3410 |
}
|
| 3411 |
|
| 3412 |
:root {
|
|
@@ -3417,13 +3541,13 @@ index d6b5535..69eff6c 100644
|
|
| 3417 |
--font-ui: var(--font-ui-default);
|
| 3418 |
--font-mono: var(--font-mono-default);
|
| 3419 |
--font-ui-weight: 400;
|
| 3420 |
-
@@ -883,11 +
|
| 3421 |
.markdown-body ol > li { list-style-type: decimal; }
|
| 3422 |
.markdown-body li > p { margin: 0; }
|
| 3423 |
.markdown-body li::marker {
|
| 3424 |
- color: color-mix(in srgb, var(--accent) 72%, var(--text-muted));
|
| 3425 |
- font-weight: 600;
|
| 3426 |
-
+ color: var(--text-
|
| 3427 |
+ font-weight: 500;
|
| 3428 |
}
|
| 3429 |
.markdown-body strong {
|
|
@@ -3432,7 +3556,17 @@ index d6b5535..69eff6c 100644
|
|
| 3432 |
font-weight: 700;
|
| 3433 |
}
|
| 3434 |
.markdown-body em { color: var(--text-muted); }
|
| 3435 |
-
@@ -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3436 |
max-width: 100%;
|
| 3437 |
height: auto;
|
| 3438 |
margin: 8px 0;
|
|
@@ -3451,7 +3585,7 @@ index d6b5535..69eff6c 100644
|
|
| 3451 |
}
|
| 3452 |
.markdown-body .contains-task-list {
|
| 3453 |
padding-left: 0;
|
| 3454 |
-
@@ -935,7 +
|
| 3455 |
height: 14px;
|
| 3456 |
margin: 0;
|
| 3457 |
border: 1px solid var(--border);
|
|
@@ -3460,7 +3594,7 @@ index d6b5535..69eff6c 100644
|
|
| 3460 |
background: var(--bg);
|
| 3461 |
display: inline-grid;
|
| 3462 |
place-content: center;
|
| 3463 |
-
@@ -967,8 +
|
| 3464 |
margin: 8px 0;
|
| 3465 |
overflow-x: auto;
|
| 3466 |
overflow-y: hidden;
|
|
@@ -3471,7 +3605,7 @@ index d6b5535..69eff6c 100644
|
|
| 3471 |
}
|
| 3472 |
.markdown-body table {
|
| 3473 |
border-collapse: separate;
|
| 3474 |
-
@@ -1088,7 +
|
| 3475 |
padding: 7px 8px;
|
| 3476 |
overflow: auto;
|
| 3477 |
border: 1px solid var(--border);
|
|
@@ -3480,7 +3614,7 @@ index d6b5535..69eff6c 100644
|
|
| 3480 |
background: var(--bg-panel);
|
| 3481 |
font-family: var(--font-mono);
|
| 3482 |
font-size: 11px;
|
| 3483 |
-
@@ -1108,16 +
|
| 3484 |
min-width: 0;
|
| 3485 |
max-width: 100%;
|
| 3486 |
margin: 6px 0;
|
|
@@ -3500,7 +3634,7 @@ index d6b5535..69eff6c 100644
|
|
| 3500 |
color: var(--text-dim);
|
| 3501 |
font-size: 11px;
|
| 3502 |
display: flex;
|
| 3503 |
-
@@ -1137,7 +
|
| 3504 |
}
|
| 3505 |
.markdown-code-action {
|
| 3506 |
border: 1px solid var(--border);
|
|
@@ -3509,7 +3643,7 @@ index d6b5535..69eff6c 100644
|
|
| 3509 |
padding: 2px 7px;
|
| 3510 |
background: var(--bg);
|
| 3511 |
color: var(--text-muted);
|
| 3512 |
-
@@ -1228,7 +
|
| 3513 |
max-width: 100%;
|
| 3514 |
max-height: 100%;
|
| 3515 |
object-fit: contain;
|
|
@@ -3518,7 +3652,7 @@ index d6b5535..69eff6c 100644
|
|
| 3518 |
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
|
| 3519 |
}
|
| 3520 |
|
| 3521 |
-
@@ -1244,7 +
|
| 3522 |
height: 36px;
|
| 3523 |
padding: 0;
|
| 3524 |
border: 1px solid var(--border);
|
|
@@ -3527,7 +3661,7 @@ index d6b5535..69eff6c 100644
|
|
| 3527 |
background: var(--bg-panel);
|
| 3528 |
color: var(--text-muted);
|
| 3529 |
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.28);
|
| 3530 |
-
@@ -1320,7 +
|
| 3531 |
height: 24px;
|
| 3532 |
overflow: hidden;
|
| 3533 |
border: 1px solid var(--border);
|
|
@@ -3536,7 +3670,7 @@ index d6b5535..69eff6c 100644
|
|
| 3536 |
background: var(--bg);
|
| 3537 |
}
|
| 3538 |
.mermaid-zoom-stepper button,
|
| 3539 |
-
@@ -1347,7 +
|
| 3540 |
.mermaid-zoom-icon-button {
|
| 3541 |
flex: 0 0 24px;
|
| 3542 |
border: 1px solid var(--border);
|
|
@@ -3545,7 +3679,7 @@ index d6b5535..69eff6c 100644
|
|
| 3545 |
}
|
| 3546 |
.mermaid-zoom-stepper button:hover:not(:disabled),
|
| 3547 |
.mermaid-zoom-icon-button:hover {
|
| 3548 |
-
@@ -1384,7 +
|
| 3549 |
.mermaid-zoom-canvas {
|
| 3550 |
padding: 12px;
|
| 3551 |
border: 1px solid var(--border);
|
|
@@ -3554,7 +3688,7 @@ index d6b5535..69eff6c 100644
|
|
| 3554 |
background: var(--bg-panel);
|
| 3555 |
margin: 0 auto;
|
| 3556 |
transition: width 0.15s ease;
|
| 3557 |
-
@@ -1460,7 +
|
| 3558 |
height: 24px;
|
| 3559 |
overflow: hidden;
|
| 3560 |
border: 1px solid var(--border);
|
|
@@ -3563,7 +3697,7 @@ index d6b5535..69eff6c 100644
|
|
| 3563 |
}
|
| 3564 |
|
| 3565 |
.file-viewer-mode-button {
|
| 3566 |
-
@@ -1503,12 +
|
| 3567 |
height: 32px;
|
| 3568 |
padding: 0;
|
| 3569 |
box-sizing: border-box;
|
|
@@ -3578,7 +3712,7 @@ index d6b5535..69eff6c 100644
|
|
| 3578 |
opacity: 0;
|
| 3579 |
visibility: hidden;
|
| 3580 |
pointer-events: none;
|
| 3581 |
-
@@ -1584,22 +
|
| 3582 |
|
| 3583 |
.new-session-logo {
|
| 3584 |
flex: none;
|
|
@@ -3607,7 +3741,7 @@ index d6b5535..69eff6c 100644
|
|
| 3607 |
line-height: 1.15;
|
| 3608 |
white-space: nowrap;
|
| 3609 |
}
|
| 3610 |
-
@@ -1613,7 +
|
| 3611 |
gap: 6px;
|
| 3612 |
min-width: 0;
|
| 3613 |
color: var(--text-dim);
|
|
@@ -3617,7 +3751,7 @@ index d6b5535..69eff6c 100644
|
|
| 3617 |
line-height: 1.4;
|
| 3618 |
white-space: nowrap;
|
| 3619 |
}
|
| 3620 |
-
@@ -1638,7 +
|
| 3621 |
gap: 3px;
|
| 3622 |
margin: -3px -4px;
|
| 3623 |
padding: 3px 4px;
|
|
@@ -3626,7 +3760,7 @@ index d6b5535..69eff6c 100644
|
|
| 3626 |
color: var(--accent);
|
| 3627 |
font-weight: 600;
|
| 3628 |
text-decoration: none;
|
| 3629 |
-
@@ -1652,7 +
|
| 3630 |
|
| 3631 |
/* The ring inside the box, clear of the "→" and "·" beside it. */
|
| 3632 |
.new-session-update:focus-visible {
|
|
@@ -3635,7 +3769,7 @@ index d6b5535..69eff6c 100644
|
|
| 3635 |
outline-offset: -2px;
|
| 3636 |
}
|
| 3637 |
|
| 3638 |
-
@@ -1678,12 +
|
| 3639 |
}
|
| 3640 |
|
| 3641 |
.new-session-logo {
|
|
@@ -3651,7 +3785,7 @@ index d6b5535..69eff6c 100644
|
|
| 3651 |
}
|
| 3652 |
}
|
| 3653 |
|
| 3654 |
-
@@ -1722,14 +
|
| 3655 |
min-width: 0;
|
| 3656 |
height: 29px;
|
| 3657 |
padding: 0 10px;
|
|
@@ -3669,7 +3803,7 @@ index d6b5535..69eff6c 100644
|
|
| 3669 |
}
|
| 3670 |
|
| 3671 |
/* Above a fresh composer (inline): no box until hovered or open, 12px, the
|
| 3672 |
-
@@ -1752,7 +
|
| 3673 |
height: 26px;
|
| 3674 |
padding: 0 6px;
|
| 3675 |
border: 0;
|
|
@@ -3678,7 +3812,7 @@ index d6b5535..69eff6c 100644
|
|
| 3678 |
background: transparent;
|
| 3679 |
}
|
| 3680 |
|
| 3681 |
-
@@ -1781,8 +
|
| 3682 |
max-width: 28ch;
|
| 3683 |
overflow: hidden;
|
| 3684 |
color: var(--text-dim);
|
|
@@ -3689,7 +3823,7 @@ index d6b5535..69eff6c 100644
|
|
| 3689 |
}
|
| 3690 |
|
| 3691 |
.project-picker-parent {
|
| 3692 |
-
@@ -1851,7 +
|
| 3693 |
path's color and code type, cut at its end. */
|
| 3694 |
.project-picker-label.is-alias {
|
| 3695 |
color: var(--text);
|
|
@@ -3699,7 +3833,7 @@ index d6b5535..69eff6c 100644
|
|
| 3699 |
}
|
| 3700 |
|
| 3701 |
/* A path or a branch, cut at its left (rtl), the text itself kept left to
|
| 3702 |
-
@@ -1901,7 +
|
| 3703 |
border-color: color-mix(in srgb, var(--accent) 40%, transparent);
|
| 3704 |
background: color-mix(in srgb, var(--accent) 6%, transparent);
|
| 3705 |
color: var(--text-dim);
|
|
@@ -3709,7 +3843,7 @@ index d6b5535..69eff6c 100644
|
|
| 3709 |
}
|
| 3710 |
|
| 3711 |
/* No worktree list here (the files tab): a box that says why, and does nothing. */
|
| 3712 |
-
@@ -1978,7 +
|
| 3713 |
box-sizing: border-box;
|
| 3714 |
flex: 0 0 24px;
|
| 3715 |
border: 1px solid var(--border);
|
|
@@ -3718,7 +3852,7 @@ index d6b5535..69eff6c 100644
|
|
| 3718 |
background: transparent;
|
| 3719 |
color: var(--text-muted);
|
| 3720 |
cursor: pointer;
|
| 3721 |
-
@@ -2037,7 +
|
| 3722 |
.markdown-file-preview pre {
|
| 3723 |
background: var(--bg-panel);
|
| 3724 |
border: 1px solid var(--border);
|
|
@@ -3727,7 +3861,7 @@ index d6b5535..69eff6c 100644
|
|
| 3727 |
padding: 12px 16px;
|
| 3728 |
overflow-x: auto;
|
| 3729 |
font-family: var(--font-mono);
|
| 3730 |
-
@@ -2049,7 +
|
| 3731 |
font-size: 0.9em;
|
| 3732 |
background: var(--bg-panel);
|
| 3733 |
padding: 1px 5px;
|
|
@@ -3736,7 +3870,7 @@ index d6b5535..69eff6c 100644
|
|
| 3736 |
}
|
| 3737 |
.markdown-file-preview pre code {
|
| 3738 |
background: none;
|
| 3739 |
-
@@ -2061,7 +
|
| 3740 |
margin-bottom: 20px;
|
| 3741 |
padding: 14px 16px 12px;
|
| 3742 |
border: 1px solid var(--border);
|
|
@@ -3745,7 +3879,7 @@ index d6b5535..69eff6c 100644
|
|
| 3745 |
background: var(--bg-panel);
|
| 3746 |
}
|
| 3747 |
.markdown-frontmatter-title {
|
| 3748 |
-
@@ -2474,3 +
|
| 3749 |
display: none !important;
|
| 3750 |
}
|
| 3751 |
}
|
|
@@ -4292,7 +4426,7 @@ index 79c8282..97731f1 100644
|
|
| 4292 |
lang: "en",
|
| 4293 |
icons: [
|
| 4294 |
diff --git a/app/settings.css b/app/settings.css
|
| 4295 |
-
index 0bf21eb..
|
| 4296 |
--- a/app/settings.css
|
| 4297 |
+++ b/app/settings.css
|
| 4298 |
@@ -40,9 +40,9 @@
|
|
@@ -4424,15 +4558,19 @@ index 0bf21eb..4938ff2 100644
|
|
| 4424 |
background: var(--bg-panel);
|
| 4425 |
overflow-y: auto;
|
| 4426 |
}
|
| 4427 |
-
@@ -457,
|
| 4428 |
.enabled-models-pin {
|
| 4429 |
flex-shrink: 0;
|
| 4430 |
padding: 1px 4px;
|
| 4431 |
- border-radius: 3px;
|
|
|
|
|
|
|
| 4432 |
+ border-radius: var(--radius-xs);
|
| 4433 |
-
|
| 4434 |
-
|
| 4435 |
font-size: 9px;
|
|
|
|
|
|
|
| 4436 |
@@ -497,7 +498,7 @@
|
| 4437 |
justify-content: space-between;
|
| 4438 |
gap: 18px;
|
|
@@ -4469,7 +4607,7 @@ index 0bf21eb..4938ff2 100644
|
|
| 4469 |
}
|
| 4470 |
|
| 4471 |
.config-field-label {
|
| 4472 |
-
@@ -697,
|
| 4473 |
.config-scope-tag {
|
| 4474 |
flex-shrink: 0;
|
| 4475 |
padding: 1px 5px;
|
|
@@ -4478,6 +4616,16 @@ index 0bf21eb..4938ff2 100644
|
|
| 4478 |
background: rgba(120, 120, 120, 0.12);
|
| 4479 |
color: var(--text-dim);
|
| 4480 |
font-size: 10px;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4481 |
@@ -730,7 +731,7 @@
|
| 4482 |
height: 30px;
|
| 4483 |
overflow: hidden;
|
|
@@ -5119,7 +5267,7 @@ index 737e7a5..19466a2 100644
|
|
| 5119 |
color: var(--text-muted);
|
| 5120 |
font: inherit;
|
| 5121 |
diff --git a/app/sidebar.css b/app/sidebar.css
|
| 5122 |
-
index 86728a2..
|
| 5123 |
--- a/app/sidebar.css
|
| 5124 |
+++ b/app/sidebar.css
|
| 5125 |
@@ -91,7 +91,7 @@
|
|
@@ -5131,6 +5279,15 @@ index 86728a2..fd45b6f 100644
|
|
| 5131 |
outline-offset: -2px;
|
| 5132 |
}
|
| 5133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5134 |
@@ -184,7 +184,7 @@
|
| 5135 |
right: var(--session-tree-inset-right);
|
| 5136 |
left: var(--session-tree-inset-left);
|
|
@@ -5176,6 +5333,23 @@ index 86728a2..fd45b6f 100644
|
|
| 5176 |
cursor: default;
|
| 5177 |
}
|
| 5178 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5179 |
@@ -297,20 +276,23 @@ a.session-tree-main {
|
| 5180 |
}
|
| 5181 |
}
|
|
@@ -5297,6 +5471,15 @@ index 86728a2..fd45b6f 100644
|
|
| 5297 |
text-align: left;
|
| 5298 |
white-space: nowrap;
|
| 5299 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5300 |
@@ -726,7 +714,7 @@ a.session-tree-main {
|
| 5301 |
width: 22px;
|
| 5302 |
height: 22px;
|
|
@@ -5646,7 +5829,7 @@ index 861a1c6..d5d466e 100644
|
|
| 5646 |
<span>{savedOk ? t("i18n.saved") : saving ? t("agents.saving") : t("agents.save")}</span>
|
| 5647 |
</ConfigButton>
|
| 5648 |
diff --git a/components/AppShell.tsx b/components/AppShell.tsx
|
| 5649 |
-
index c8c4ba9..
|
| 5650 |
--- a/components/AppShell.tsx
|
| 5651 |
+++ b/components/AppShell.tsx
|
| 5652 |
@@ -3,6 +3,8 @@
|
|
@@ -6147,6 +6330,15 @@ index c8c4ba9..fb61efb 100644
|
|
| 6147 |
</button>
|
| 6148 |
);
|
| 6149 |
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6150 |
@@ -2178,13 +2018,9 @@ export function AppShell() {
|
| 6151 |
onMouseLeave={(e) => { e.currentTarget.style.color = "var(--text-muted)"; }}
|
| 6152 |
>
|
|
@@ -6256,7 +6448,7 @@ index c8c4ba9..fb61efb 100644
|
|
| 6256 |
</div>
|
| 6257 |
|
| 6258 |
diff --git a/components/BranchNavigator.tsx b/components/BranchNavigator.tsx
|
| 6259 |
-
index 0f8186b..
|
| 6260 |
--- a/components/BranchNavigator.tsx
|
| 6261 |
+++ b/components/BranchNavigator.tsx
|
| 6262 |
@@ -3,6 +3,8 @@
|
|
@@ -6268,11 +6460,15 @@ index 0f8186b..e7fde3d 100644
|
|
| 6268 |
|
| 6269 |
interface Props {
|
| 6270 |
tree: SessionTreeNode[];
|
| 6271 |
-
@@ -
|
|
|
|
|
|
|
| 6272 |
color: role === "user" ? "var(--accent)" : "var(--text-dim)",
|
| 6273 |
-
|
| 6274 |
-
|
| 6275 |
- borderRadius: 3,
|
|
|
|
|
|
|
| 6276 |
+ borderRadius: "var(--radius-xs)",
|
| 6277 |
padding: "0 4px",
|
| 6278 |
marginRight: 5,
|
|
@@ -6376,7 +6572,7 @@ index adfcc12..cb17981 100644
|
|
| 6376 |
assert.doesNotMatch(html, /type="file"|Attach files|Change tool preset/);
|
| 6377 |
});
|
| 6378 |
diff --git a/components/ChatInput.tsx b/components/ChatInput.tsx
|
| 6379 |
-
index 9a0a1b7..
|
| 6380 |
--- a/components/ChatInput.tsx
|
| 6381 |
+++ b/components/ChatInput.tsx
|
| 6382 |
@@ -35,6 +35,8 @@ import { useFontPreferences } from "@/hooks/useFontPreferences";
|
|
@@ -6853,10 +7049,10 @@ index 9a0a1b7..7dd9112 100644
|
|
| 6853 |
+ style={{
|
| 6854 |
+ display: "flex", alignItems: "center", gap: 5,
|
| 6855 |
+ padding: "7px 12px",
|
| 6856 |
-
+ background: canQueueStreamingMessage ? "
|
| 6857 |
+ border: "none",
|
| 6858 |
+ borderRadius: "var(--radius-sm)",
|
| 6859 |
-
+ color: canQueueStreamingMessage ? "
|
| 6860 |
+ cursor: canQueueStreamingMessage ? "pointer" : "not-allowed",
|
| 6861 |
+ fontSize: 13, fontWeight: 600, letterSpacing: "-0.01em",
|
| 6862 |
+ transition: "background 0.12s",
|
|
@@ -7678,7 +7874,7 @@ index 2c78471..12d53ec 100644
|
|
| 7678 |
|
| 7679 |
{minimapHovered && allNodes.length > 0 && (
|
| 7680 |
diff --git a/components/ChatWindow.tsx b/components/ChatWindow.tsx
|
| 7681 |
-
index 6c540a4..
|
| 7682 |
--- a/components/ChatWindow.tsx
|
| 7683 |
+++ b/components/ChatWindow.tsx
|
| 7684 |
@@ -1,19 +1,20 @@
|
|
@@ -7818,7 +8014,19 @@ index 6c540a4..900d76c 100644
|
|
| 7818 |
export function ChatWindow({ session, searchTarget, onSearchTargetHandled, initialScrollPosition, onScrollPositionChange, sessionRunning, newSessionCwd, newSessionDraftKey, newSessionContextBar, initialNewSessionChoices, onNewSessionChoicesChange, onAgentEnd, onToolEnd, onAttentionNeeded, onSessionCreated, onSessionForked, modelsRefreshKey, chatInputRef, onBranchDataChange, onSystemPromptChange, onSystemToolsChange, onSystemInfoLoaderChange, onSessionStatsChange, onSessionStatsPanelOpen, onOpenSettings, onContextUsageChange, onOpenFile, onFilesUploaded, onOpenSubagent, onAskInNewChat, quoteSelectionEnabled = false, initialPrompt, onInitialPromptConsumed, soundEnabled = true, onSoundToggle, playDoneSound = () => {}, unlockAudio, holdAudio }: Props) {
|
| 7819 |
const { t } = useI18n();
|
| 7820 |
const isMobile = useIsMobile();
|
| 7821 |
-
@@ -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7822 |
/>
|
| 7823 |
))}
|
| 7824 |
</div>
|
|
@@ -7966,6 +8174,17 @@ index 6c540a4..900d76c 100644
|
|
| 7966 |
<div className="new-session-brand-text">
|
| 7967 |
<span className="new-session-brand-name">Pi Web</span>
|
| 7968 |
<span className="new-session-meta">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7969 |
@@ -1648,11 +1643,7 @@ function ExtensionWaitingCount({ count }: { count: number }) {
|
| 7970 |
/** Corner brackets pointing outward; when expanded they point inward (restore). */
|
| 7971 |
function ExtensionSizeIcon({ expanded }: { expanded: boolean }) {
|
|
@@ -8295,7 +8514,7 @@ index 5bd7070..f6586f6 100644
|
|
| 8295 |
<span className="extension-widget-key">{widget.key}</span>
|
| 8296 |
</>
|
| 8297 |
diff --git a/components/FileExplorer.tsx b/components/FileExplorer.tsx
|
| 8298 |
-
index 94cf736..
|
| 8299 |
--- a/components/FileExplorer.tsx
|
| 8300 |
+++ b/components/FileExplorer.tsx
|
| 8301 |
@@ -18,6 +18,8 @@ import { buildSearchTree, type SearchTreeNode } from "@/lib/search-tree";
|
|
@@ -8307,6 +8526,15 @@ index 94cf736..77f0807 100644
|
|
| 8307 |
type Translate = ReturnType<typeof useI18n>["t"];
|
| 8308 |
|
| 8309 |
/** Why a listed entry is normally left out; only listings with `hidden=1` report it. */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8310 |
@@ -174,20 +176,13 @@ function GitStatusBadge({ status, t }: { status: GitFileStatus; t: Translate })
|
| 8311 |
|
| 8312 |
function MentionIcon({ size = 11 }: { size?: number }) {
|
|
@@ -8351,6 +8579,15 @@ index 94cf736..77f0807 100644
|
|
| 8351 |
)}
|
| 8352 |
{!node.isDir && <span style={{ width: 10, flexShrink: 0 }} />}
|
| 8353 |
<span style={{ flexShrink: 0, display: "flex", alignItems: "center", opacity: hidden ? HIDDEN_ENTRY_OPACITY : 1 }}>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8354 |
@@ -419,9 +408,7 @@ export function TreeNode({
|
| 8355 |
</span>
|
| 8356 |
)}
|
|
@@ -8621,10 +8858,18 @@ index b46d21a..8a8b640 100644
|
|
| 8621 |
|
| 8622 |
export function FolderIcon({ size = 14, open = false }: IconProps & { open?: boolean }) {
|
| 8623 |
diff --git a/components/FileViewer.tsx b/components/FileViewer.tsx
|
| 8624 |
-
index 9426211..
|
| 8625 |
--- a/components/FileViewer.tsx
|
| 8626 |
+++ b/components/FileViewer.tsx
|
| 8627 |
-
@@ -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8628 |
type FileViewerDisplayMode as DisplayMode,
|
| 8629 |
type FileViewerState,
|
| 8630 |
} from "@/lib/file-viewer-state";
|
|
@@ -8633,7 +8878,24 @@ index 9426211..5c5712b 100644
|
|
| 8633 |
|
| 8634 |
export type { FileViewerState } from "@/lib/file-viewer-state";
|
| 8635 |
|
| 8636 |
-
@@ -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8637 |
|
| 8638 |
function MentionIcon() {
|
| 8639 |
return (
|
|
@@ -8645,7 +8907,7 @@ index 9426211..5c5712b 100644
|
|
| 8645 |
);
|
| 8646 |
}
|
| 8647 |
|
| 8648 |
-
@@ -252,11 +
|
| 8649 |
aria-label={t("i18n.downloadFile")}
|
| 8650 |
className="file-viewer-icon-button"
|
| 8651 |
>
|
|
@@ -8658,7 +8920,16 @@ index 9426211..5c5712b 100644
|
|
| 8658 |
</a>
|
| 8659 |
);
|
| 8660 |
}
|
| 8661 |
-
@@ -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8662 |
color: wrapLines ? "var(--text)" : "var(--text-muted)",
|
| 8663 |
}}
|
| 8664 |
>
|
|
@@ -8672,7 +8943,7 @@ index 9426211..5c5712b 100644
|
|
| 8672 |
</button>
|
| 8673 |
</>
|
| 8674 |
)}
|
| 8675 |
-
@@ -1733,7 +
|
| 8676 |
gap: 10,
|
| 8677 |
padding: "5px 8px",
|
| 8678 |
border: "1px solid var(--border)",
|
|
@@ -8730,19 +9001,29 @@ index 9b81f64..22b3e6d 100644
|
|
| 8730 |
</dialog>
|
| 8731 |
)}
|
| 8732 |
diff --git a/components/MermaidBlock.tsx b/components/MermaidBlock.tsx
|
| 8733 |
-
index 25e4a63..
|
| 8734 |
--- a/components/MermaidBlock.tsx
|
| 8735 |
+++ b/components/MermaidBlock.tsx
|
| 8736 |
-
@@ -10,6 +10,
|
| 8737 |
import { copyText } from "@/lib/clipboard";
|
| 8738 |
import { takeHighlightBudget, waitForHighlight } from "@/lib/highlight-queue";
|
| 8739 |
import { prismLanguagesLoaded } from "@/lib/prism-languages";
|
|
|
|
| 8740 |
+import { CarbonIcon } from "./CarbonIcon";
|
| 8741 |
+import * as Carbon from "./carbon/icons";
|
| 8742 |
|
| 8743 |
interface MermaidBlockProps {
|
| 8744 |
code: string;
|
| 8745 |
-
@@ -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8746 |
title={t("i18n.zoomOut")}
|
| 8747 |
aria-label={t("i18n.zoomOut")}
|
| 8748 |
>
|
|
@@ -8753,7 +9034,7 @@ index 25e4a63..75bd5f2 100644
|
|
| 8753 |
</button>
|
| 8754 |
<span className="mermaid-zoom-value">{Math.round(zoom * 100)}%</span>
|
| 8755 |
<button
|
| 8756 |
-
@@ -206,9 +
|
| 8757 |
title={t("i18n.zoomIn")}
|
| 8758 |
aria-label={t("i18n.zoomIn")}
|
| 8759 |
>
|
|
@@ -8764,7 +9045,7 @@ index 25e4a63..75bd5f2 100644
|
|
| 8764 |
</button>
|
| 8765 |
</div>
|
| 8766 |
<button
|
| 8767 |
-
@@ -218,9 +
|
| 8768 |
title={t("i18n.fitToWidth")}
|
| 8769 |
aria-label={t("i18n.fitToWidth")}
|
| 8770 |
>
|
|
@@ -8775,7 +9056,7 @@ index 25e4a63..75bd5f2 100644
|
|
| 8775 |
</button>
|
| 8776 |
<button
|
| 8777 |
type="button"
|
| 8778 |
-
@@ -229,9 +
|
| 8779 |
title={t("i18n.close")}
|
| 8780 |
aria-label={t("i18n.close")}
|
| 8781 |
>
|
|
@@ -8786,6 +9067,32 @@ index 25e4a63..75bd5f2 100644
|
|
| 8786 |
</button>
|
| 8787 |
</div>
|
| 8788 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8789 |
diff --git a/components/MessageView.test.mjs b/components/MessageView.test.mjs
|
| 8790 |
index 515b033..eeb4c1e 100644
|
| 8791 |
--- a/components/MessageView.test.mjs
|
|
@@ -8812,7 +9119,7 @@ index 515b033..eeb4c1e 100644
|
|
| 8812 |
|
| 8813 |
test("collapses a displayed custom message from its header alone", () => {
|
| 8814 |
diff --git a/components/MessageView.tsx b/components/MessageView.tsx
|
| 8815 |
-
index f2ff1de..
|
| 8816 |
--- a/components/MessageView.tsx
|
| 8817 |
+++ b/components/MessageView.tsx
|
| 8818 |
@@ -1,10 +1,11 @@
|
|
@@ -8914,7 +9221,7 @@ index f2ff1de..de7e3d6 100644
|
|
| 8914 |
src={src}
|
| 8915 |
alt=""
|
| 8916 |
- style={{ maxWidth: 240, maxHeight: 240, borderRadius: 6, objectFit: "contain", display: "block", border: "1px solid rgba(59,130,246,0.15)" }}
|
| 8917 |
-
+ style={{ maxWidth: 240, maxHeight: 240, borderRadius: "var(--radius-sm)", objectFit: "contain", display: "block", border: "
|
| 8918 |
/>
|
| 8919 |
</ImagePreview>
|
| 8920 |
);
|
|
@@ -9895,6 +10202,30 @@ index f2ff1de..de7e3d6 100644
|
|
| 9895 |
const { t } = useI18n();
|
| 9896 |
const showFileHeaders = files.length > 1;
|
| 9897 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9898 |
@@ -1587,7 +1456,6 @@ function ResultImages({ images, isError }: { images: ImageContent[]; isError: bo
|
| 9899 |
flexWrap: "wrap",
|
| 9900 |
padding: "10px",
|
|
@@ -10106,7 +10437,7 @@ index ba912ee..db662d8 100644
|
|
| 10106 |
<SelectorRow active={!value} gutter={Boolean(onSetDefault)} onSelect={() => {
|
| 10107 |
setOpen(false);
|
| 10108 |
diff --git a/components/ModelsConfig.tsx b/components/ModelsConfig.tsx
|
| 10109 |
-
index 9f6b556..
|
| 10110 |
--- a/components/ModelsConfig.tsx
|
| 10111 |
+++ b/components/ModelsConfig.tsx
|
| 10112 |
@@ -54,6 +54,8 @@ import { providerBadgeLabel } from "./enabled-models-helpers";
|
|
@@ -10192,7 +10523,23 @@ index 9f6b556..7063172 100644
|
|
| 10192 |
>
|
| 10193 |
{selectedCount
|
| 10194 |
? t("models.discoveryAddSelectedCount", { count: selectedCount })
|
| 10195 |
-
@@ -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10196 |
alignItems: "center",
|
| 10197 |
gap: 8,
|
| 10198 |
padding: "5px 4px",
|
|
@@ -10201,7 +10548,7 @@ index 9f6b556..7063172 100644
|
|
| 10201 |
background: "transparent",
|
| 10202 |
border: "1px solid transparent",
|
| 10203 |
}}
|
| 10204 |
-
@@ -644,7 +
|
| 10205 |
</span>
|
| 10206 |
</div>
|
| 10207 |
|
|
@@ -10210,7 +10557,7 @@ index 9f6b556..7063172 100644
|
|
| 10210 |
<button
|
| 10211 |
onClick={() => setLevel(level, "omit")}
|
| 10212 |
style={{ ...btnBase, ...(state === "omit" ? btnActive : {}) }}
|
| 10213 |
-
@@ -659,7 +
|
| 10214 |
</button>
|
| 10215 |
</div>
|
| 10216 |
|
|
@@ -10219,7 +10566,7 @@ index 9f6b556..7063172 100644
|
|
| 10219 |
<button
|
| 10220 |
onClick={() => setLevel(level, strVal || level)}
|
| 10221 |
style={{ ...btnBase, ...(state === "string" ? btnActive : {}), borderRight: "1px solid var(--border)", flexShrink: 0 }}
|
| 10222 |
-
@@ -747,7 +
|
| 10223 |
padding: "6px 9px",
|
| 10224 |
background: "none",
|
| 10225 |
border: "1px solid rgba(239,68,68,0.3)",
|
|
@@ -10228,7 +10575,7 @@ index 9f6b556..7063172 100644
|
|
| 10228 |
color: "#ef4444",
|
| 10229 |
cursor: "pointer",
|
| 10230 |
fontSize: 11,
|
| 10231 |
-
@@ -768,7 +
|
| 10232 |
...current,
|
| 10233 |
{ id: nextRowIdRef.current++, name: "", value: "" },
|
| 10234 |
])}
|
|
@@ -10237,16 +10584,20 @@ index 9f6b556..7063172 100644
|
|
| 10237 |
+ Add header
|
| 10238 |
</button>
|
| 10239 |
</div>
|
| 10240 |
-
@@ -1051,
|
| 10241 |
height: 28,
|
| 10242 |
padding: "0 8px",
|
| 10243 |
border: `1px solid ${testState.phase === "error" ? "#fecaca" : testState.phase === "success" ? "#bbf7d0" : "var(--border)"}`,
|
| 10244 |
- borderRadius: 4,
|
|
|
|
|
|
|
| 10245 |
+ borderRadius: "var(--radius-xs)",
|
| 10246 |
-
|
| 10247 |
-
|
| 10248 |
fontSize: 11,
|
| 10249 |
-
|
|
|
|
|
|
|
| 10250 |
className={testState.phase === "success" ? "is-success" : undefined}
|
| 10251 |
>
|
| 10252 |
{testState.phase === "success" && (
|
|
@@ -10257,7 +10608,7 @@ index 9f6b556..7063172 100644
|
|
| 10257 |
)}
|
| 10258 |
{testState.phase === "testing" ? t("i18n.checking") : testState.phase === "success" ? t("common.ok") : t("i18n.test")}
|
| 10259 |
</ConfigButton>
|
| 10260 |
-
@@ -1096,7 +
|
| 10261 |
onClick={() => void handleCatalogFill()}
|
| 10262 |
disabled={!model.id.trim() || catalogState.phase === "loading"}
|
| 10263 |
style={{
|
|
@@ -10266,7 +10617,7 @@ index 9f6b556..7063172 100644
|
|
| 10266 |
background: "var(--bg-panel)",
|
| 10267 |
color: !model.id.trim() || catalogState.phase === "loading" ? "var(--text-dim)" : "var(--text-muted)",
|
| 10268 |
cursor: !model.id.trim() || catalogState.phase === "loading" ? "not-allowed" : "pointer",
|
| 10269 |
-
@@ -1227,20 +
|
| 10270 |
{advancedSummary}
|
| 10271 |
</span>
|
| 10272 |
</span>
|
|
@@ -10288,7 +10639,7 @@ index 9f6b556..7063172 100644
|
|
| 10288 |
</button>
|
| 10289 |
|
| 10290 |
{advancedOpen && (
|
| 10291 |
-
@@ -1492,7 +
|
| 10292 |
<button
|
| 10293 |
key={option.id}
|
| 10294 |
onClick={() => submitSelection(loginState.token, option.id)}
|
|
@@ -10297,7 +10648,7 @@ index 9f6b556..7063172 100644
|
|
| 10297 |
>
|
| 10298 |
{option.label}
|
| 10299 |
</button>
|
| 10300 |
-
@@ -1527,7 +
|
| 10301 |
<p style={{ margin: 0, fontSize: 12, color: "var(--text-muted)", lineHeight: 1.5 }}>
|
| 10302 |
Open the verification page and enter this code:
|
| 10303 |
</p>
|
|
@@ -10306,7 +10657,7 @@ index 9f6b556..7063172 100644
|
|
| 10306 |
{loginState.userCode}
|
| 10307 |
</div>
|
| 10308 |
<p style={{ margin: 0, fontSize: 11, color: "var(--text-dim)", lineHeight: 1.5 }}>
|
| 10309 |
-
@@ -1666,7 +
|
| 10310 |
style={{
|
| 10311 |
padding: "6px 12px",
|
| 10312 |
background: savedOk ? "#16a34a" : apiKey.trim() ? "var(--accent)" : "var(--bg-panel)",
|
|
@@ -10315,7 +10666,7 @@ index 9f6b556..7063172 100644
|
|
| 10315 |
color: savedOk ? "#fff" : apiKey.trim() ? "var(--accent-contrast)" : "var(--text-dim)",
|
| 10316 |
cursor: (saving || !apiKey.trim() || savedOk) ? "not-allowed" : "pointer",
|
| 10317 |
fontSize: 12, fontWeight: 600, flexShrink: 0,
|
| 10318 |
-
@@ -1674,9 +
|
| 10319 |
}}
|
| 10320 |
>
|
| 10321 |
{savedOk && (
|
|
@@ -10326,7 +10677,7 @@ index 9f6b556..7063172 100644
|
|
| 10326 |
)}
|
| 10327 |
{savedOk ? t("i18n.saved") : saving ? t("i18n.saving") : t("i18n.save")}
|
| 10328 |
</button>
|
| 10329 |
-
@@ -1725,7 +
|
| 10330 |
padding: "10px 12px",
|
| 10331 |
background: "var(--bg-panel)",
|
| 10332 |
border: "1px solid var(--border)",
|
|
@@ -10335,7 +10686,7 @@ index 9f6b556..7063172 100644
|
|
| 10335 |
boxSizing: "border-box",
|
| 10336 |
cursor: "pointer",
|
| 10337 |
minWidth: 0,
|
| 10338 |
-
@@ -1747,12 +
|
| 10339 |
onClose();
|
| 10340 |
}}
|
| 10341 |
>
|
|
@@ -10350,7 +10701,7 @@ index 9f6b556..7063172 100644
|
|
| 10350 |
<input
|
| 10351 |
ref={inputRef}
|
| 10352 |
value={search}
|
| 10353 |
-
@@ -1782,10 +
|
| 10354 |
<div style={{ fontSize: 12, fontWeight: 600, color: "var(--text)", lineHeight: 1.3, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>OpenAI / Anthropic compatible</div>
|
| 10355 |
<div style={{ fontSize: 10, color: "var(--text-dim)", marginTop: 2 }}>{t("i18n.customEndpoint")}</div>
|
| 10356 |
</div>
|
|
@@ -10363,7 +10714,7 @@ index 9f6b556..7063172 100644
|
|
| 10363 |
</span>
|
| 10364 |
</button>
|
| 10365 |
)}
|
| 10366 |
-
@@ -2177,13 +
|
| 10367 |
onClick={() => setSelection({ type: "provider", name: pName })}
|
| 10368 |
active={isProviderSelected}
|
| 10369 |
>
|
|
@@ -10378,16 +10729,16 @@ index 9f6b556..7063172 100644
|
|
| 10378 |
<ConfigSidebarText className="is-grow">
|
| 10379 |
{pName}
|
| 10380 |
</ConfigSidebarText>
|
| 10381 |
-
@@ -2204,7 +
|
| 10382 |
{m.id || t("i18n.newModel")}
|
| 10383 |
</ConfigSidebarText>
|
| 10384 |
{m.reasoning && (
|
| 10385 |
- <span style={{ fontSize: 9, padding: "1px 4px", background: "rgba(99,102,241,0.12)", color: "rgba(99,102,241,0.8)", borderRadius: 3, flexShrink: 0 }}>T</span>
|
| 10386 |
-
+ <span style={{ fontSize: 9, padding: "1px 4px", background: "
|
| 10387 |
)}
|
| 10388 |
</ConfigSidebarItem>
|
| 10389 |
);
|
| 10390 |
-
@@ -2250,10 +
|
| 10391 |
className={savedOk ? "is-success" : undefined}
|
| 10392 |
>
|
| 10393 |
{savedOk && (
|
|
@@ -10641,7 +10992,7 @@ index cf38be6..c08aaef 100644
|
|
| 10641 |
</button>
|
| 10642 |
{report && (
|
| 10643 |
diff --git a/components/SelectorRow.tsx b/components/SelectorRow.tsx
|
| 10644 |
-
index 555da8f..
|
| 10645 |
--- a/components/SelectorRow.tsx
|
| 10646 |
+++ b/components/SelectorRow.tsx
|
| 10647 |
@@ -2,12 +2,12 @@
|
|
@@ -10698,6 +11049,15 @@ index 555da8f..e64352a 100644
|
|
| 10698 |
cursor: "pointer",
|
| 10699 |
opacity: showSave ? 1 : 0,
|
| 10700 |
pointerEvents: showSave ? "auto" : "none",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10701 |
diff --git a/components/SessionSidebar.test.mjs b/components/SessionSidebar.test.mjs
|
| 10702 |
index 6bc923f..70b9e80 100644
|
| 10703 |
--- a/components/SessionSidebar.test.mjs
|
|
@@ -10730,7 +11090,7 @@ index 6bc923f..70b9e80 100644
|
|
| 10730 |
// search in the toolbar row's place.
|
| 10731 |
const head = html.slice(html.indexOf('<div class="sidebar-files-head">'), html.indexOf('<div class="sidebar-files-scroll'));
|
| 10732 |
diff --git a/components/SessionTree.test.mjs b/components/SessionTree.test.mjs
|
| 10733 |
-
index fb53e4b..
|
| 10734 |
--- a/components/SessionTree.test.mjs
|
| 10735 |
+++ b/components/SessionTree.test.mjs
|
| 10736 |
@@ -119,7 +119,7 @@ test("the pinned header toggles the section and shows activity dots only while c
|
|
@@ -10751,12 +11111,14 @@ index fb53e4b..f6596f1 100644
|
|
| 10751 |
assert.match(html, /<span class="session-tree-group-name">app<\/span><svg[^>]*class="session-tree-group-pin"[\s\S]*?<\/svg><span class="session-tree-summary">/);
|
| 10752 |
// No folder: a project group is no second file tree.
|
| 10753 |
assert.doesNotMatch(html, /session-tree-group-icon/);
|
| 10754 |
-
@@ -
|
|
|
|
| 10755 |
assert.doesNotMatch(css, /@starting-style|prefers-color-scheme/);
|
| 10756 |
const colors = new Set((css.match(/#[0-9a-fA-F]{3,8}\b/g) ?? []).map((color) => color.toLowerCase()));
|
| 10757 |
-
|
| 10758 |
- // Three text colors: content, labels, meta (plus the accent for states,
|
| 10759 |
- // the errors' red and the delete button's white). No mixes of their own.
|
|
|
|
| 10760 |
+ // Three text colors: content, labels, meta, the selected title a step
|
| 10761 |
+ // stronger (plus the accent for states, the errors' red and the delete
|
| 10762 |
+ // button's white). No mixes of their own.
|
|
@@ -11316,7 +11678,7 @@ index 23e0c79..2919906 100644
|
|
| 11316 |
</div>
|
| 11317 |
);
|
| 11318 |
diff --git a/components/TerminalPanel.tsx b/components/TerminalPanel.tsx
|
| 11319 |
-
index d24bb94..
|
| 11320 |
--- a/components/TerminalPanel.tsx
|
| 11321 |
+++ b/components/TerminalPanel.tsx
|
| 11322 |
@@ -18,6 +18,8 @@ import {
|
|
@@ -11328,7 +11690,25 @@ index d24bb94..28a4582 100644
|
|
| 11328 |
|
| 11329 |
interface Props {
|
| 11330 |
tab: TerminalTab;
|
| 11331 |
-
@@ -
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11332 |
</div>
|
| 11333 |
{status === "error" && (
|
| 11334 |
<button type="button" onClick={() => setReconnectKey((key) => key + 1)} disabled={Boolean(tab.closing)} title={t("terminal.reconnect")} aria-label={t("terminal.reconnect")}>
|
|
@@ -12061,6 +12441,123 @@ index d8b6d41..e9a45e1 100644
|
|
| 12061 |
"chat.filesWritten": "已變更的檔案",
|
| 12062 |
"chat.openWrittenFile": "開啟 {name}",
|
| 12063 |
"chat.loadEarlier": "向上捲動以載入較早的訊息",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12064 |
diff --git a/lib/step-summary.test.mjs b/lib/step-summary.test.mjs
|
| 12065 |
new file mode 100644
|
| 12066 |
index 0000000..6cbe005
|
|
|
|
| 1 |
diff --git a/AGENTS.md b/AGENTS.md
|
| 2 |
+
index a5c8983..c40477c 100644
|
| 3 |
--- a/AGENTS.md
|
| 4 |
+++ b/AGENTS.md
|
| 5 |
+
@@ -138,6 +138,8 @@ lib/
|
| 6 |
extension-ui-queue.ts FIFO queues for extension dialogs and custom panels, by request id; isBlockingExtensionUiRequest() (which method a run waits on)
|
| 7 |
markdown.ts shared markdown helpers
|
| 8 |
markdown-blocks.ts splitMarkdownBlocks(): where a streaming reply may be cut into blocks parsed apart
|
| 9 |
+ step-summary.ts a turn's steps counted by kind (ran, read, edited, …) for their one summary line above the answer
|
| 10 |
+
+ neutral-code-theme.ts the Prism themes (dark+, vs) with their blue tokens in neutral grays, keywords bold; Mermaid's neutral base theme
|
| 11 |
highlight-queue.ts code fences past a task's highlight budget get their colors in idle slices
|
| 12 |
prism-languages.ts loadPrismLanguages(): every Prism language in a chunk of its own, loaded when a highlighter first needs it
|
| 13 |
gfm-autolink-email-loader.cjs bundler loader: remark-gfm's email regex without a lookbehind literal
|
| 14 |
+
@@ -174,7 +176,8 @@ lib/
|
| 15 |
components/
|
| 16 |
AppShell.tsx layout, URL state, tab management
|
| 17 |
SessionSidebar.tsx toolbar row (Sessions | Files tabs, or the Pi Web brand with the files below them; new, search): tree, archive, menus, toast; files section (resizable, foldable; one header row with the file keys and changed-files count, the picker's row) or tab (picker boxes + file buttons), FileExplorer
|
|
|
|
| 21 |
SessionTree.tsx virtualized rows of the session tree and the archive view
|
| 22 |
SidebarMenu.tsx sidebar popup menu (bottom sheet on phones), portaled to body: filter field, item secondary actions, the files tab's classic look
|
| 23 |
ProjectWorktreePicker.tsx project and worktree boxes + menus: the files' (one row below the sessions, stacked in the tab) and the new-session bar's
|
| 24 |
+
@@ -274,3 +277,23 @@ Location: `~/.pi/agent/sessions/<encoded-cwd>/<timestamp>_<uuid>.jsonl`
|
| 25 |
--accent --user-bg --tool-bg
|
| 26 |
--font-mono
|
| 27 |
```
|
|
|
|
| 42 |
+`.ui-btn` classes in `app/globals.css` (`--sm`, `--xs`, `--lg`, `--icon`, `--primary`, …); the
|
| 43 |
+transcript's pieces (`.turn-user`, `.turn-reply`, `.steps-summary`, `.tool-call-row`, `.composer`)
|
| 44 |
+are styled there too.
|
| 45 |
+
+`--accent` is a neutral gray in light and dark (no blue anywhere: links are told apart by their
|
| 46 |
+
+underline, focus by a 2px ring), `--accent-soft` its tint; Mist, Rose and Pine keep their own hues.
|
| 47 |
+
+Code blocks and the file viewer use `lib/neutral-code-theme.ts`; the terminal keeps its ANSI hues.
|
| 48 |
+
diff --git a/app/api/files/[...path]/route.ts b/app/api/files/[...path]/route.ts
|
| 49 |
+
index d4d7df6..25f95bd 100644
|
| 50 |
+
--- a/app/api/files/[...path]/route.ts
|
| 51 |
+
+++ b/app/api/files/[...path]/route.ts
|
| 52 |
+
@@ -394,7 +394,7 @@ function wrapDocxPreviewHtml(bodyHtml: string, fileName: string): string {
|
| 53 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
| 54 |
+
<style>
|
| 55 |
+
:root { color-scheme: light; }
|
| 56 |
+
- html, body { margin: 0; min-height: 100%; background: #eef1f5; color: #171717; }
|
| 57 |
+
+ html, body { margin: 0; min-height: 100%; background: #f5f5f5; color: #171717; }
|
| 58 |
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; padding: 28px; }
|
| 59 |
+
main {
|
| 60 |
+
box-sizing: border-box;
|
| 61 |
+
@@ -403,23 +403,24 @@ function wrapDocxPreviewHtml(bodyHtml: string, fileName: string): string {
|
| 62 |
+
margin: 0 auto;
|
| 63 |
+
padding: 56px 64px;
|
| 64 |
+
background: #fff;
|
| 65 |
+
- box-shadow: 0 8px 28px rgba(15, 23, 42, 0.14);
|
| 66 |
+
+ box-shadow: 0 8px 28px rgba(0, 0, 0, 0.12);
|
| 67 |
+
}
|
| 68 |
+
.file-title {
|
| 69 |
+
margin: 0 0 28px;
|
| 70 |
+
padding-bottom: 10px;
|
| 71 |
+
- border-bottom: 1px solid #e5e7eb;
|
| 72 |
+
- color: #6b7280;
|
| 73 |
+
+ border-bottom: 1px solid #e5e5e5;
|
| 74 |
+
+ color: #666666;
|
| 75 |
+
font: 12px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
| 76 |
+
word-break: break-word;
|
| 77 |
+
}
|
| 78 |
+
- h1, h2, h3, h4, h5, h6 { line-height: 1.3; margin: 1.1em 0 0.45em; color: #111827; }
|
| 79 |
+
+ h1, h2, h3, h4, h5, h6 { line-height: 1.3; margin: 1.1em 0 0.45em; color: #0a0a0a; }
|
| 80 |
+
p { margin: 0.65em 0; line-height: 1.7; }
|
| 81 |
+
table { border-collapse: collapse; max-width: 100%; margin: 1em 0; }
|
| 82 |
+
- th, td { border: 1px solid #d1d5db; padding: 6px 9px; vertical-align: top; }
|
| 83 |
+
+ th, td { border: 1px solid #d4d4d4; padding: 6px 9px; vertical-align: top; }
|
| 84 |
+
img { max-width: 100%; height: auto; }
|
| 85 |
+
pre { white-space: pre-wrap; overflow-wrap: anywhere; }
|
| 86 |
+
- a { color: #2563eb; }
|
| 87 |
+
+ a { color: #404040; text-decoration: underline; text-underline-offset: 3px; }
|
| 88 |
+
+ a:hover { color: #0a0a0a; }
|
| 89 |
+
@media (max-width: 720px) {
|
| 90 |
+
body { padding: 0; background: #fff; }
|
| 91 |
+
main { min-height: 100vh; padding: 28px 22px; box-shadow: none; }
|
| 92 |
diff --git a/app/favicon.ico b/app/favicon.ico
|
| 93 |
index 2da6b7dc00c64c59ef23cff153a6051478ad9315..b407f41443e3e94f4204a1862227e0d457f7d823 100644
|
| 94 |
GIT binary patch
|
|
|
|
| 3128 |
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
| 3129 |
+OTHER DEALINGS IN THE FONT SOFTWARE.
|
| 3130 |
diff --git a/app/globals.css b/app/globals.css
|
| 3131 |
+
index d6b5535..46c0a53 100644
|
| 3132 |
--- a/app/globals.css
|
| 3133 |
+++ b/app/globals.css
|
| 3134 |
+
@@ -24,46 +24,109 @@
|
| 3135 |
--font-mono-font: var(--font-mono);
|
| 3136 |
}
|
| 3137 |
|
|
|
|
| 3158 |
+ --text: #2e2e2e;
|
| 3159 |
+ --text-muted: #525252;
|
| 3160 |
+ --text-dim: #666666;
|
| 3161 |
+
+ --accent: #404040;
|
| 3162 |
+
+ --accent-hover: #0a0a0a;
|
| 3163 |
--accent-contrast: #ffffff;
|
| 3164 |
- --user-bg: #eff6ff;
|
| 3165 |
+
+ --accent-soft: rgba(10, 10, 10, 0.07);
|
| 3166 |
+ --user-bg: #f5f5f5;
|
| 3167 |
--assistant-bg: #ffffff;
|
| 3168 |
- --tool-bg: #f9fafb;
|
|
|
|
| 3180 |
+ --danger-soft: rgba(193, 0, 7, 0.07);
|
| 3181 |
+ --success: #008236;
|
| 3182 |
+ --warning: #bb4d00;
|
| 3183 |
+
+ --focus-ring: #404040;
|
| 3184 |
+ --scrim: rgba(10, 10, 10, 0.32);
|
| 3185 |
+ --border: transparent;
|
| 3186 |
+ --shadow-pop: 0 10px 30px rgba(0, 0, 0, 0.12), 0 2px 6px rgba(0, 0, 0, 0.06);
|
|
|
|
| 3212 |
+ --text: #d4d4d4;
|
| 3213 |
+ --text-muted: #a3a3a3;
|
| 3214 |
+ --text-dim: #8a8a8a;
|
| 3215 |
+
+ --accent: #e5e5e5;
|
| 3216 |
+
+ --accent-hover: #ffffff;
|
| 3217 |
+ --accent-contrast: #0a0a0a;
|
| 3218 |
+
+ --accent-soft: rgba(255, 255, 255, 0.10);
|
| 3219 |
+ --user-bg: #1a1a1a;
|
| 3220 |
+ --assistant-bg: #0a0a0a;
|
| 3221 |
+ --tool-bg: #1a1a1a;
|
|
|
|
| 3229 |
+ --danger-soft: rgba(255, 100, 103, 0.10);
|
| 3230 |
+ --success: #05df72;
|
| 3231 |
+ --warning: #ffb900;
|
| 3232 |
+
+ --focus-ring: #e5e5e5;
|
| 3233 |
+ --scrim: rgba(0, 0, 0, 0.6);
|
| 3234 |
+ --border: transparent;
|
| 3235 |
+ --shadow-pop: 0 12px 36px rgba(0, 0, 0, 0.6), 0 2px 8px rgba(0, 0, 0, 0.4);
|
|
|
|
| 3269 |
}
|
| 3270 |
|
| 3271 |
[data-theme="mist"] {
|
| 3272 |
+
@@ -72,7 +135,7 @@ html.dark,
|
| 3273 |
--bg-panel: #e9f0ee;
|
| 3274 |
--bg-hover: #e0eae7;
|
| 3275 |
--bg-selected: #d7e4df;
|
|
|
|
| 3278 |
--text: #202e2b;
|
| 3279 |
--text-muted: #455f56;
|
| 3280 |
--text-dim: #52685f;
|
| 3281 |
+
@@ -83,6 +146,18 @@ html.dark,
|
| 3282 |
--assistant-bg: #f4f8f7;
|
| 3283 |
--tool-bg: #ecf3f0;
|
| 3284 |
--bg-subtle: rgba(24,65,50,0.04);
|
|
|
|
| 3297 |
}
|
| 3298 |
|
| 3299 |
[data-theme="rose"] {
|
| 3300 |
+
@@ -91,7 +166,7 @@ html.dark,
|
| 3301 |
--bg-panel: #f3edef;
|
| 3302 |
--bg-hover: #eee3e7;
|
| 3303 |
--bg-selected: #e8dce1;
|
|
|
|
| 3306 |
--text: #34282e;
|
| 3307 |
--text-muted: #65505a;
|
| 3308 |
--text-dim: #705b65;
|
| 3309 |
+
@@ -102,6 +177,18 @@ html.dark,
|
| 3310 |
--assistant-bg: #fcf7f8;
|
| 3311 |
--tool-bg: #f7eef2;
|
| 3312 |
--bg-subtle: rgba(89,39,62,0.04);
|
|
|
|
| 3325 |
}
|
| 3326 |
|
| 3327 |
html[data-theme="pine"],
|
| 3328 |
+
@@ -111,7 +198,7 @@ html[data-theme="pine"],
|
| 3329 |
--bg-panel: #212b28;
|
| 3330 |
--bg-hover: #2b3632;
|
| 3331 |
--bg-selected: #35433c;
|
|
|
|
| 3334 |
--text: #e6ede8;
|
| 3335 |
--text-muted: #c3d0c6;
|
| 3336 |
--text-dim: #afc2b5;
|
| 3337 |
+
@@ -122,6 +209,18 @@ html[data-theme="pine"],
|
| 3338 |
--assistant-bg: #19201f;
|
| 3339 |
--tool-bg: #222d29;
|
| 3340 |
--bg-subtle: rgba(210,236,219,0.04);
|
|
|
|
| 3353 |
}
|
| 3354 |
|
| 3355 |
/* WebKit reports dvh/innerHeight without the status-bar strip for installed
|
| 3356 |
+
@@ -135,28 +234,12 @@ html[data-theme="pine"],
|
| 3357 |
}
|
| 3358 |
}
|
| 3359 |
|
|
|
|
| 3386 |
}
|
| 3387 |
|
| 3388 |
/* Theme toggle: circular wipe via View Transitions API.
|
| 3389 |
+
@@ -218,9 +301,9 @@ body {
|
| 3390 |
margin-bottom: 22px;
|
| 3391 |
}
|
| 3392 |
|
|
|
|
| 3398 |
}
|
| 3399 |
|
| 3400 |
.web-login-brand h1 {
|
| 3401 |
+
@@ -247,15 +330,15 @@ body {
|
| 3402 |
min-width: 0;
|
| 3403 |
padding: 10px 10px 10px 14px;
|
| 3404 |
border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
|
| 3405 |
- border-radius: 14px;
|
| 3406 |
+ border-radius: var(--radius-xl);
|
| 3407 |
background: var(--bg);
|
| 3408 |
+
- box-shadow: 0 1px 2px rgba(15, 23, 42, 0.04), 0 8px 24px -12px rgba(15, 23, 42, 0.10);
|
| 3409 |
+
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 8px 24px -12px rgba(0, 0, 0, 0.10);
|
| 3410 |
transition: border-color 0.15s, box-shadow 0.15s;
|
| 3411 |
+
}
|
| 3412 |
+
|
| 3413 |
+
.web-login-composer:focus-within {
|
| 3414 |
+
border-color: var(--accent);
|
| 3415 |
+
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent), 0 8px 24px -12px rgba(15, 23, 42, 0.10);
|
| 3416 |
+
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 14%, transparent), 0 8px 24px -12px rgba(0, 0, 0, 0.10);
|
| 3417 |
+
}
|
| 3418 |
+
|
| 3419 |
+
.web-login-label {
|
| 3420 |
+
@@ -307,7 +390,7 @@ html.dark .web-login-error {
|
| 3421 |
min-height: 32px;
|
| 3422 |
padding: 7px 14px;
|
| 3423 |
border: none;
|
|
|
|
| 3426 |
background: var(--accent);
|
| 3427 |
color: var(--accent-contrast);
|
| 3428 |
cursor: pointer;
|
| 3429 |
+
@@ -339,7 +422,7 @@ html.dark .web-login-error {
|
| 3430 |
+
height: 100%;
|
| 3431 |
+
min-width: 0;
|
| 3432 |
+
min-height: 0;
|
| 3433 |
+
- background: #111318;
|
| 3434 |
+
+ background: #111111;
|
| 3435 |
+
}
|
| 3436 |
+
|
| 3437 |
+
.terminal-panel-header {
|
| 3438 |
+
@@ -350,7 +433,7 @@ html.dark .web-login-error {
|
| 3439 |
+
padding: 0 10px 0 13px;
|
| 3440 |
+
border-bottom: 1px solid #2f3540;
|
| 3441 |
+
background: #181b21;
|
| 3442 |
+
- color: #9ca3af;
|
| 3443 |
+
+ color: #a3a3a3;
|
| 3444 |
+
font-family: var(--font-mono);
|
| 3445 |
+
font-size: 11px;
|
| 3446 |
+
}
|
| 3447 |
+
@@ -388,23 +471,23 @@ html.dark .web-login-error {
|
| 3448 |
flex-shrink: 0;
|
| 3449 |
padding: 0 8px;
|
| 3450 |
border: 1px solid #343a46;
|
| 3451 |
- border-radius: 5px;
|
| 3452 |
+ border-radius: var(--radius-sm);
|
| 3453 |
background: transparent;
|
| 3454 |
+
- color: #9ca3af;
|
| 3455 |
+
+ color: #a3a3a3;
|
| 3456 |
cursor: pointer;
|
| 3457 |
+
font: inherit;
|
| 3458 |
+
}
|
| 3459 |
+
|
| 3460 |
+
.terminal-panel-exit {
|
| 3461 |
+
padding: 7px 12px;
|
| 3462 |
+
- color: #9ca3af;
|
| 3463 |
+
+ color: #a3a3a3;
|
| 3464 |
+
font-family: var(--font-mono);
|
| 3465 |
+
font-size: 11px;
|
| 3466 |
+
}
|
| 3467 |
+
|
| 3468 |
+
.terminal-panel-header button:hover {
|
| 3469 |
+
background: #242932;
|
| 3470 |
+
- color: #e5e7eb;
|
| 3471 |
+
+ color: #e5e5e5;
|
| 3472 |
+
}
|
| 3473 |
+
|
| 3474 |
+
.terminal-panel-error {
|
| 3475 |
+
@@ -451,7 +534,7 @@ html[data-keyboard-open] .terminal-extra-keys {
|
| 3476 |
+
padding: 0;
|
| 3477 |
+
border: none;
|
| 3478 |
+
background: transparent;
|
| 3479 |
+
- color: #d7dce5;
|
| 3480 |
+
+ color: #d4d4d4;
|
| 3481 |
+
font-family: var(--font-mono);
|
| 3482 |
+
font-size: 12px;
|
| 3483 |
+
touch-action: manipulation;
|
| 3484 |
+
@@ -471,7 +554,7 @@ html[data-keyboard-open] .terminal-extra-keys {
|
| 3485 |
+
}
|
| 3486 |
+
|
| 3487 |
+
.terminal-extra-keys button.is-active {
|
| 3488 |
+
- color: #60a5fa;
|
| 3489 |
+
+ color: #e5e5e5;
|
| 3490 |
+
}
|
| 3491 |
+
|
| 3492 |
+
.terminal-xterm-host {
|
| 3493 |
+
@@ -480,7 +563,7 @@ html[data-keyboard-open] .terminal-extra-keys {
|
| 3494 |
+
}
|
| 3495 |
+
|
| 3496 |
+
.terminal-xterm .xterm-viewport {
|
| 3497 |
+
- background-color: #111318;
|
| 3498 |
+
+ background-color: #111111;
|
| 3499 |
+
}
|
| 3500 |
+
|
| 3501 |
+
/*
|
| 3502 |
+
@@ -502,7 +585,7 @@ html[data-keyboard-open] .terminal-extra-keys {
|
| 3503 |
background-color: color-mix(in srgb, var(--text-dim) 45%, transparent);
|
| 3504 |
background-clip: padding-box;
|
| 3505 |
border: 2px solid transparent;
|
|
|
|
| 3508 |
}
|
| 3509 |
/* Only ever set background-color here: the `background` shorthand resets
|
| 3510 |
background-clip and would snap the thumb to the full 10px on hover. */
|
| 3511 |
+
@@ -538,7 +621,7 @@ html[data-keyboard-open] .terminal-extra-keys {
|
| 3512 |
}
|
| 3513 |
::-webkit-scrollbar-thumb {
|
| 3514 |
border-width: 1px;
|
|
|
|
| 3517 |
}
|
| 3518 |
}
|
| 3519 |
|
| 3520 |
+
@@ -703,9 +786,9 @@ button.extension-widget-trigger:focus-visible {
|
| 3521 |
|
| 3522 |
.extension-widget-placement-icon {
|
| 3523 |
display: block;
|
|
|
|
| 3530 |
fill: currentColor;
|
| 3531 |
}
|
| 3532 |
|
| 3533 |
+
@@ -826,8 +909,8 @@ button.extension-widget-trigger:focus-visible {
|
| 3534 |
}
|
| 3535 |
|
| 3536 |
:root {
|
|
|
|
| 3541 |
--font-ui: var(--font-ui-default);
|
| 3542 |
--font-mono: var(--font-mono-default);
|
| 3543 |
--font-ui-weight: 400;
|
| 3544 |
+
@@ -883,11 +966,11 @@ pre, code {
|
| 3545 |
.markdown-body ol > li { list-style-type: decimal; }
|
| 3546 |
.markdown-body li > p { margin: 0; }
|
| 3547 |
.markdown-body li::marker {
|
| 3548 |
- color: color-mix(in srgb, var(--accent) 72%, var(--text-muted));
|
| 3549 |
- font-weight: 600;
|
| 3550 |
+
+ color: var(--text-muted);
|
| 3551 |
+ font-weight: 500;
|
| 3552 |
}
|
| 3553 |
.markdown-body strong {
|
|
|
|
| 3556 |
font-weight: 700;
|
| 3557 |
}
|
| 3558 |
.markdown-body em { color: var(--text-muted); }
|
| 3559 |
+
@@ -896,24 +979,23 @@ pre, code {
|
| 3560 |
+
text-decoration: underline;
|
| 3561 |
+
text-decoration-thickness: 1px;
|
| 3562 |
+
text-underline-offset: 3px;
|
| 3563 |
+
- text-decoration-color: color-mix(in srgb, var(--accent) 45%, transparent);
|
| 3564 |
+
+ text-decoration-color: color-mix(in srgb, var(--accent) 50%, transparent);
|
| 3565 |
+
}
|
| 3566 |
+
-.markdown-body a:hover { text-decoration-color: var(--accent); }
|
| 3567 |
+
+.markdown-body a:hover { color: var(--accent-hover); text-decoration-color: var(--accent-hover); }
|
| 3568 |
+
.markdown-body img {
|
| 3569 |
+
display: block;
|
| 3570 |
max-width: 100%;
|
| 3571 |
height: auto;
|
| 3572 |
margin: 8px 0;
|
|
|
|
| 3585 |
}
|
| 3586 |
.markdown-body .contains-task-list {
|
| 3587 |
padding-left: 0;
|
| 3588 |
+
@@ -935,7 +1017,7 @@ pre, code {
|
| 3589 |
height: 14px;
|
| 3590 |
margin: 0;
|
| 3591 |
border: 1px solid var(--border);
|
|
|
|
| 3594 |
background: var(--bg);
|
| 3595 |
display: inline-grid;
|
| 3596 |
place-content: center;
|
| 3597 |
+
@@ -967,8 +1049,8 @@ pre, code {
|
| 3598 |
margin: 8px 0;
|
| 3599 |
overflow-x: auto;
|
| 3600 |
overflow-y: hidden;
|
|
|
|
| 3605 |
}
|
| 3606 |
.markdown-body table {
|
| 3607 |
border-collapse: separate;
|
| 3608 |
+
@@ -1088,7 +1170,7 @@ pre, code {
|
| 3609 |
padding: 7px 8px;
|
| 3610 |
overflow: auto;
|
| 3611 |
border: 1px solid var(--border);
|
|
|
|
| 3614 |
background: var(--bg-panel);
|
| 3615 |
font-family: var(--font-mono);
|
| 3616 |
font-size: 11px;
|
| 3617 |
+
@@ -1108,16 +1190,13 @@ pre, code {
|
| 3618 |
min-width: 0;
|
| 3619 |
max-width: 100%;
|
| 3620 |
margin: 6px 0;
|
|
|
|
| 3634 |
color: var(--text-dim);
|
| 3635 |
font-size: 11px;
|
| 3636 |
display: flex;
|
| 3637 |
+
@@ -1137,7 +1216,7 @@ pre, code {
|
| 3638 |
}
|
| 3639 |
.markdown-code-action {
|
| 3640 |
border: 1px solid var(--border);
|
|
|
|
| 3643 |
padding: 2px 7px;
|
| 3644 |
background: var(--bg);
|
| 3645 |
color: var(--text-muted);
|
| 3646 |
+
@@ -1228,7 +1307,7 @@ pre, code {
|
| 3647 |
max-width: 100%;
|
| 3648 |
max-height: 100%;
|
| 3649 |
object-fit: contain;
|
|
|
|
| 3652 |
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
|
| 3653 |
}
|
| 3654 |
|
| 3655 |
+
@@ -1244,7 +1323,7 @@ pre, code {
|
| 3656 |
height: 36px;
|
| 3657 |
padding: 0;
|
| 3658 |
border: 1px solid var(--border);
|
|
|
|
| 3661 |
background: var(--bg-panel);
|
| 3662 |
color: var(--text-muted);
|
| 3663 |
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.28);
|
| 3664 |
+
@@ -1320,7 +1399,7 @@ pre, code {
|
| 3665 |
height: 24px;
|
| 3666 |
overflow: hidden;
|
| 3667 |
border: 1px solid var(--border);
|
|
|
|
| 3670 |
background: var(--bg);
|
| 3671 |
}
|
| 3672 |
.mermaid-zoom-stepper button,
|
| 3673 |
+
@@ -1347,7 +1426,7 @@ pre, code {
|
| 3674 |
.mermaid-zoom-icon-button {
|
| 3675 |
flex: 0 0 24px;
|
| 3676 |
border: 1px solid var(--border);
|
|
|
|
| 3679 |
}
|
| 3680 |
.mermaid-zoom-stepper button:hover:not(:disabled),
|
| 3681 |
.mermaid-zoom-icon-button:hover {
|
| 3682 |
+
@@ -1384,7 +1463,7 @@ pre, code {
|
| 3683 |
.mermaid-zoom-canvas {
|
| 3684 |
padding: 12px;
|
| 3685 |
border: 1px solid var(--border);
|
|
|
|
| 3688 |
background: var(--bg-panel);
|
| 3689 |
margin: 0 auto;
|
| 3690 |
transition: width 0.15s ease;
|
| 3691 |
+
@@ -1460,7 +1539,7 @@ pre, code {
|
| 3692 |
height: 24px;
|
| 3693 |
overflow: hidden;
|
| 3694 |
border: 1px solid var(--border);
|
|
|
|
| 3697 |
}
|
| 3698 |
|
| 3699 |
.file-viewer-mode-button {
|
| 3700 |
+
@@ -1503,12 +1582,11 @@ pre, code {
|
| 3701 |
height: 32px;
|
| 3702 |
padding: 0;
|
| 3703 |
box-sizing: border-box;
|
|
|
|
| 3712 |
opacity: 0;
|
| 3713 |
visibility: hidden;
|
| 3714 |
pointer-events: none;
|
| 3715 |
+
@@ -1584,22 +1662,21 @@ pre, code {
|
| 3716 |
|
| 3717 |
.new-session-logo {
|
| 3718 |
flex: none;
|
|
|
|
| 3741 |
line-height: 1.15;
|
| 3742 |
white-space: nowrap;
|
| 3743 |
}
|
| 3744 |
+
@@ -1613,7 +1690,8 @@ pre, code {
|
| 3745 |
gap: 6px;
|
| 3746 |
min-width: 0;
|
| 3747 |
color: var(--text-dim);
|
|
|
|
| 3751 |
line-height: 1.4;
|
| 3752 |
white-space: nowrap;
|
| 3753 |
}
|
| 3754 |
+
@@ -1638,7 +1716,7 @@ pre, code {
|
| 3755 |
gap: 3px;
|
| 3756 |
margin: -3px -4px;
|
| 3757 |
padding: 3px 4px;
|
|
|
|
| 3760 |
color: var(--accent);
|
| 3761 |
font-weight: 600;
|
| 3762 |
text-decoration: none;
|
| 3763 |
+
@@ -1652,7 +1730,7 @@ pre, code {
|
| 3764 |
|
| 3765 |
/* The ring inside the box, clear of the "→" and "·" beside it. */
|
| 3766 |
.new-session-update:focus-visible {
|
|
|
|
| 3769 |
outline-offset: -2px;
|
| 3770 |
}
|
| 3771 |
|
| 3772 |
+
@@ -1678,12 +1756,12 @@ pre, code {
|
| 3773 |
}
|
| 3774 |
|
| 3775 |
.new-session-logo {
|
|
|
|
| 3785 |
}
|
| 3786 |
}
|
| 3787 |
|
| 3788 |
+
@@ -1722,14 +1800,14 @@ pre, code {
|
| 3789 |
min-width: 0;
|
| 3790 |
height: 29px;
|
| 3791 |
padding: 0 10px;
|
|
|
|
| 3803 |
}
|
| 3804 |
|
| 3805 |
/* Above a fresh composer (inline): no box until hovered or open, 12px, the
|
| 3806 |
+
@@ -1752,7 +1830,7 @@ pre, code {
|
| 3807 |
height: 26px;
|
| 3808 |
padding: 0 6px;
|
| 3809 |
border: 0;
|
|
|
|
| 3812 |
background: transparent;
|
| 3813 |
}
|
| 3814 |
|
| 3815 |
+
@@ -1781,8 +1859,9 @@ pre, code {
|
| 3816 |
max-width: 28ch;
|
| 3817 |
overflow: hidden;
|
| 3818 |
color: var(--text-dim);
|
|
|
|
| 3823 |
}
|
| 3824 |
|
| 3825 |
.project-picker-parent {
|
| 3826 |
+
@@ -1851,7 +1930,8 @@ pre, code {
|
| 3827 |
path's color and code type, cut at its end. */
|
| 3828 |
.project-picker-label.is-alias {
|
| 3829 |
color: var(--text);
|
|
|
|
| 3833 |
}
|
| 3834 |
|
| 3835 |
/* A path or a branch, cut at its left (rtl), the text itself kept left to
|
| 3836 |
+
@@ -1901,7 +1981,8 @@ pre, code {
|
| 3837 |
border-color: color-mix(in srgb, var(--accent) 40%, transparent);
|
| 3838 |
background: color-mix(in srgb, var(--accent) 6%, transparent);
|
| 3839 |
color: var(--text-dim);
|
|
|
|
| 3843 |
}
|
| 3844 |
|
| 3845 |
/* No worktree list here (the files tab): a box that says why, and does nothing. */
|
| 3846 |
+
@@ -1978,7 +2059,7 @@ pre, code {
|
| 3847 |
box-sizing: border-box;
|
| 3848 |
flex: 0 0 24px;
|
| 3849 |
border: 1px solid var(--border);
|
|
|
|
| 3852 |
background: transparent;
|
| 3853 |
color: var(--text-muted);
|
| 3854 |
cursor: pointer;
|
| 3855 |
+
@@ -2037,7 +2118,7 @@ pre, code {
|
| 3856 |
.markdown-file-preview pre {
|
| 3857 |
background: var(--bg-panel);
|
| 3858 |
border: 1px solid var(--border);
|
|
|
|
| 3861 |
padding: 12px 16px;
|
| 3862 |
overflow-x: auto;
|
| 3863 |
font-family: var(--font-mono);
|
| 3864 |
+
@@ -2049,7 +2130,7 @@ pre, code {
|
| 3865 |
font-size: 0.9em;
|
| 3866 |
background: var(--bg-panel);
|
| 3867 |
padding: 1px 5px;
|
|
|
|
| 3870 |
}
|
| 3871 |
.markdown-file-preview pre code {
|
| 3872 |
background: none;
|
| 3873 |
+
@@ -2061,7 +2142,7 @@ pre, code {
|
| 3874 |
margin-bottom: 20px;
|
| 3875 |
padding: 14px 16px 12px;
|
| 3876 |
border: 1px solid var(--border);
|
|
|
|
| 3879 |
background: var(--bg-panel);
|
| 3880 |
}
|
| 3881 |
.markdown-frontmatter-title {
|
| 3882 |
+
@@ -2474,3 +2555,428 @@ span.token.table {
|
| 3883 |
display: none !important;
|
| 3884 |
}
|
| 3885 |
}
|
|
|
|
| 4426 |
lang: "en",
|
| 4427 |
icons: [
|
| 4428 |
diff --git a/app/settings.css b/app/settings.css
|
| 4429 |
+
index 0bf21eb..e8788d8 100644
|
| 4430 |
--- a/app/settings.css
|
| 4431 |
+++ b/app/settings.css
|
| 4432 |
@@ -40,9 +40,9 @@
|
|
|
|
| 4558 |
background: var(--bg-panel);
|
| 4559 |
overflow-y: auto;
|
| 4560 |
}
|
| 4561 |
+
@@ -457,9 +458,9 @@
|
| 4562 |
.enabled-models-pin {
|
| 4563 |
flex-shrink: 0;
|
| 4564 |
padding: 1px 4px;
|
| 4565 |
- border-radius: 3px;
|
| 4566 |
+
- background: rgba(99, 102, 241, 0.12);
|
| 4567 |
+
- color: rgba(99, 102, 241, 0.8);
|
| 4568 |
+ border-radius: var(--radius-xs);
|
| 4569 |
+
+ background: var(--bg-subtle);
|
| 4570 |
+
+ color: var(--text-muted);
|
| 4571 |
font-size: 9px;
|
| 4572 |
+
}
|
| 4573 |
+
|
| 4574 |
@@ -497,7 +498,7 @@
|
| 4575 |
justify-content: space-between;
|
| 4576 |
gap: 18px;
|
|
|
|
| 4607 |
}
|
| 4608 |
|
| 4609 |
.config-field-label {
|
| 4610 |
+
@@ -697,15 +698,15 @@
|
| 4611 |
.config-scope-tag {
|
| 4612 |
flex-shrink: 0;
|
| 4613 |
padding: 1px 5px;
|
|
|
|
| 4616 |
background: rgba(120, 120, 120, 0.12);
|
| 4617 |
color: var(--text-dim);
|
| 4618 |
font-size: 10px;
|
| 4619 |
+
}
|
| 4620 |
+
|
| 4621 |
+
.config-scope-tag.is-project {
|
| 4622 |
+
- background: rgba(99, 102, 241, 0.12);
|
| 4623 |
+
- color: rgba(99, 102, 241, 0.8);
|
| 4624 |
+
+ background: var(--bg-subtle);
|
| 4625 |
+
+ color: var(--text-muted);
|
| 4626 |
+
}
|
| 4627 |
+
|
| 4628 |
+
.config-scope-switch-field {
|
| 4629 |
@@ -730,7 +731,7 @@
|
| 4630 |
height: 30px;
|
| 4631 |
overflow: hidden;
|
|
|
|
| 5267 |
color: var(--text-muted);
|
| 5268 |
font: inherit;
|
| 5269 |
diff --git a/app/sidebar.css b/app/sidebar.css
|
| 5270 |
+
index 86728a2..1d59f26 100644
|
| 5271 |
--- a/app/sidebar.css
|
| 5272 |
+++ b/app/sidebar.css
|
| 5273 |
@@ -91,7 +91,7 @@
|
|
|
|
| 5279 |
outline-offset: -2px;
|
| 5280 |
}
|
| 5281 |
|
| 5282 |
+
@@ -171,7 +171,7 @@
|
| 5283 |
+
}
|
| 5284 |
+
|
| 5285 |
+
.session-tree-pinned-dot.is-unread {
|
| 5286 |
+
- background: #0891b2;
|
| 5287 |
+
+ background: var(--text-strong);
|
| 5288 |
+
}
|
| 5289 |
+
|
| 5290 |
+
/* Session row: title, branch, time; actions take the time's place on
|
| 5291 |
@@ -184,7 +184,7 @@
|
| 5292 |
right: var(--session-tree-inset-right);
|
| 5293 |
left: var(--session-tree-inset-left);
|
|
|
|
| 5333 |
cursor: default;
|
| 5334 |
}
|
| 5335 |
|
| 5336 |
+
@@ -255,14 +234,14 @@ a.session-tree-main {
|
| 5337 |
+
width: 6px;
|
| 5338 |
+
height: 6px;
|
| 5339 |
+
border-radius: 50%;
|
| 5340 |
+
- background: #0891b2;
|
| 5341 |
+
+ background: var(--text-strong);
|
| 5342 |
+
}
|
| 5343 |
+
|
| 5344 |
+
.session-tree-unread::after {
|
| 5345 |
+
content: "";
|
| 5346 |
+
position: absolute;
|
| 5347 |
+
inset: -3px;
|
| 5348 |
+
- border: 1.4px solid #0891b2;
|
| 5349 |
+
+ border: 1.4px solid var(--text-strong);
|
| 5350 |
+
border-radius: 50%;
|
| 5351 |
+
opacity: 0.35;
|
| 5352 |
+
animation: session-tree-status-pulse 1.6s ease-in-out infinite;
|
| 5353 |
@@ -297,20 +276,23 @@ a.session-tree-main {
|
| 5354 |
}
|
| 5355 |
}
|
|
|
|
| 5471 |
text-align: left;
|
| 5472 |
white-space: nowrap;
|
| 5473 |
}
|
| 5474 |
+
@@ -669,7 +657,7 @@ a.session-tree-main {
|
| 5475 |
+
width: 6px;
|
| 5476 |
+
height: 6px;
|
| 5477 |
+
border-radius: 50%;
|
| 5478 |
+
- background: #0891b2;
|
| 5479 |
+
+ background: var(--text-strong);
|
| 5480 |
+
}
|
| 5481 |
+
|
| 5482 |
+
.session-tree-group-actions {
|
| 5483 |
@@ -726,7 +714,7 @@ a.session-tree-main {
|
| 5484 |
width: 22px;
|
| 5485 |
height: 22px;
|
|
|
|
| 5829 |
<span>{savedOk ? t("i18n.saved") : saving ? t("agents.saving") : t("agents.save")}</span>
|
| 5830 |
</ConfigButton>
|
| 5831 |
diff --git a/components/AppShell.tsx b/components/AppShell.tsx
|
| 5832 |
+
index c8c4ba9..0d90e2b 100644
|
| 5833 |
--- a/components/AppShell.tsx
|
| 5834 |
+++ b/components/AppShell.tsx
|
| 5835 |
@@ -3,6 +3,8 @@
|
|
|
|
| 6330 |
</button>
|
| 6331 |
);
|
| 6332 |
};
|
| 6333 |
+
@@ -2033,7 +1873,7 @@ export function AppShell() {
|
| 6334 |
+
transform: translateY(0);
|
| 6335 |
+
filter: blur(0);
|
| 6336 |
+
background: color-mix(in srgb, var(--accent) 8%, var(--bg-panel));
|
| 6337 |
+
- box-shadow: 0 18px 44px rgba(37,99,235,0.16);
|
| 6338 |
+
+ box-shadow: 0 18px 44px rgba(0,0,0,0.16);
|
| 6339 |
+
}
|
| 6340 |
+
100% {
|
| 6341 |
+
opacity: 1;
|
| 6342 |
@@ -2178,13 +2018,9 @@ export function AppShell() {
|
| 6343 |
onMouseLeave={(e) => { e.currentTarget.style.color = "var(--text-muted)"; }}
|
| 6344 |
>
|
|
|
|
| 6448 |
</div>
|
| 6449 |
|
| 6450 |
diff --git a/components/BranchNavigator.tsx b/components/BranchNavigator.tsx
|
| 6451 |
+
index 0f8186b..d31883a 100644
|
| 6452 |
--- a/components/BranchNavigator.tsx
|
| 6453 |
+++ b/components/BranchNavigator.tsx
|
| 6454 |
@@ -3,6 +3,8 @@
|
|
|
|
| 6460 |
|
| 6461 |
interface Props {
|
| 6462 |
tree: SessionTreeNode[];
|
| 6463 |
+
@@ -207,9 +209,9 @@ function TreeNodeView({ node, activePathIds, depth, isLast, parentLines, onSelec
|
| 6464 |
+
fontSize: 9,
|
| 6465 |
+
fontFamily: "var(--font-mono)",
|
| 6466 |
color: role === "user" ? "var(--accent)" : "var(--text-dim)",
|
| 6467 |
+
- background: role === "user" ? "rgba(37,99,235,0.08)" : "var(--bg-hover)",
|
| 6468 |
+
- border: `1px solid ${role === "user" ? "rgba(37,99,235,0.2)" : "var(--border)"}`,
|
| 6469 |
- borderRadius: 3,
|
| 6470 |
+
+ background: role === "user" ? "var(--accent-soft)" : "var(--bg-hover)",
|
| 6471 |
+
+ border: "1px solid var(--border)",
|
| 6472 |
+ borderRadius: "var(--radius-xs)",
|
| 6473 |
padding: "0 4px",
|
| 6474 |
marginRight: 5,
|
|
|
|
| 6572 |
assert.doesNotMatch(html, /type="file"|Attach files|Change tool preset/);
|
| 6573 |
});
|
| 6574 |
diff --git a/components/ChatInput.tsx b/components/ChatInput.tsx
|
| 6575 |
+
index 9a0a1b7..80465c6 100644
|
| 6576 |
--- a/components/ChatInput.tsx
|
| 6577 |
+++ b/components/ChatInput.tsx
|
| 6578 |
@@ -35,6 +35,8 @@ import { useFontPreferences } from "@/hooks/useFontPreferences";
|
|
|
|
| 7049 |
+ style={{
|
| 7050 |
+ display: "flex", alignItems: "center", gap: 5,
|
| 7051 |
+ padding: "7px 12px",
|
| 7052 |
+
+ background: canQueueStreamingMessage ? "var(--accent-soft)" : "var(--bg-subtle)",
|
| 7053 |
+ border: "none",
|
| 7054 |
+ borderRadius: "var(--radius-sm)",
|
| 7055 |
+
+ color: canQueueStreamingMessage ? "var(--text-strong)" : "var(--text-dim)",
|
| 7056 |
+ cursor: canQueueStreamingMessage ? "pointer" : "not-allowed",
|
| 7057 |
+ fontSize: 13, fontWeight: 600, letterSpacing: "-0.01em",
|
| 7058 |
+ transition: "background 0.12s",
|
|
|
|
| 7874 |
|
| 7875 |
{minimapHovered && allNodes.length > 0 && (
|
| 7876 |
diff --git a/components/ChatWindow.tsx b/components/ChatWindow.tsx
|
| 7877 |
+
index 6c540a4..57ffd84 100644
|
| 7878 |
--- a/components/ChatWindow.tsx
|
| 7879 |
+++ b/components/ChatWindow.tsx
|
| 7880 |
@@ -1,19 +1,20 @@
|
|
|
|
| 8014 |
export function ChatWindow({ session, searchTarget, onSearchTargetHandled, initialScrollPosition, onScrollPositionChange, sessionRunning, newSessionCwd, newSessionDraftKey, newSessionContextBar, initialNewSessionChoices, onNewSessionChoicesChange, onAgentEnd, onToolEnd, onAttentionNeeded, onSessionCreated, onSessionForked, modelsRefreshKey, chatInputRef, onBranchDataChange, onSystemPromptChange, onSystemToolsChange, onSystemInfoLoaderChange, onSessionStatsChange, onSessionStatsPanelOpen, onOpenSettings, onContextUsageChange, onOpenFile, onFilesUploaded, onOpenSubagent, onAskInNewChat, quoteSelectionEnabled = false, initialPrompt, onInitialPromptConsumed, soundEnabled = true, onSoundToggle, playDoneSound = () => {}, unlockAudio, holdAudio }: Props) {
|
| 8015 |
const { t } = useI18n();
|
| 8016 |
const isMobile = useIsMobile();
|
| 8017 |
+
@@ -1014,34 +1025,17 @@ export function ChatWindow({ session, searchTarget, onSearchTargetHandled, initi
|
| 8018 |
+
onDrop={handleDrop}
|
| 8019 |
+
>
|
| 8020 |
+
{isDragOver && (
|
| 8021 |
+
- <div className="pointer-events-none absolute inset-0 z-50 flex animate-[drop-zone-in_0.15s_ease_both] items-center justify-center bg-[rgba(37,99,235,0.06)] backdrop-blur-[1px]">
|
| 8022 |
+
+ <div className="pointer-events-none absolute inset-0 z-50 flex animate-[drop-zone-in_0.15s_ease_both] items-center justify-center bg-[var(--accent-soft)] backdrop-blur-[1px]">
|
| 8023 |
+
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
|
| 8024 |
+
{[0, 0.8, 1.6].map((delay) => (
|
| 8025 |
+
<div
|
| 8026 |
+
key={delay}
|
| 8027 |
+
- className="absolute h-[720px] w-[720px] rounded-full border-[1.5px] border-solid border-[rgba(37,99,235,0.5)] animate-[drop-ripple_2.4s_ease-out_infinite_backwards]"
|
| 8028 |
+
+ className="absolute h-[720px] w-[720px] rounded-full border-[1.5px] border-solid border-[var(--text-dim)] animate-[drop-ripple_2.4s_ease-out_infinite_backwards]"
|
| 8029 |
+
style={{ transformOrigin: "center", animationDelay: `${delay}s` }}
|
| 8030 |
/>
|
| 8031 |
))}
|
| 8032 |
</div>
|
|
|
|
| 8174 |
<div className="new-session-brand-text">
|
| 8175 |
<span className="new-session-brand-name">Pi Web</span>
|
| 8176 |
<span className="new-session-meta">
|
| 8177 |
+
@@ -1562,8 +1557,8 @@ function NoticeShelf({ notices, floating = false, onPauseChange }: { notices: No
|
| 8178 |
+
width: "fit-content",
|
| 8179 |
+
maxWidth: "min(100%, 620px)",
|
| 8180 |
+
boxShadow: floating
|
| 8181 |
+
- ? "0 1px 2px rgba(15,23,42,0.05), 0 10px 28px -14px rgba(15,23,42,0.24)"
|
| 8182 |
+
- : "0 1px 2px rgba(15,23,42,0.04), 0 8px 24px -12px rgba(15,23,42,0.10)",
|
| 8183 |
+
+ ? "0 1px 2px rgba(0,0,0,0.05), 0 10px 28px -14px rgba(0,0,0,0.24)"
|
| 8184 |
+
+ : "0 1px 2px rgba(0,0,0,0.04), 0 8px 24px -12px rgba(0,0,0,0.10)",
|
| 8185 |
+
fontSize: 14,
|
| 8186 |
+
lineHeight: 1.5,
|
| 8187 |
+
transformOrigin: "top right",
|
| 8188 |
@@ -1648,11 +1643,7 @@ function ExtensionWaitingCount({ count }: { count: number }) {
|
| 8189 |
/** Corner brackets pointing outward; when expanded they point inward (restore). */
|
| 8190 |
function ExtensionSizeIcon({ expanded }: { expanded: boolean }) {
|
|
|
|
| 8514 |
<span className="extension-widget-key">{widget.key}</span>
|
| 8515 |
</>
|
| 8516 |
diff --git a/components/FileExplorer.tsx b/components/FileExplorer.tsx
|
| 8517 |
+
index 94cf736..f4fc956 100644
|
| 8518 |
--- a/components/FileExplorer.tsx
|
| 8519 |
+++ b/components/FileExplorer.tsx
|
| 8520 |
@@ -18,6 +18,8 @@ import { buildSearchTree, type SearchTreeNode } from "@/lib/search-tree";
|
|
|
|
| 8526 |
type Translate = ReturnType<typeof useI18n>["t"];
|
| 8527 |
|
| 8528 |
/** Why a listed entry is normally left out; only listings with `hidden=1` report it. */
|
| 8529 |
+
@@ -144,7 +146,7 @@ const GIT_STATUS_COLORS: Record<GitFileStatusKind, string> = {
|
| 8530 |
+
modified: "#d6a84b",
|
| 8531 |
+
added: "#4ade80",
|
| 8532 |
+
deleted: "#f87171",
|
| 8533 |
+
- renamed: "#60a5fa",
|
| 8534 |
+
+ renamed: "var(--text-muted)",
|
| 8535 |
+
untracked: "#4ade80",
|
| 8536 |
+
conflict: "#f87171",
|
| 8537 |
+
};
|
| 8538 |
@@ -174,20 +176,13 @@ function GitStatusBadge({ status, t }: { status: GitFileStatus; t: Translate })
|
| 8539 |
|
| 8540 |
function MentionIcon({ size = 11 }: { size?: number }) {
|
|
|
|
| 8579 |
)}
|
| 8580 |
{!node.isDir && <span style={{ width: 10, flexShrink: 0 }} />}
|
| 8581 |
<span style={{ flexShrink: 0, display: "flex", alignItems: "center", opacity: hidden ? HIDDEN_ENTRY_OPACITY : 1 }}>
|
| 8582 |
+
@@ -396,7 +385,7 @@ export function TreeNode({
|
| 8583 |
+
aria-label={t("files.newlyUploaded")}
|
| 8584 |
+
style={{ width: 14, height: 14, flexShrink: 0, display: "flex", alignItems: "center", justifyContent: "center" }}
|
| 8585 |
+
>
|
| 8586 |
+
- <span style={{ width: 6, height: 6, borderRadius: "50%", background: "#3b82f6" }} />
|
| 8587 |
+
+ <span style={{ width: 6, height: 6, borderRadius: "50%", background: "var(--text-strong)" }} />
|
| 8588 |
+
</span>
|
| 8589 |
+
)}
|
| 8590 |
+
{!hovered && !node.isDir && gitStatus && (
|
| 8591 |
@@ -419,9 +408,7 @@ export function TreeNode({
|
| 8592 |
</span>
|
| 8593 |
)}
|
|
|
|
| 8858 |
|
| 8859 |
export function FolderIcon({ size = 14, open = false }: IconProps & { open?: boolean }) {
|
| 8860 |
diff --git a/components/FileViewer.tsx b/components/FileViewer.tsx
|
| 8861 |
+
index 9426211..5f0943f 100644
|
| 8862 |
--- a/components/FileViewer.tsx
|
| 8863 |
+++ b/components/FileViewer.tsx
|
| 8864 |
+
@@ -6,6 +6,7 @@ import SyntaxHighlighter from "react-syntax-highlighter/dist/esm/prism-light";
|
| 8865 |
+
import renderSyntaxNode from "react-syntax-highlighter/dist/esm/create-element";
|
| 8866 |
+
import { vs } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
| 8867 |
+
import { vscDarkPlus } from "react-syntax-highlighter/dist/cjs/styles/prism";
|
| 8868 |
+
+import { neutralCodeTheme } from "@/lib/neutral-code-theme";
|
| 8869 |
+
import ReactMarkdown from "react-markdown";
|
| 8870 |
+
import { useTheme } from "@/hooks/useTheme";
|
| 8871 |
+
import { usePrismLanguages } from "@/hooks/usePrismLanguages";
|
| 8872 |
+
@@ -34,6 +35,8 @@ import {
|
| 8873 |
type FileViewerDisplayMode as DisplayMode,
|
| 8874 |
type FileViewerState,
|
| 8875 |
} from "@/lib/file-viewer-state";
|
|
|
|
| 8878 |
|
| 8879 |
export type { FileViewerState } from "@/lib/file-viewer-state";
|
| 8880 |
|
| 8881 |
+
@@ -81,12 +84,14 @@ const FILE_CODE_STYLE: CSSProperties = {
|
| 8882 |
+
// dark theme's shorthand is dropped, as CodeBlock does: a theme switch then never
|
| 8883 |
+
// makes React remove one beside the other (it warned, and the view lost its
|
| 8884 |
+
// background).
|
| 8885 |
+
-const fileViewerDarkTheme = {
|
| 8886 |
+
+const fileViewerDarkTheme = neutralCodeTheme({
|
| 8887 |
+
...vscDarkPlus,
|
| 8888 |
+
'pre[class*="language-"]': {
|
| 8889 |
+
...vscDarkPlus['pre[class*="language-"]'],
|
| 8890 |
+
},
|
| 8891 |
+
-};
|
| 8892 |
+
+}, "dark");
|
| 8893 |
+
+// Both without blue: lib/neutral-code-theme.ts.
|
| 8894 |
+
+const fileViewerLightTheme = neutralCodeTheme(vs, "light");
|
| 8895 |
+
delete fileViewerDarkTheme['pre[class*="language-"]'].background;
|
| 8896 |
+
|
| 8897 |
+
const FILE_LINE_NUMBER_STYLE: CSSProperties = {
|
| 8898 |
+
@@ -119,10 +124,7 @@ interface SelectedLineRange {
|
| 8899 |
|
| 8900 |
function MentionIcon() {
|
| 8901 |
return (
|
|
|
|
| 8907 |
);
|
| 8908 |
}
|
| 8909 |
|
| 8910 |
+
@@ -252,11 +254,7 @@ function DownloadLink({ filePath, sourceSessionId }: { filePath: string; sourceS
|
| 8911 |
aria-label={t("i18n.downloadFile")}
|
| 8912 |
className="file-viewer-icon-button"
|
| 8913 |
>
|
|
|
|
| 8920 |
</a>
|
| 8921 |
);
|
| 8922 |
}
|
| 8923 |
+
@@ -1442,7 +1440,7 @@ function TextFileViewer({
|
| 8924 |
+
<SyntaxHighlighter
|
| 8925 |
+
className={wrapLines ? "file-source-view is-wrapped" : "file-source-view"}
|
| 8926 |
+
language={language === "text" || !prismReady ? "plaintext" : language}
|
| 8927 |
+
- style={isDark ? fileViewerDarkTheme : vs}
|
| 8928 |
+
+ style={isDark ? fileViewerDarkTheme : fileViewerLightTheme}
|
| 8929 |
+
showLineNumbers
|
| 8930 |
+
lineNumberStyle={{
|
| 8931 |
+
...FILE_LINE_NUMBER_STYLE,
|
| 8932 |
+
@@ -1708,12 +1706,7 @@ function TextFileViewer({
|
| 8933 |
color: wrapLines ? "var(--text)" : "var(--text-muted)",
|
| 8934 |
}}
|
| 8935 |
>
|
|
|
|
| 8943 |
</button>
|
| 8944 |
</>
|
| 8945 |
)}
|
| 8946 |
+
@@ -1733,7 +1726,7 @@ function TextFileViewer({
|
| 8947 |
gap: 10,
|
| 8948 |
padding: "5px 8px",
|
| 8949 |
border: "1px solid var(--border)",
|
|
|
|
| 9001 |
</dialog>
|
| 9002 |
)}
|
| 9003 |
diff --git a/components/MermaidBlock.tsx b/components/MermaidBlock.tsx
|
| 9004 |
+
index 25e4a63..8ce9799 100644
|
| 9005 |
--- a/components/MermaidBlock.tsx
|
| 9006 |
+++ b/components/MermaidBlock.tsx
|
| 9007 |
+
@@ -10,6 +10,9 @@ import { usePrismLanguages } from "@/hooks/usePrismLanguages";
|
| 9008 |
import { copyText } from "@/lib/clipboard";
|
| 9009 |
import { takeHighlightBudget, waitForHighlight } from "@/lib/highlight-queue";
|
| 9010 |
import { prismLanguagesLoaded } from "@/lib/prism-languages";
|
| 9011 |
+
+import { neutralCodeTheme, neutralMermaidTheme } from "@/lib/neutral-code-theme";
|
| 9012 |
+import { CarbonIcon } from "./CarbonIcon";
|
| 9013 |
+import * as Carbon from "./carbon/icons";
|
| 9014 |
|
| 9015 |
interface MermaidBlockProps {
|
| 9016 |
code: string;
|
| 9017 |
+
@@ -59,7 +62,7 @@ export function MermaidBlock({ code, isStreaming, defaultPreview = false }: Merm
|
| 9018 |
+
startOnLoad: false,
|
| 9019 |
+
securityLevel: "strict",
|
| 9020 |
+
suppressErrorRendering: true,
|
| 9021 |
+
- theme: isDark ? "dark" : "default",
|
| 9022 |
+
+ ...neutralMermaidTheme(isDark),
|
| 9023 |
+
});
|
| 9024 |
+
|
| 9025 |
+
const parsed = await mermaid.parse(code, { suppressErrors: true });
|
| 9026 |
+
@@ -194,9 +197,7 @@ function MermaidZoomDialog({ svg, onClose }: { svg: string; onClose: () => void
|
| 9027 |
title={t("i18n.zoomOut")}
|
| 9028 |
aria-label={t("i18n.zoomOut")}
|
| 9029 |
>
|
|
|
|
| 9034 |
</button>
|
| 9035 |
<span className="mermaid-zoom-value">{Math.round(zoom * 100)}%</span>
|
| 9036 |
<button
|
| 9037 |
+
@@ -206,9 +207,7 @@ function MermaidZoomDialog({ svg, onClose }: { svg: string; onClose: () => void
|
| 9038 |
title={t("i18n.zoomIn")}
|
| 9039 |
aria-label={t("i18n.zoomIn")}
|
| 9040 |
>
|
|
|
|
| 9045 |
</button>
|
| 9046 |
</div>
|
| 9047 |
<button
|
| 9048 |
+
@@ -218,9 +217,7 @@ function MermaidZoomDialog({ svg, onClose }: { svg: string; onClose: () => void
|
| 9049 |
title={t("i18n.fitToWidth")}
|
| 9050 |
aria-label={t("i18n.fitToWidth")}
|
| 9051 |
>
|
|
|
|
| 9056 |
</button>
|
| 9057 |
<button
|
| 9058 |
type="button"
|
| 9059 |
+
@@ -229,9 +226,7 @@ function MermaidZoomDialog({ svg, onClose }: { svg: string; onClose: () => void
|
| 9060 |
title={t("i18n.close")}
|
| 9061 |
aria-label={t("i18n.close")}
|
| 9062 |
>
|
|
|
|
| 9067 |
</button>
|
| 9068 |
</div>
|
| 9069 |
</div>
|
| 9070 |
+
@@ -262,12 +257,14 @@ interface CodeBlockProps {
|
| 9071 |
+
// Prism's light theme uses backgroundColor, while its dark theme uses the
|
| 9072 |
+
// background shorthand. Keep the pre background solely in customStyle so a
|
| 9073 |
+
// theme switch cannot remove/update one and reset the other in React's diff.
|
| 9074 |
+
-const codeBlockDarkTheme = {
|
| 9075 |
+
+const codeBlockDarkTheme = neutralCodeTheme({
|
| 9076 |
+
...vscDarkPlus,
|
| 9077 |
+
'pre[class*="language-"]': {
|
| 9078 |
+
...vscDarkPlus['pre[class*="language-"]'],
|
| 9079 |
+
},
|
| 9080 |
+
-};
|
| 9081 |
+
+}, "dark");
|
| 9082 |
+
+// Both without blue: lib/neutral-code-theme.ts.
|
| 9083 |
+
+const codeBlockLightTheme = neutralCodeTheme(vs, "light");
|
| 9084 |
+
delete codeBlockDarkTheme['pre[class*="language-"]'].background;
|
| 9085 |
+
|
| 9086 |
+
/**
|
| 9087 |
+
@@ -337,7 +334,7 @@ export const CodeBlock = memo(function CodeBlock({ code, lang, headerAction, isS
|
| 9088 |
+
) : (
|
| 9089 |
+
<SyntaxHighlighter
|
| 9090 |
+
language={highlighted ? lang || "text" : "text"}
|
| 9091 |
+
- style={isDark ? codeBlockDarkTheme : vs}
|
| 9092 |
+
+ style={isDark ? codeBlockDarkTheme : codeBlockLightTheme}
|
| 9093 |
+
showLineNumbers
|
| 9094 |
+
lineNumberStyle={{ color: "var(--text-dim)", fontStyle: "normal" }}
|
| 9095 |
+
customStyle={{
|
| 9096 |
diff --git a/components/MessageView.test.mjs b/components/MessageView.test.mjs
|
| 9097 |
index 515b033..eeb4c1e 100644
|
| 9098 |
--- a/components/MessageView.test.mjs
|
|
|
|
| 9119 |
|
| 9120 |
test("collapses a displayed custom message from its header alone", () => {
|
| 9121 |
diff --git a/components/MessageView.tsx b/components/MessageView.tsx
|
| 9122 |
+
index f2ff1de..d98d368 100644
|
| 9123 |
--- a/components/MessageView.tsx
|
| 9124 |
+++ b/components/MessageView.tsx
|
| 9125 |
@@ -1,10 +1,11 @@
|
|
|
|
| 9221 |
src={src}
|
| 9222 |
alt=""
|
| 9223 |
- style={{ maxWidth: 240, maxHeight: 240, borderRadius: 6, objectFit: "contain", display: "block", border: "1px solid rgba(59,130,246,0.15)" }}
|
| 9224 |
+
+ style={{ maxWidth: 240, maxHeight: 240, borderRadius: "var(--radius-sm)", objectFit: "contain", display: "block", border: "none" }}
|
| 9225 |
/>
|
| 9226 |
</ImagePreview>
|
| 9227 |
);
|
|
|
|
| 10202 |
const { t } = useI18n();
|
| 10203 |
const showFileHeaders = files.length > 1;
|
| 10204 |
|
| 10205 |
+
@@ -1483,12 +1352,12 @@ function PatchTextView({ text }: { text: string }) {
|
| 10206 |
+
const bg =
|
| 10207 |
+
kind === "added" ? "rgba(34,197,94,0.12)" :
|
| 10208 |
+
kind === "removed" ? "rgba(248,113,113,0.13)" :
|
| 10209 |
+
- kind === "hunk" ? "rgba(96,165,250,0.12)" :
|
| 10210 |
+
+ kind === "hunk" ? "var(--bg-subtle)" :
|
| 10211 |
+
"transparent";
|
| 10212 |
+
const color =
|
| 10213 |
+
kind === "added" ? "#22c55e" :
|
| 10214 |
+
kind === "removed" ? "#f87171" :
|
| 10215 |
+
- kind === "hunk" ? "var(--accent)" :
|
| 10216 |
+
+ kind === "hunk" ? "var(--text-dim)" :
|
| 10217 |
+
"var(--text)";
|
| 10218 |
+
|
| 10219 |
+
return (
|
| 10220 |
+
@@ -1502,7 +1371,7 @@ function PatchTextView({ text }: { text: string }) {
|
| 10221 |
+
: kind === "removed"
|
| 10222 |
+
? "3px solid #f87171"
|
| 10223 |
+
: kind === "hunk"
|
| 10224 |
+
- ? "3px solid var(--accent)"
|
| 10225 |
+
+ ? "3px solid var(--line)"
|
| 10226 |
+
: "3px solid transparent",
|
| 10227 |
+
}}
|
| 10228 |
+
>
|
| 10229 |
@@ -1587,7 +1456,6 @@ function ResultImages({ images, isError }: { images: ImageContent[]; isError: bo
|
| 10230 |
flexWrap: "wrap",
|
| 10231 |
padding: "10px",
|
|
|
|
| 10437 |
<SelectorRow active={!value} gutter={Boolean(onSetDefault)} onSelect={() => {
|
| 10438 |
setOpen(false);
|
| 10439 |
diff --git a/components/ModelsConfig.tsx b/components/ModelsConfig.tsx
|
| 10440 |
+
index 9f6b556..470db1f 100644
|
| 10441 |
--- a/components/ModelsConfig.tsx
|
| 10442 |
+++ b/components/ModelsConfig.tsx
|
| 10443 |
@@ -54,6 +54,8 @@ import { providerBadgeLabel } from "./enabled-models-helpers";
|
|
|
|
| 10523 |
>
|
| 10524 |
{selectedCount
|
| 10525 |
? t("models.discoveryAddSelectedCount", { count: selectedCount })
|
| 10526 |
+
@@ -561,10 +555,11 @@ type ThinkingLevel = typeof THINKING_LEVELS[number];
|
| 10527 |
+
|
| 10528 |
+
const LEVEL_COLORS: Record<ThinkingLevel, string> = {
|
| 10529 |
+
off: "var(--text-dim)",
|
| 10530 |
+
- minimal: "#6b7280",
|
| 10531 |
+
- low: "#60a5fa",
|
| 10532 |
+
- medium: "#a78bfa",
|
| 10533 |
+
- high: "#f472b6",
|
| 10534 |
+
+ // Neutral grays up to medium, then the warm state colours: no blue or violet.
|
| 10535 |
+
+ minimal: "var(--text-dim)",
|
| 10536 |
+
+ low: "var(--text-muted)",
|
| 10537 |
+
+ medium: "var(--text-strong)",
|
| 10538 |
+
+ high: "#f59e0b",
|
| 10539 |
+
xhigh: "#fb923c",
|
| 10540 |
+
max: "#ef4444",
|
| 10541 |
+
};
|
| 10542 |
+
@@ -627,7 +622,7 @@ function ThinkingLevelMapEditor({
|
| 10543 |
alignItems: "center",
|
| 10544 |
gap: 8,
|
| 10545 |
padding: "5px 4px",
|
|
|
|
| 10548 |
background: "transparent",
|
| 10549 |
border: "1px solid transparent",
|
| 10550 |
}}
|
| 10551 |
+
@@ -644,7 +639,7 @@ function ThinkingLevelMapEditor({
|
| 10552 |
</span>
|
| 10553 |
</div>
|
| 10554 |
|
|
|
|
| 10557 |
<button
|
| 10558 |
onClick={() => setLevel(level, "omit")}
|
| 10559 |
style={{ ...btnBase, ...(state === "omit" ? btnActive : {}) }}
|
| 10560 |
+
@@ -659,7 +654,7 @@ function ThinkingLevelMapEditor({
|
| 10561 |
</button>
|
| 10562 |
</div>
|
| 10563 |
|
|
|
|
| 10566 |
<button
|
| 10567 |
onClick={() => setLevel(level, strVal || level)}
|
| 10568 |
style={{ ...btnBase, ...(state === "string" ? btnActive : {}), borderRight: "1px solid var(--border)", flexShrink: 0 }}
|
| 10569 |
+
@@ -747,7 +742,7 @@ function HeaderListEditor({ headers, onChange }: {
|
| 10570 |
padding: "6px 9px",
|
| 10571 |
background: "none",
|
| 10572 |
border: "1px solid rgba(239,68,68,0.3)",
|
|
|
|
| 10575 |
color: "#ef4444",
|
| 10576 |
cursor: "pointer",
|
| 10577 |
fontSize: 11,
|
| 10578 |
+
@@ -768,7 +763,7 @@ function HeaderListEditor({ headers, onChange }: {
|
| 10579 |
...current,
|
| 10580 |
{ id: nextRowIdRef.current++, name: "", value: "" },
|
| 10581 |
])}
|
|
|
|
| 10584 |
+ Add header
|
| 10585 |
</button>
|
| 10586 |
</div>
|
| 10587 |
+
@@ -1051,9 +1046,9 @@ function ModelDetail({
|
| 10588 |
height: 28,
|
| 10589 |
padding: "0 8px",
|
| 10590 |
border: `1px solid ${testState.phase === "error" ? "#fecaca" : testState.phase === "success" ? "#bbf7d0" : "var(--border)"}`,
|
| 10591 |
- borderRadius: 4,
|
| 10592 |
+
- background: testState.phase === "error" ? "#fee2e2" : testState.phase === "success" ? "#dcfce7" : "#e5e7eb",
|
| 10593 |
+
- color: "#111827",
|
| 10594 |
+ borderRadius: "var(--radius-xs)",
|
| 10595 |
+
+ background: testState.phase === "error" ? "#fee2e2" : testState.phase === "success" ? "#dcfce7" : "#e5e5e5",
|
| 10596 |
+
+ color: "#0a0a0a",
|
| 10597 |
fontSize: 11,
|
| 10598 |
+
display: "inline-flex",
|
| 10599 |
+
alignItems: "center",
|
| 10600 |
+
@@ -1075,9 +1070,7 @@ function ModelDetail({
|
| 10601 |
className={testState.phase === "success" ? "is-success" : undefined}
|
| 10602 |
>
|
| 10603 |
{testState.phase === "success" && (
|
|
|
|
| 10608 |
)}
|
| 10609 |
{testState.phase === "testing" ? t("i18n.checking") : testState.phase === "success" ? t("common.ok") : t("i18n.test")}
|
| 10610 |
</ConfigButton>
|
| 10611 |
+
@@ -1096,7 +1089,7 @@ function ModelDetail({
|
| 10612 |
onClick={() => void handleCatalogFill()}
|
| 10613 |
disabled={!model.id.trim() || catalogState.phase === "loading"}
|
| 10614 |
style={{
|
|
|
|
| 10617 |
background: "var(--bg-panel)",
|
| 10618 |
color: !model.id.trim() || catalogState.phase === "loading" ? "var(--text-dim)" : "var(--text-muted)",
|
| 10619 |
cursor: !model.id.trim() || catalogState.phase === "loading" ? "not-allowed" : "pointer",
|
| 10620 |
+
@@ -1227,20 +1220,7 @@ function ModelDetail({
|
| 10621 |
{advancedSummary}
|
| 10622 |
</span>
|
| 10623 |
</span>
|
|
|
|
| 10639 |
</button>
|
| 10640 |
|
| 10641 |
{advancedOpen && (
|
| 10642 |
+
@@ -1492,7 +1472,7 @@ function OAuthDetail({ provider, onRefresh, enabledModels }: {
|
| 10643 |
<button
|
| 10644 |
key={option.id}
|
| 10645 |
onClick={() => submitSelection(loginState.token, option.id)}
|
|
|
|
| 10648 |
>
|
| 10649 |
{option.label}
|
| 10650 |
</button>
|
| 10651 |
+
@@ -1527,7 +1507,7 @@ function OAuthDetail({ provider, onRefresh, enabledModels }: {
|
| 10652 |
<p style={{ margin: 0, fontSize: 12, color: "var(--text-muted)", lineHeight: 1.5 }}>
|
| 10653 |
Open the verification page and enter this code:
|
| 10654 |
</p>
|
|
|
|
| 10657 |
{loginState.userCode}
|
| 10658 |
</div>
|
| 10659 |
<p style={{ margin: 0, fontSize: 11, color: "var(--text-dim)", lineHeight: 1.5 }}>
|
| 10660 |
+
@@ -1666,7 +1646,7 @@ function ApiKeyDetail({ provider, onRefresh, enabledModels }: {
|
| 10661 |
style={{
|
| 10662 |
padding: "6px 12px",
|
| 10663 |
background: savedOk ? "#16a34a" : apiKey.trim() ? "var(--accent)" : "var(--bg-panel)",
|
|
|
|
| 10666 |
color: savedOk ? "#fff" : apiKey.trim() ? "var(--accent-contrast)" : "var(--text-dim)",
|
| 10667 |
cursor: (saving || !apiKey.trim() || savedOk) ? "not-allowed" : "pointer",
|
| 10668 |
fontSize: 12, fontWeight: 600, flexShrink: 0,
|
| 10669 |
+
@@ -1674,9 +1654,7 @@ function ApiKeyDetail({ provider, onRefresh, enabledModels }: {
|
| 10670 |
}}
|
| 10671 |
>
|
| 10672 |
{savedOk && (
|
|
|
|
| 10677 |
)}
|
| 10678 |
{savedOk ? t("i18n.saved") : saving ? t("i18n.saving") : t("i18n.save")}
|
| 10679 |
</button>
|
| 10680 |
+
@@ -1725,7 +1703,7 @@ function AddProviderPicker({
|
| 10681 |
padding: "10px 12px",
|
| 10682 |
background: "var(--bg-panel)",
|
| 10683 |
border: "1px solid var(--border)",
|
|
|
|
| 10686 |
boxSizing: "border-box",
|
| 10687 |
cursor: "pointer",
|
| 10688 |
minWidth: 0,
|
| 10689 |
+
@@ -1747,12 +1725,10 @@ function AddProviderPicker({
|
| 10690 |
onClose();
|
| 10691 |
}}
|
| 10692 |
>
|
|
|
|
| 10701 |
<input
|
| 10702 |
ref={inputRef}
|
| 10703 |
value={search}
|
| 10704 |
+
@@ -1782,10 +1758,8 @@ function AddProviderPicker({
|
| 10705 |
<div style={{ fontSize: 12, fontWeight: 600, color: "var(--text)", lineHeight: 1.3, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>OpenAI / Anthropic compatible</div>
|
| 10706 |
<div style={{ fontSize: 10, color: "var(--text-dim)", marginTop: 2 }}>{t("i18n.customEndpoint")}</div>
|
| 10707 |
</div>
|
|
|
|
| 10714 |
</span>
|
| 10715 |
</button>
|
| 10716 |
)}
|
| 10717 |
+
@@ -2177,13 +2151,7 @@ export function ModelsConfig({ onClose, embedded = false, cwd = null }: {
|
| 10718 |
onClick={() => setSelection({ type: "provider", name: pName })}
|
| 10719 |
active={isProviderSelected}
|
| 10720 |
>
|
|
|
|
| 10729 |
<ConfigSidebarText className="is-grow">
|
| 10730 |
{pName}
|
| 10731 |
</ConfigSidebarText>
|
| 10732 |
+
@@ -2204,7 +2172,7 @@ export function ModelsConfig({ onClose, embedded = false, cwd = null }: {
|
| 10733 |
{m.id || t("i18n.newModel")}
|
| 10734 |
</ConfigSidebarText>
|
| 10735 |
{m.reasoning && (
|
| 10736 |
- <span style={{ fontSize: 9, padding: "1px 4px", background: "rgba(99,102,241,0.12)", color: "rgba(99,102,241,0.8)", borderRadius: 3, flexShrink: 0 }}>T</span>
|
| 10737 |
+
+ <span style={{ fontSize: 9, padding: "1px 4px", background: "var(--bg-subtle)", color: "var(--text-muted)", borderRadius: "var(--radius-xs)", flexShrink: 0 }}>T</span>
|
| 10738 |
)}
|
| 10739 |
</ConfigSidebarItem>
|
| 10740 |
);
|
| 10741 |
+
@@ -2250,10 +2218,7 @@ export function ModelsConfig({ onClose, embedded = false, cwd = null }: {
|
| 10742 |
className={savedOk ? "is-success" : undefined}
|
| 10743 |
>
|
| 10744 |
{savedOk && (
|
|
|
|
| 10992 |
</button>
|
| 10993 |
{report && (
|
| 10994 |
diff --git a/components/SelectorRow.tsx b/components/SelectorRow.tsx
|
| 10995 |
+
index 555da8f..5bd340c 100644
|
| 10996 |
--- a/components/SelectorRow.tsx
|
| 10997 |
+++ b/components/SelectorRow.tsx
|
| 10998 |
@@ -2,12 +2,12 @@
|
|
|
|
| 11049 |
cursor: "pointer",
|
| 11050 |
opacity: showSave ? 1 : 0,
|
| 11051 |
pointerEvents: showSave ? "auto" : "none",
|
| 11052 |
+
@@ -120,7 +123,7 @@ export function SelectorRow({
|
| 11053 |
+
onMouseEnter={(event) => {
|
| 11054 |
+
event.currentTarget.style.background = "var(--bg-selected)";
|
| 11055 |
+
event.currentTarget.style.color = "var(--accent)";
|
| 11056 |
+
- event.currentTarget.style.borderColor = "rgba(37,99,235,0.35)";
|
| 11057 |
+
+ event.currentTarget.style.borderColor = "var(--border)";
|
| 11058 |
+
}}
|
| 11059 |
+
onMouseLeave={(event) => {
|
| 11060 |
+
event.currentTarget.style.background = "var(--bg-hover)";
|
| 11061 |
diff --git a/components/SessionSidebar.test.mjs b/components/SessionSidebar.test.mjs
|
| 11062 |
index 6bc923f..70b9e80 100644
|
| 11063 |
--- a/components/SessionSidebar.test.mjs
|
|
|
|
| 11090 |
// search in the toolbar row's place.
|
| 11091 |
const head = html.slice(html.indexOf('<div class="sidebar-files-head">'), html.indexOf('<div class="sidebar-files-scroll'));
|
| 11092 |
diff --git a/components/SessionTree.test.mjs b/components/SessionTree.test.mjs
|
| 11093 |
+
index fb53e4b..060f6d0 100644
|
| 11094 |
--- a/components/SessionTree.test.mjs
|
| 11095 |
+++ b/components/SessionTree.test.mjs
|
| 11096 |
@@ -119,7 +119,7 @@ test("the pinned header toggles the section and shows activity dots only while c
|
|
|
|
| 11111 |
assert.match(html, /<span class="session-tree-group-name">app<\/span><svg[^>]*class="session-tree-group-pin"[\s\S]*?<\/svg><span class="session-tree-summary">/);
|
| 11112 |
// No folder: a project group is no second file tree.
|
| 11113 |
assert.doesNotMatch(html, /session-tree-group-icon/);
|
| 11114 |
+
@@ -467,12 +467,13 @@ test("row CSS stays flat, themed and quiet", () => {
|
| 11115 |
+
assert.doesNotMatch(css, /&/, "no CSS nesting");
|
| 11116 |
assert.doesNotMatch(css, /@starting-style|prefers-color-scheme/);
|
| 11117 |
const colors = new Set((css.match(/#[0-9a-fA-F]{3,8}\b/g) ?? []).map((color) => color.toLowerCase()));
|
| 11118 |
+
- for (const color of colors) assert.ok(["#ef4444", "#0891b2", "#d97706", "#f87171", "#fff"].includes(color), `unexpected color ${color}`);
|
| 11119 |
- // Three text colors: content, labels, meta (plus the accent for states,
|
| 11120 |
- // the errors' red and the delete button's white). No mixes of their own.
|
| 11121 |
+
+ for (const color of colors) assert.ok(["#ef4444", "#d97706", "#f87171", "#fff"].includes(color), `unexpected color ${color}`);
|
| 11122 |
+ // Three text colors: content, labels, meta, the selected title a step
|
| 11123 |
+ // stronger (plus the accent for states, the errors' red and the delete
|
| 11124 |
+ // button's white). No mixes of their own.
|
|
|
|
| 11678 |
</div>
|
| 11679 |
);
|
| 11680 |
diff --git a/components/TerminalPanel.tsx b/components/TerminalPanel.tsx
|
| 11681 |
+
index d24bb94..77b2f58 100644
|
| 11682 |
--- a/components/TerminalPanel.tsx
|
| 11683 |
+++ b/components/TerminalPanel.tsx
|
| 11684 |
@@ -18,6 +18,8 @@ import {
|
|
|
|
| 11690 |
|
| 11691 |
interface Props {
|
| 11692 |
tab: TerminalTab;
|
| 11693 |
+
@@ -94,11 +96,12 @@ export function TerminalPanel({ tab, active, onRestart, onClosed, onCloseError }
|
| 11694 |
+
screenReaderMode: true,
|
| 11695 |
+
disableStdin: true,
|
| 11696 |
+
theme: {
|
| 11697 |
+
- background: "#111318", foreground: "#d7dce5", cursor: "#60a5fa",
|
| 11698 |
+
- selectionBackground: "#365b8a",
|
| 11699 |
+
- black: "#1d222b", red: "#f87171", green: "#4ade80", yellow: "#facc15",
|
| 11700 |
+
- blue: "#60a5fa", magenta: "#c084fc", cyan: "#22d3ee", white: "#e5e7eb",
|
| 11701 |
+
- brightBlack: "#6b7280", brightRed: "#fca5a5", brightGreen: "#86efac",
|
| 11702 |
+
+ // Neutral grays like the rest of the app; the ANSI hues stay, they are the programs' own colours.
|
| 11703 |
+
+ background: "#111111", foreground: "#d4d4d4", cursor: "#e5e5e5",
|
| 11704 |
+
+ selectionBackground: "#3d3d3d",
|
| 11705 |
+
+ black: "#1f1f1f", red: "#f87171", green: "#4ade80", yellow: "#facc15",
|
| 11706 |
+
+ blue: "#60a5fa", magenta: "#c084fc", cyan: "#22d3ee", white: "#e5e5e5",
|
| 11707 |
+
+ brightBlack: "#737373", brightRed: "#fca5a5", brightGreen: "#86efac",
|
| 11708 |
+
brightYellow: "#fde047", brightBlue: "#93c5fd", brightMagenta: "#d8b4fe",
|
| 11709 |
+
brightCyan: "#67e8f9", brightWhite: "#ffffff",
|
| 11710 |
+
},
|
| 11711 |
+
@@ -260,15 +263,11 @@ export function TerminalPanel({ tab, active, onRestart, onClosed, onCloseError }
|
| 11712 |
</div>
|
| 11713 |
{status === "error" && (
|
| 11714 |
<button type="button" onClick={() => setReconnectKey((key) => key + 1)} disabled={Boolean(tab.closing)} title={t("terminal.reconnect")} aria-label={t("terminal.reconnect")}>
|
|
|
|
| 12441 |
"chat.filesWritten": "已變更的檔案",
|
| 12442 |
"chat.openWrittenFile": "開啟 {name}",
|
| 12443 |
"chat.loadEarlier": "向上捲動以載入較早的訊息",
|
| 12444 |
+
diff --git a/lib/neutral-code-theme.test.mjs b/lib/neutral-code-theme.test.mjs
|
| 12445 |
+
new file mode 100644
|
| 12446 |
+
index 0000000..a9dc596
|
| 12447 |
+
--- /dev/null
|
| 12448 |
+
+++ b/lib/neutral-code-theme.test.mjs
|
| 12449 |
+
@@ -0,0 +1,45 @@
|
| 12450 |
+
+import assert from "node:assert/strict";
|
| 12451 |
+
+import test from "node:test";
|
| 12452 |
+
+import { createRequire } from "node:module";
|
| 12453 |
+
+
|
| 12454 |
+
+const require = createRequire(import.meta.url);
|
| 12455 |
+
+const { vscDarkPlus, vs } = require("react-syntax-highlighter/dist/cjs/styles/prism");
|
| 12456 |
+
+const load = () => import("./neutral-code-theme.ts");
|
| 12457 |
+
+
|
| 12458 |
+
+// A colour is blue when its hue sits between cyan and violet and it is not a gray.
|
| 12459 |
+
+function isBlue(hex) {
|
| 12460 |
+
+ const m = /^#([0-9a-f]{6})$/i.exec(hex ?? "");
|
| 12461 |
+
+ if (!m) return false;
|
| 12462 |
+
+ const [r, g, b] = [0, 2, 4].map((i) => parseInt(m[1].slice(i, i + 2), 16) / 255);
|
| 12463 |
+
+ const max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min, l = (max + min) / 2;
|
| 12464 |
+
+ if (d < 0.06) return false;
|
| 12465 |
+
+ const s = d / (1 - Math.abs(2 * l - 1));
|
| 12466 |
+
+ let h = max === r ? ((g - b) / d) % 6 : max === g ? (b - r) / d + 2 : (r - g) / d + 4;
|
| 12467 |
+
+ h = (h * 60 + 360) % 360;
|
| 12468 |
+
+ return s > 0.2 && h >= 180 && h <= 275;
|
| 12469 |
+
+}
|
| 12470 |
+
+
|
| 12471 |
+
+test("no token of either code theme is blue; keywords keep standing out by weight", async () => {
|
| 12472 |
+
+ const { neutralCodeTheme } = await load();
|
| 12473 |
+
+ for (const [theme, mode, keyword] of [[vscDarkPlus, "dark", "keyword"], [vs, "light", "keyword"]]) {
|
| 12474 |
+
+ const out = neutralCodeTheme(theme, mode);
|
| 12475 |
+
+ const blue = Object.entries(out).filter(([, style]) => isBlue(style.color)).map(([k, style]) => `${k} ${style.color}`);
|
| 12476 |
+
+ assert.deepEqual(blue, [], `${mode}: blue tokens left`);
|
| 12477 |
+
+ assert.equal(out[keyword].fontWeight, 600, `${mode}: keywords are bold`);
|
| 12478 |
+
+ }
|
| 12479 |
+
+ // the other hues stay: strings, comments
|
| 12480 |
+
+ assert.equal(neutralCodeTheme(vscDarkPlus, "dark").string.color, vscDarkPlus.string.color);
|
| 12481 |
+
+ assert.equal(neutralCodeTheme(vs, "light").comment.color, vs.comment.color);
|
| 12482 |
+
+ // the source themes are not changed
|
| 12483 |
+
+ assert.equal(vscDarkPlus.keyword.color.toLowerCase(), "#569cd6");
|
| 12484 |
+
+});
|
| 12485 |
+
+
|
| 12486 |
+
+test("Mermaid diagrams use the base theme with neutral variables only", async () => {
|
| 12487 |
+
+ const { neutralMermaidTheme } = await load();
|
| 12488 |
+
+ for (const dark of [true, false]) {
|
| 12489 |
+
+ const { theme, themeVariables } = neutralMermaidTheme(dark);
|
| 12490 |
+
+ assert.equal(theme, "base");
|
| 12491 |
+
+ const blue = Object.entries(themeVariables).filter(([, v]) => typeof v === "string" && isBlue(v));
|
| 12492 |
+
+ assert.deepEqual(blue, []);
|
| 12493 |
+
+ }
|
| 12494 |
+
+});
|
| 12495 |
+
diff --git a/lib/neutral-code-theme.ts b/lib/neutral-code-theme.ts
|
| 12496 |
+
new file mode 100644
|
| 12497 |
+
index 0000000..ff0d9d6
|
| 12498 |
+
--- /dev/null
|
| 12499 |
+
+++ b/lib/neutral-code-theme.ts
|
| 12500 |
+
@@ -0,0 +1,60 @@
|
| 12501 |
+
+import type { CSSProperties } from "react";
|
| 12502 |
+
+
|
| 12503 |
+
+/**
|
| 12504 |
+
+ * The Prism themes Pi Web highlights code with (VS Code's dark+ and light "vs"),
|
| 12505 |
+
+ * with their blue tokens turned into the design system's neutral grays: no blue
|
| 12506 |
+
+ * anywhere in the app, code blocks included. Keywords keep standing out by weight
|
| 12507 |
+
+ * (600) instead of hue; strings, numbers, functions and comments keep their own
|
| 12508 |
+
+ * colours. Client-safe and pure.
|
| 12509 |
+
+ */
|
| 12510 |
+
+type PrismTheme = Record<string, CSSProperties>;
|
| 12511 |
+
+
|
| 12512 |
+
+const DARK: Record<string, CSSProperties> = {
|
| 12513 |
+
+ "#569cd6": { color: "#e5e5e5", fontWeight: 600 }, // keywords, tags, booleans
|
| 12514 |
+
+ "#9cdcfe": { color: "#e5e5e5" }, // properties, constants, attribute names
|
| 12515 |
+
+};
|
| 12516 |
+
+
|
| 12517 |
+
+const LIGHT: Record<string, CSSProperties> = {
|
| 12518 |
+
+ "#0000ff": { color: "#0a0a0a", fontWeight: 600 }, // keywords, at-rules, attribute values
|
| 12519 |
+
+ "#00009f": { color: "#0a0a0a", fontWeight: 600 },
|
| 12520 |
+
+ "#2b91af": { color: "#404040", fontWeight: 500 }, // class names, JSON keys
|
| 12521 |
+
+ "#36acaa": { color: "#5c5c5c" }, // numbers, booleans, variables
|
| 12522 |
+
+};
|
| 12523 |
+
+
|
| 12524 |
+
+export function neutralCodeTheme(theme: PrismTheme, mode: "dark" | "light"): PrismTheme {
|
| 12525 |
+
+ const map = mode === "dark" ? DARK : LIGHT;
|
| 12526 |
+
+ const out: PrismTheme = {};
|
| 12527 |
+
+ for (const [selector, style] of Object.entries(theme)) {
|
| 12528 |
+
+ const color = typeof style.color === "string" ? style.color.toLowerCase() : "";
|
| 12529 |
+
+ out[selector] = map[color] ? { ...style, ...map[color] } : { ...style };
|
| 12530 |
+
+ }
|
| 12531 |
+
+ return out;
|
| 12532 |
+
+}
|
| 12533 |
+
+
|
| 12534 |
+
+/** Mermaid's diagrams in the same neutral grays ("base" theme), never its lavender and blue defaults. */
|
| 12535 |
+
+export function neutralMermaidTheme(isDark: boolean) {
|
| 12536 |
+
+ return {
|
| 12537 |
+
+ theme: "base" as const,
|
| 12538 |
+
+ themeVariables: isDark
|
| 12539 |
+
+ ? {
|
| 12540 |
+
+ darkMode: true, background: "#0a0a0a", fontFamily: "Geist, ui-sans-serif, system-ui, sans-serif",
|
| 12541 |
+
+ primaryColor: "#1f1f1f", primaryTextColor: "#ededed", primaryBorderColor: "#525252",
|
| 12542 |
+
+ secondaryColor: "#2a2a2a", secondaryTextColor: "#ededed", secondaryBorderColor: "#525252",
|
| 12543 |
+
+ tertiaryColor: "#1a1a1a", tertiaryTextColor: "#d4d4d4", tertiaryBorderColor: "#3d3d3d",
|
| 12544 |
+
+ lineColor: "#a3a3a3", textColor: "#d4d4d4", mainBkg: "#1f1f1f", nodeBorder: "#525252",
|
| 12545 |
+
+ clusterBkg: "#141414", clusterBorder: "#3d3d3d", edgeLabelBackground: "#1a1a1a",
|
| 12546 |
+
+ noteBkgColor: "#2a2a2a", noteTextColor: "#ededed", noteBorderColor: "#525252",
|
| 12547 |
+
+ actorBkg: "#1f1f1f", actorBorder: "#525252", actorTextColor: "#ededed", signalColor: "#d4d4d4", signalTextColor: "#d4d4d4",
|
| 12548 |
+
+ }
|
| 12549 |
+
+ : {
|
| 12550 |
+
+ darkMode: false, background: "#ffffff", fontFamily: "Geist, ui-sans-serif, system-ui, sans-serif",
|
| 12551 |
+
+ primaryColor: "#f5f5f5", primaryTextColor: "#0a0a0a", primaryBorderColor: "#a3a3a3",
|
| 12552 |
+
+ secondaryColor: "#e5e5e5", secondaryTextColor: "#0a0a0a", secondaryBorderColor: "#a3a3a3",
|
| 12553 |
+
+ tertiaryColor: "#fafafa", tertiaryTextColor: "#2e2e2e", tertiaryBorderColor: "#d4d4d4",
|
| 12554 |
+
+ lineColor: "#525252", textColor: "#2e2e2e", mainBkg: "#f5f5f5", nodeBorder: "#a3a3a3",
|
| 12555 |
+
+ clusterBkg: "#fafafa", clusterBorder: "#d4d4d4", edgeLabelBackground: "#ffffff",
|
| 12556 |
+
+ noteBkgColor: "#f5f5f5", noteTextColor: "#0a0a0a", noteBorderColor: "#a3a3a3",
|
| 12557 |
+
+ actorBkg: "#f5f5f5", actorBorder: "#a3a3a3", actorTextColor: "#0a0a0a", signalColor: "#2e2e2e", signalTextColor: "#2e2e2e",
|
| 12558 |
+
+ },
|
| 12559 |
+
+ };
|
| 12560 |
+
+}
|
| 12561 |
diff --git a/lib/step-summary.test.mjs b/lib/step-summary.test.mjs
|
| 12562 |
new file mode 100644
|
| 12563 |
index 0000000..6cbe005
|