dsh-ui-tweaks 0.16.2 → 0.16.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +4 -12
- package/README.md +4 -11
- package/lib/client.js +288 -4
- package/lib/client.js.map +1 -1
- package/lib/types/client/effort.d.ts +47 -0
- package/lib/types/client/effort.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +2 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/package.json +41 -42
package/README.en.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dsh-ui-tweaks
|
|
2
2
|
|
|
3
|
-
> **Dependency**: currently targets **DSH v0.1.5-
|
|
3
|
+
> **Dependency**: currently targets **DSH v0.1.5-rc.1**.
|
|
4
4
|
|
|
5
5
|
A [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/) (DSH) web plugin that live-tunes the conversation UI from the Settings panel.
|
|
6
6
|
|
|
@@ -18,7 +18,7 @@ A [DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/) (DSH)
|
|
|
18
18
|
## Features
|
|
19
19
|
|
|
20
20
|
- **Code font size (px)** — absolute 8–32px, default 13 (DSH's stock code-block size at a 16px body); applies to code blocks, with inline code following proportionally. The legacy percentage (`codeFontScale`) stays compatible and is overridden once a px value is set. Message text keeps DSH's stock sizing.
|
|
21
|
-
- **Theme (single choice in Layout, stock look by default)** — choose `Default` or **Neon poster**: a two-scheme poster skin — paper-white with ink-black hairlines in light mode, near-black with light hairlines in dark mode, both with lime highlights and an Anthropic-red action accent (buttons, links and selections follow the theme tokens automatically; code blocks and the composer card get hard offset shadows), applied live. Future skins will be added as further options the same way.
|
|
21
|
+
- **Theme (single choice in Layout, stock look by default)** — choose `Default` or **Neon poster**: a two-scheme poster skin — paper-white with ink-black hairlines in light mode, near-black with light hairlines in dark mode, both with lime highlights and an Anthropic-red action accent (buttons, links and selections follow the theme tokens automatically; code blocks and the composer card get hard offset shadows), and the composer's reasoning-effort label is tinted by intensity (green / amber / blue / violet, with a same-hue wash sweeping option rows on hover), applied live. Future skins will be added as further options the same way.
|
|
22
22
|
- **Timeline (single choice in Features)** — one switch, two options:
|
|
23
23
|
- **Native (default)** — DSH's built-in turn rail (the row of small dots beside the messages), the stock behavior.
|
|
24
24
|
- **Web (classic)** — the v0.11 classic right-side navigation rail, restored: vertically centered on the message area's right edge, a thin line strip when collapsed, a 240px panel on hover (message previews + current-position highlight), a per-item detail bubble with timestamp, and **click to jump** (deep history pages in automatically before landing, with a landing self-check); wheel over the rail scrubs clipped items into reach. Data comes from the server-side `dshChatTimeline` session projection (every user message, independent of the browser's loaded window); sessions with fewer than two user messages hide it. On the web option the native turn rail is hidden with one theme-independent CSS rule (matching its `--turn-natural-height` inline variable), so the two never appear together.
|
|
@@ -61,7 +61,7 @@ Settings entry: **Settings → UI Tweaks**.
|
|
|
61
61
|
# from npm (recommended, prebuilt)
|
|
62
62
|
npx -y @deepseek-ai/dsh plugin --profile web add dsh-ui-tweaks
|
|
63
63
|
|
|
64
|
-
# from GitHub (source
|
|
64
|
+
# from GitHub (source with prebuilt artifacts; no local build needed)
|
|
65
65
|
npx -y @deepseek-ai/dsh plugin --profile web add github:wlj521/dsh-ui-tweaks
|
|
66
66
|
```
|
|
67
67
|
|
|
@@ -73,15 +73,7 @@ npx -y @deepseek-ai/dsh plugin --profile web add dsh-ui-tweaks@0.12.0
|
|
|
73
73
|
npx -y @deepseek-ai/dsh plugin --profile web add github:wlj521/dsh-ui-tweaks#v0.12.0 # pin a git tag
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
add the exact key it prints to the profile's `pnpm-workspace.yaml`:
|
|
78
|
-
|
|
79
|
-
```yaml
|
|
80
|
-
allowBuilds:
|
|
81
|
-
dsh-ui-tweaks: true
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
…then run `add` again. Restart DSH web once after installing (bundle plugins
|
|
76
|
+
Restart DSH web once after installing (bundle plugins
|
|
85
77
|
are scanned at process start).
|
|
86
78
|
|
|
87
79
|
> If pnpm reports symlink/hoist errors, set `nodeLinker: hoisted` in the
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dsh-ui-tweaks
|
|
2
2
|
|
|
3
|
-
> **依赖版本**:当前依赖 **DSH v0.1.5-
|
|
3
|
+
> **依赖版本**:当前依赖 **DSH v0.1.5-rc.1**。
|
|
4
4
|
|
|
5
5
|
[DeepSeek Harness](https://deepseek-harness.github.io/deepseek-harness/)(DSH)Web UI 插件:在设置面板中实时调整对话界面——代码字号、**主题皮肤**(荧光海报风)、**时间线切换**(原生回合导航轨 / 经典网页时间线)、**GitBar**(会话头部的分支胶囊 + 右侧边栏里的终端 / 代码差异标签页),可开关的**归档管理**(设置中的「归档」页面:查看、恢复或彻底删除已归档会话),可开关的**任务提醒**(会话完成或需要交互时,通过标签页标题闪烁 / 系统通知 / 提示音把你唤回来),以及**缓存命中率两位小数**(把输入框下方统计条的缓存命中百分比改写为精确值)。
|
|
6
6
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
## 功能
|
|
19
19
|
|
|
20
20
|
- **代码字号(px)**:绝对值 8–32,默认 13(正文 16 时的 DSH 原生代码块字号);作用于代码块,行内代码按比例跟随——旧版的百分比配置(`codeFontScale`)仍然兼容,一旦设置新的 px 值即以其为准。消息正文保持 DSH 原生字号。
|
|
21
|
-
- **主题(布局区单选,默认即原生外观)**:可选 `默认` 或 **荧光海报**——海报风双方案皮肤:浅色模式纸白底 + 黑粗线条,深色模式近黑底 + 浅色粗线条,都配荧光黄高亮 + Anthropic
|
|
21
|
+
- **主题(布局区单选,默认即原生外观)**:可选 `默认` 或 **荧光海报**——海报风双方案皮肤:浅色模式纸白底 + 黑粗线条,深色模式近黑底 + 浅色粗线条,都配荧光黄高亮 + Anthropic 红点缀(按钮、链接、选中态跟随主题变量自动变色,代码块与输入框卡片带硬阴影贴纸效果),输入框模型旁的**推理等级**按强度着色(绿 / 琥珀 / 蓝 / 紫,悬停选项行时同色底从左扫过),切换即时生效。后续皮肤会以同样方式追加为新选项。
|
|
22
22
|
- **时间线(功能区内单选)**:一个开关两档,选一个:
|
|
23
23
|
- **原生(默认)**:DSH 自带的回合导航轨(消息右侧的小圆点条),即官方默认行为。
|
|
24
24
|
- **网页(经典)**:找回 v0.11 的经典右侧导航轨——消息区右缘垂直居中,收起时是一列细线,悬停展开为 240px 面板(消息预览 + 高亮当前位),悬停单项弹出带时间戳的详情气泡,**点击跳转**到对应消息(深历史会自动连续翻页加载后落点,带落点自校验);滚轮可在轨上直接翻阅被裁剪的条目。数据来自服务端 `dshChatTimeline` 会话投影(全量用户消息,与浏览器已加载窗口无关),少于两条用户消息的会话自动隐藏;切换到网页档时,原生回合导航轨由插件用一条主题无关的 CSS 规则(命中其 `--turn-natural-height` 内联变量)隐藏,两个不会同时出现。
|
|
@@ -64,7 +64,7 @@ ui-tweaks:
|
|
|
64
64
|
# 方式一:从 npm 安装(推荐,预构建产物,一条命令装好)
|
|
65
65
|
npx -y @deepseek-ai/dsh plugin --profile web add dsh-ui-tweaks
|
|
66
66
|
|
|
67
|
-
# 方式二:从 GitHub
|
|
67
|
+
# 方式二:从 GitHub 仓库安装(源码 + 预构建产物,无需本地构建)
|
|
68
68
|
npx -y @deepseek-ai/dsh plugin --profile web add github:wlj521/dsh-ui-tweaks
|
|
69
69
|
```
|
|
70
70
|
|
|
@@ -75,14 +75,7 @@ npx -y @deepseek-ai/dsh plugin --profile web add dsh-ui-tweaks@0.12.0
|
|
|
75
75
|
npx -y @deepseek-ai/dsh plugin --profile web add github:wlj521/dsh-ui-tweaks#v0.12.0 # 锁定 git tag
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
```yaml
|
|
81
|
-
allowBuilds:
|
|
82
|
-
dsh-ui-tweaks: true
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
然后重新执行 `add`。安装完成后**重启一次 `dsh web`**(bundle 插件在进程启动时扫描)。
|
|
78
|
+
安装完成后**重启一次 `dsh web`**(bundle 插件在进程启动时扫描)。
|
|
86
79
|
|
|
87
80
|
> 若 pnpm 报符号链接/hoist 相关错误,可在 profile 的 `pnpm-workspace.yaml` 中设置 `nodeLinker: hoisted`。
|
|
88
81
|
|
package/lib/client.js
CHANGED
|
@@ -417,6 +417,197 @@ function PreciseCacheHitEntry({ useProjection }) {
|
|
|
417
417
|
return null;
|
|
418
418
|
}
|
|
419
419
|
};
|
|
420
|
+
__modules["./effort.js"] = function(module, exports, require, __load_) {
|
|
421
|
+
"use strict";
|
|
422
|
+
/**
|
|
423
|
+
* dsh-ui-tweaks — thinking-effort tint for the composer model seat (browser half).
|
|
424
|
+
*
|
|
425
|
+
* Tags every rendered thinking-effort label with its intensity band, so the
|
|
426
|
+
* poster skin can paint it in a solid band color (Low green, Medium amber,
|
|
427
|
+
* High blue, Max Codex-violet), and wash the hovered option row with a
|
|
428
|
+
* left-to-right gradient in the same hue. Three surfaces are covered — the
|
|
429
|
+
* `model · effort` trigger in `conversation.input.model`, the Effort cell
|
|
430
|
+
* value in its dropdown menu root, and every option row in the menu's
|
|
431
|
+
* effort pane (label span plus row button, the button carrying the band for
|
|
432
|
+
* the background wash). Off / provider-Default / unrecognized names stay
|
|
433
|
+
* the stock caption gray — a level we cannot place gets no color rather
|
|
434
|
+
* than a wrong one; extend BAND_WORDS when a new adapter ships new names.
|
|
435
|
+
*
|
|
436
|
+
* - Effort levels are adapter-owned: DeepSeek advertises Off/Low/High/Max,
|
|
437
|
+
* pi-ai-backed providers offer off/minimal/low/medium/high/xhigh/max
|
|
438
|
+
* (shown capitalized), others e.g. Standard. Classification reads the
|
|
439
|
+
* rendered label text, never a fixed id vocabulary.
|
|
440
|
+
* - The trigger renders model-name and effort spans in order (the chevron is
|
|
441
|
+
* an svg), so the trailing span is the effort label when present. The menu
|
|
442
|
+
* is portaled to document.body, outside the seat: each trigger's
|
|
443
|
+
* `aria-controls` names its menu element, which is how the watcher reaches
|
|
444
|
+
* it. Inside the menu, radios beside group sections are the model list
|
|
445
|
+
* (left alone); radios without sections are effort options.
|
|
446
|
+
* - One MutationObserver re-tags on React repaints (model switch, effort
|
|
447
|
+
* change, session switch, menu open); writes are idempotent — an attribute
|
|
448
|
+
* is only touched when it actually changes — so the loop settles after one
|
|
449
|
+
* pass. Removed nodes take their tags with them; cleanup clears the rest.
|
|
450
|
+
* - Mounted only while the neon-poster theme is active (see the effect in
|
|
451
|
+
* index.tsx): other themes have no rules for the tag and stay stock.
|
|
452
|
+
* @module dsh-ui-tweaks/client/effort
|
|
453
|
+
*/
|
|
454
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
455
|
+
exports.classifyEffort = classifyEffort;
|
|
456
|
+
exports.installEffortTag = installEffortTag;
|
|
457
|
+
/**
|
|
458
|
+
* Rendered effort names per band, matched case-insensitively after trimming.
|
|
459
|
+
* DeepSeek advertises Off/Low/High/Max; pi-ai-backed providers offer
|
|
460
|
+
* off/minimal/low/medium/high/xhigh/max (shown capitalized); others e.g.
|
|
461
|
+
* Standard. Adapter names are English in practice; the Chinese entries cover
|
|
462
|
+
* adapters that localize their level names.
|
|
463
|
+
*/
|
|
464
|
+
const BAND_WORDS = {
|
|
465
|
+
low: ['low', 'minimal', 'mini', 'eco', 'fast', '低', '快速'],
|
|
466
|
+
medium: ['medium', 'standard', 'balanced', 'moderate', '中', '标准'],
|
|
467
|
+
high: ['high', 'deep', 'advanced', '高', '深度', '强'],
|
|
468
|
+
max: ['max', 'maximum', 'ultra', 'extreme', 'highest', 'xhigh', '最高', '最强'],
|
|
469
|
+
};
|
|
470
|
+
/**
|
|
471
|
+
* Names that state no intensity: the provider default (whose real level is
|
|
472
|
+
* unknown to the viewer), an automatic choice, or thinking turned off.
|
|
473
|
+
*/
|
|
474
|
+
const NEUTRAL_WORDS = [
|
|
475
|
+
'default',
|
|
476
|
+
'provider default',
|
|
477
|
+
'auto',
|
|
478
|
+
'off',
|
|
479
|
+
'none',
|
|
480
|
+
'disabled',
|
|
481
|
+
'默认',
|
|
482
|
+
'自动',
|
|
483
|
+
'关闭',
|
|
484
|
+
'无',
|
|
485
|
+
];
|
|
486
|
+
/**
|
|
487
|
+
* Place a rendered effort label on the intensity scale.
|
|
488
|
+
* @param name - the effort label as shown (e.g. 'High').
|
|
489
|
+
* @returns its band, or null to leave the stock caption untouched.
|
|
490
|
+
*/
|
|
491
|
+
function classifyEffort(name) {
|
|
492
|
+
const normalized = name.trim().toLowerCase();
|
|
493
|
+
if (normalized === '')
|
|
494
|
+
return null;
|
|
495
|
+
for (const word of NEUTRAL_WORDS) {
|
|
496
|
+
if (normalized === word)
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
for (const [band, words] of Object.entries(BAND_WORDS)) {
|
|
500
|
+
if (words.includes(normalized))
|
|
501
|
+
return band;
|
|
502
|
+
}
|
|
503
|
+
return null;
|
|
504
|
+
}
|
|
505
|
+
/** Attribute the tagger sets on effort label spans; the skin reads it. */
|
|
506
|
+
const EFFORT_ATTR = 'data-dut-effort';
|
|
507
|
+
/** Model triggers live in the input's model seat, wherever it is mounted. */
|
|
508
|
+
const MODEL_TRIGGER_SELECTOR = 'div[data-slot="conversation.input.model"] button';
|
|
509
|
+
/** Root-cell headings that name the effort row, in either shipped locale. */
|
|
510
|
+
const EFFORT_CELL_LABELS = new Set(['推理等级', 'Effort']);
|
|
511
|
+
/**
|
|
512
|
+
* Tag (or untag) one label span; idempotent — untouched when already correct.
|
|
513
|
+
* @param el - the label span, if the structure offered one.
|
|
514
|
+
* @param present - whether this span is an effort label.
|
|
515
|
+
*/
|
|
516
|
+
function tagLabel(el, band) {
|
|
517
|
+
if (!(el instanceof HTMLElement))
|
|
518
|
+
return;
|
|
519
|
+
if (band === null) {
|
|
520
|
+
if (el.hasAttribute(EFFORT_ATTR))
|
|
521
|
+
el.removeAttribute(EFFORT_ATTR);
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
if (el.getAttribute(EFFORT_ATTR) !== band)
|
|
525
|
+
el.setAttribute(EFFORT_ATTR, band);
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Tag a model trigger's effort span: the trailing direct-child span, present
|
|
529
|
+
* only while the model advertises reasoning levels.
|
|
530
|
+
*/
|
|
531
|
+
function applyToTrigger(button) {
|
|
532
|
+
const spans = button.querySelectorAll(':scope > span');
|
|
533
|
+
for (const span of spans) {
|
|
534
|
+
const isEffort = spans.length >= 2 && span === spans[spans.length - 1];
|
|
535
|
+
tagLabel(span, isEffort ? classifyEffort(span.textContent?.trim() ?? '') : null);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Tag one open model menu, reached through its trigger's aria-controls.
|
|
540
|
+
* Radios beside group sections are the model list (left alone); radios
|
|
541
|
+
* without sections are effort options; the root pane's Effort cell value
|
|
542
|
+
* takes the current effort's band.
|
|
543
|
+
*/
|
|
544
|
+
function applyToMenu(menu) {
|
|
545
|
+
const radios = menu.querySelectorAll('button[role="menuitemradio"]');
|
|
546
|
+
const hasGroups = menu.querySelector('section[role="group"]') !== null;
|
|
547
|
+
for (const radio of radios) {
|
|
548
|
+
if (!(radio instanceof HTMLButtonElement))
|
|
549
|
+
continue;
|
|
550
|
+
if (hasGroups) {
|
|
551
|
+
radio.removeAttribute(EFFORT_ATTR);
|
|
552
|
+
for (const tagged of radio.querySelectorAll(`span[${EFFORT_ATTR}]`)) {
|
|
553
|
+
tagged.removeAttribute(EFFORT_ATTR);
|
|
554
|
+
}
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
const copy = radio.querySelector(':scope > span');
|
|
558
|
+
const band = classifyEffort(copy?.textContent?.trim() ?? '');
|
|
559
|
+
tagLabel(copy, band);
|
|
560
|
+
// The row button carries the same band so the skin can wash its
|
|
561
|
+
// background on hover without :has selectors.
|
|
562
|
+
if (band === null) {
|
|
563
|
+
radio.removeAttribute(EFFORT_ATTR);
|
|
564
|
+
}
|
|
565
|
+
else if (radio.getAttribute(EFFORT_ATTR) !== band) {
|
|
566
|
+
radio.setAttribute(EFFORT_ATTR, band);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
for (const cell of menu.querySelectorAll('button[role="menuitem"]')) {
|
|
570
|
+
if (!(cell instanceof HTMLButtonElement))
|
|
571
|
+
continue;
|
|
572
|
+
const spans = cell.querySelectorAll(':scope > span');
|
|
573
|
+
if (spans.length < 2)
|
|
574
|
+
continue;
|
|
575
|
+
const head = spans[0];
|
|
576
|
+
const value = spans[spans.length - 1] ?? null;
|
|
577
|
+
const isEffortCell = EFFORT_CELL_LABELS.has(head?.textContent?.trim() ?? '');
|
|
578
|
+
tagLabel(value, isEffortCell ? classifyEffort(value?.textContent?.trim() ?? '') : null);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Watch every composer model trigger (and its open dropdown menu) and keep
|
|
583
|
+
* the effort band tags current.
|
|
584
|
+
* @returns cleanup: stops the observer and removes all tags set here.
|
|
585
|
+
*/
|
|
586
|
+
function installEffortTag() {
|
|
587
|
+
const apply = () => {
|
|
588
|
+
for (const button of document.querySelectorAll(MODEL_TRIGGER_SELECTOR)) {
|
|
589
|
+
applyToTrigger(button);
|
|
590
|
+
// The menu is portaled to document.body, outside the seat: the
|
|
591
|
+
// trigger's aria-controls names its element while open.
|
|
592
|
+
const menuId = button.getAttribute('aria-controls');
|
|
593
|
+
const menu = menuId === null ? null : document.getElementById(menuId);
|
|
594
|
+
if (menu !== null)
|
|
595
|
+
applyToMenu(menu);
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
apply();
|
|
599
|
+
// React owns the trigger: every repaint re-triggers us, and the idempotent
|
|
600
|
+
// writes settle after a single extra pass.
|
|
601
|
+
const observer = new MutationObserver(apply);
|
|
602
|
+
observer.observe(document.body, { childList: true, characterData: true, subtree: true });
|
|
603
|
+
return () => {
|
|
604
|
+
observer.disconnect();
|
|
605
|
+
for (const tagged of document.querySelectorAll(`[${EFFORT_ATTR}]`)) {
|
|
606
|
+
tagged.removeAttribute(EFFORT_ATTR);
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
};
|
|
420
611
|
__modules["./gitbar.js"] = function(module, exports, require, __load_) {
|
|
421
612
|
"use strict";
|
|
422
613
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -2418,6 +2609,7 @@ const mcp_tsx_1 = __load_("./mcp.js");
|
|
|
2418
2609
|
const search_tsx_1 = __load_("./search.js");
|
|
2419
2610
|
const timeline_tsx_1 = __load_("./timeline.js");
|
|
2420
2611
|
const cachehit_tsx_1 = __load_("./cachehit.js");
|
|
2612
|
+
const effort_tsx_1 = __load_("./effort.js");
|
|
2421
2613
|
const notifier_ts_1 = __load_("./notifier.js");
|
|
2422
2614
|
const NS = 'ui-tweaks';
|
|
2423
2615
|
const SETTINGS_ROUTE = '/_dsh/ui-tweaks/settings';
|
|
@@ -2649,6 +2841,8 @@ const en = {
|
|
|
2649
2841
|
includeFile: 'Include in commit',
|
|
2650
2842
|
excludeFile: 'Exclude from commit',
|
|
2651
2843
|
terminal: 'Terminal',
|
|
2844
|
+
terminalGuide: 'A persistent shell in the session working directory.',
|
|
2845
|
+
diffGuide: 'Review working-tree changes and commit.',
|
|
2652
2846
|
notRepo: 'Not a git repository.',
|
|
2653
2847
|
termConnecting: 'connecting…',
|
|
2654
2848
|
termExited: 'shell exited',
|
|
@@ -2883,6 +3077,8 @@ const zh = {
|
|
|
2883
3077
|
includeFile: '提交包含此文件',
|
|
2884
3078
|
excludeFile: '提交排除此文件',
|
|
2885
3079
|
terminal: '终端',
|
|
3080
|
+
terminalGuide: '在会话工作目录中打开常驻 shell。',
|
|
3081
|
+
diffGuide: '查看工作区改动并提交。',
|
|
2886
3082
|
notRepo: '当前目录不是 git 仓库。',
|
|
2887
3083
|
termConnecting: '连接中…',
|
|
2888
3084
|
termExited: 'shell 已退出',
|
|
@@ -3205,6 +3401,68 @@ div[data-composer-card]{
|
|
|
3205
3401
|
(scoped to the composer's svg so the class-substring match stays safe) */
|
|
3206
3402
|
div[data-composer-card] svg [class*="track"]{stroke:var(--dut-scroll-1)}
|
|
3207
3403
|
div[data-composer-card] svg [class*="fill"]{stroke:var(--dut-accent)}
|
|
3404
|
+
/* composer model seat and its dropdown menu: thinking-effort labels in solid
|
|
3405
|
+
band colors (Low green, Medium amber, High blue, Max Codex-violet).
|
|
3406
|
+
The effort watcher tags the label spans data-dut-effort — the trigger
|
|
3407
|
+
effort span, the menu root Effort cell value, and every effort-option
|
|
3408
|
+
label — while option row buttons carry the same band for the hover wash;
|
|
3409
|
+
Off/Default stay untagged in the stock caption. One plain span selector
|
|
3410
|
+
covers all three label surfaces — the attribute only exists where the
|
|
3411
|
+
watcher set it, so no hashed module class is touched. */
|
|
3412
|
+
[data-slot="conversation.input.model"] span[data-dut-effort],
|
|
3413
|
+
div[role="menu"] span[data-dut-effort]{font-weight:600}
|
|
3414
|
+
[data-slot="conversation.input.model"] span[data-dut-effort="low"],
|
|
3415
|
+
div[role="menu"] span[data-dut-effort="low"]{color:#15803d}
|
|
3416
|
+
[data-slot="conversation.input.model"] span[data-dut-effort="medium"],
|
|
3417
|
+
div[role="menu"] span[data-dut-effort="medium"]{color:#b45309}
|
|
3418
|
+
[data-slot="conversation.input.model"] span[data-dut-effort="high"],
|
|
3419
|
+
div[role="menu"] span[data-dut-effort="high"]{color:#1d4ed8}
|
|
3420
|
+
[data-slot="conversation.input.model"] span[data-dut-effort="max"],
|
|
3421
|
+
div[role="menu"] span[data-dut-effort="max"]{color:#7c3aed}
|
|
3422
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] [data-slot="conversation.input.model"] span[data-dut-effort="low"],
|
|
3423
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] div[role="menu"] span[data-dut-effort="low"]{color:#4ade80}
|
|
3424
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] [data-slot="conversation.input.model"] span[data-dut-effort="medium"],
|
|
3425
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] div[role="menu"] span[data-dut-effort="medium"]{color:#fbbf24}
|
|
3426
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] [data-slot="conversation.input.model"] span[data-dut-effort="high"],
|
|
3427
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] div[role="menu"] span[data-dut-effort="high"]{color:#60a5fa}
|
|
3428
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] [data-slot="conversation.input.model"] span[data-dut-effort="max"],
|
|
3429
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] div[role="menu"] span[data-dut-effort="max"]{color:#a78bfa}
|
|
3430
|
+
/* Trigger chip: the input-box effort label additionally rides a translucent
|
|
3431
|
+
pill in its band hue (menu labels stay plain text). Padding/radius work
|
|
3432
|
+
as-is — the trigger is flex, so its spans are already blockified. */
|
|
3433
|
+
[data-slot="conversation.input.model"] span[data-dut-effort]{
|
|
3434
|
+
padding:1px 7px;
|
|
3435
|
+
border-radius:999px;
|
|
3436
|
+
}
|
|
3437
|
+
[data-slot="conversation.input.model"] span[data-dut-effort="low"]{background:rgba(21,128,61,.12)}
|
|
3438
|
+
[data-slot="conversation.input.model"] span[data-dut-effort="medium"]{background:rgba(180,83,9,.12)}
|
|
3439
|
+
[data-slot="conversation.input.model"] span[data-dut-effort="high"]{background:rgba(29,78,216,.10)}
|
|
3440
|
+
[data-slot="conversation.input.model"] span[data-dut-effort="max"]{background:rgba(124,58,237,.12)}
|
|
3441
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] [data-slot="conversation.input.model"] span[data-dut-effort="low"]{background:rgba(74,222,128,.18)}
|
|
3442
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] [data-slot="conversation.input.model"] span[data-dut-effort="medium"]{background:rgba(251,191,36,.18)}
|
|
3443
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] [data-slot="conversation.input.model"] span[data-dut-effort="high"]{background:rgba(96,165,250,.18)}
|
|
3444
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] [data-slot="conversation.input.model"] span[data-dut-effort="max"]{background:rgba(167,139,250,.20)}
|
|
3445
|
+
/* Effort option rows: on hover the row background wipes in from the left in
|
|
3446
|
+
the band hue (translucent wash into transparency), overriding the stock
|
|
3447
|
+
row hover fill — same specificity contest the skin always wins by naming
|
|
3448
|
+
the attribute. background-size animates reliably (no background-clip text
|
|
3449
|
+
involved); honoring prefers-reduced-motion. */
|
|
3450
|
+
div[role="menu"] button[data-dut-effort]{
|
|
3451
|
+
background-repeat:no-repeat;
|
|
3452
|
+
background-size:0% 100%;
|
|
3453
|
+
transition:background-size .6s ease;
|
|
3454
|
+
}
|
|
3455
|
+
div[role="menu"] button[data-dut-effort="low"]:hover{background-image:linear-gradient(90deg,rgba(21,128,61,.16),rgba(21,128,61,0));background-size:100% 100%}
|
|
3456
|
+
div[role="menu"] button[data-dut-effort="medium"]:hover{background-image:linear-gradient(90deg,rgba(180,83,9,.16),rgba(180,83,9,0));background-size:100% 100%}
|
|
3457
|
+
div[role="menu"] button[data-dut-effort="high"]:hover{background-image:linear-gradient(90deg,rgba(29,78,216,.14),rgba(29,78,216,0));background-size:100% 100%}
|
|
3458
|
+
div[role="menu"] button[data-dut-effort="max"]:hover{background-image:linear-gradient(90deg,rgba(124,58,237,.16),rgba(124,58,237,0));background-size:100% 100%}
|
|
3459
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] div[role="menu"] button[data-dut-effort="low"]:hover{background-image:linear-gradient(90deg,rgba(74,222,128,.18),rgba(74,222,128,0));background-size:100% 100%}
|
|
3460
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] div[role="menu"] button[data-dut-effort="medium"]:hover{background-image:linear-gradient(90deg,rgba(251,191,36,.18),rgba(251,191,36,0));background-size:100% 100%}
|
|
3461
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] div[role="menu"] button[data-dut-effort="high"]:hover{background-image:linear-gradient(90deg,rgba(96,165,250,.20),rgba(96,165,250,0));background-size:100% 100%}
|
|
3462
|
+
body:not(#dsh-ui-tweaks-theme-scope)[data-ds-dark-theme] div[role="menu"] button[data-dut-effort="max"]:hover{background-image:linear-gradient(90deg,rgba(167,139,250,.20),rgba(167,139,250,0));background-size:100% 100%}
|
|
3463
|
+
@media (prefers-reduced-motion:reduce){
|
|
3464
|
+
div[role="menu"] button[data-dut-effort]{transition:none}
|
|
3465
|
+
}
|
|
3208
3466
|
/* sidebar new-session button: lime sticker. Matched by its CSS-module class,
|
|
3209
3467
|
NOT the aria-label — the brand row button shares the same "新建会话"
|
|
3210
3468
|
label (it doubles as the new-session shortcut) and must stay plain. */
|
|
@@ -3275,7 +3533,7 @@ function runtimeStyleElement() {
|
|
|
3275
3533
|
const BASE_CSS = `
|
|
3276
3534
|
.dut-settings{display:grid;gap:8px;max-width:680px;padding:4px 2px 24px;color:var(--dsw-alias-label-primary)}
|
|
3277
3535
|
.dut-settings-header{display:flex;align-items:flex-start;gap:10px;padding:2px 2px 0}
|
|
3278
|
-
.dut-logo{flex:none;display:grid;place-items:center;width:30px;height:30px;border-radius:9px;border:1px solid var(--dsw-alias-border-l1);background:linear-gradient(135deg,color-mix(in srgb,var(--dsw-alias-state-business-primary) 16%,transparent),transparent);font-size:15px;line-height:1}
|
|
3536
|
+
.dut-logo{flex:none;display:grid;place-items:center;width:30px;height:30px;border-radius:9px;border:1px solid var(--dsw-alias-border-l1);background-image:linear-gradient(135deg,color-mix(in srgb,var(--dsw-alias-state-business-primary) 16%,transparent),transparent);font-size:15px;line-height:1}
|
|
3279
3537
|
.dut-settings-header h2{font-size:16px;letter-spacing:-.01em;margin:0 0 2px}
|
|
3280
3538
|
.dut-settings-header p{max-width:600px;margin:0;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:1.45}
|
|
3281
3539
|
.dut-panel{display:grid;gap:0;border:1px solid var(--dsw-alias-border-l1);border-radius:14px;background:var(--dsw-alias-bg-layer-1);box-shadow:var(--dsw-shadow-lv1);overflow:hidden}
|
|
@@ -3732,7 +3990,8 @@ function apply(ctx) {
|
|
|
3732
3990
|
// picking one opens the tab in the sidebar), registered only while the
|
|
3733
3991
|
// gitBarEnabled toggle in the UI Tweaks section is on. Each tab is a page
|
|
3734
3992
|
// type (no address patterns): stage one registers the type with its guide
|
|
3735
|
-
// capsule
|
|
3993
|
+
// capsule (description shows while the guide lists ≤4 entries), stage two
|
|
3994
|
+
// the body under the type's id. The terminal reattaches to
|
|
3736
3995
|
// its persistent host shell on every visit, and the diff tab keeps its
|
|
3737
3996
|
// commit band. The tab registry is resolved lazily: hosts predating the
|
|
3738
3997
|
// right sidebar simply skip these tabs while everything else keeps working.
|
|
@@ -3754,7 +4013,7 @@ function apply(ctx) {
|
|
|
3754
4013
|
id: 'dsh-ui-tweaks/terminal',
|
|
3755
4014
|
kind: 'ui-tweaks-terminal',
|
|
3756
4015
|
title: () => t('terminal'),
|
|
3757
|
-
guide: [{ order: 20, title: () => t('terminal'), icon: icons_tsx_1.TerminalIcon }],
|
|
4016
|
+
guide: [{ order: 20, title: () => t('terminal'), description: () => t('terminalGuide'), icon: icons_tsx_1.TerminalIcon }],
|
|
3758
4017
|
}));
|
|
3759
4018
|
disposers.push(ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({
|
|
3760
4019
|
name: 'sidebar.right.pane.tab',
|
|
@@ -3766,7 +4025,7 @@ function apply(ctx) {
|
|
|
3766
4025
|
id: 'dsh-ui-tweaks/diff',
|
|
3767
4026
|
kind: 'ui-tweaks-diff',
|
|
3768
4027
|
title: () => t('diffView'),
|
|
3769
|
-
guide: [{ order: 30, title: () => t('diffView'), icon: icons_tsx_1.DiffIcon }],
|
|
4028
|
+
guide: [{ order: 30, title: () => t('diffView'), description: () => t('diffGuide'), icon: icons_tsx_1.DiffIcon }],
|
|
3770
4029
|
}));
|
|
3771
4030
|
disposers.push(ctx.slots.inject('sidebar.right.pane.tab', () => ctx.slots.register({
|
|
3772
4031
|
name: 'sidebar.right.pane.tab',
|
|
@@ -3927,6 +4186,31 @@ function apply(ctx) {
|
|
|
3927
4186
|
disposeEntry = undefined;
|
|
3928
4187
|
};
|
|
3929
4188
|
}, 'dsh-ui-tweaks: precise cache hit');
|
|
4189
|
+
// Thinking-effort tint: tags thinking-effort labels — the composer
|
|
4190
|
+
// trigger, the menu root Effort cell and the menu effort options — so the
|
|
4191
|
+
// poster skin can paint solid band colors plus a hover background wash.
|
|
4192
|
+
// Mounted only while the neon-poster theme is active — other themes keep
|
|
4193
|
+
// the stock caption, and leaving the theme removes every tag.
|
|
4194
|
+
ctx.effect(() => {
|
|
4195
|
+
let disposeTag;
|
|
4196
|
+
const sync = () => {
|
|
4197
|
+
const poster = controller.getSnapshot().value?.themeStyle === 'neon-lime';
|
|
4198
|
+
if (poster && disposeTag === undefined) {
|
|
4199
|
+
disposeTag = (0, effort_tsx_1.installEffortTag)();
|
|
4200
|
+
}
|
|
4201
|
+
else if (!poster && disposeTag !== undefined) {
|
|
4202
|
+
disposeTag();
|
|
4203
|
+
disposeTag = undefined;
|
|
4204
|
+
}
|
|
4205
|
+
};
|
|
4206
|
+
sync();
|
|
4207
|
+
const unsubscribe = controller.subscribe(sync);
|
|
4208
|
+
return () => {
|
|
4209
|
+
unsubscribe();
|
|
4210
|
+
disposeTag?.();
|
|
4211
|
+
disposeTag = undefined;
|
|
4212
|
+
};
|
|
4213
|
+
}, 'dsh-ui-tweaks: effort tint');
|
|
3930
4214
|
// Task notifications: watch every session on the list feed and raise
|
|
3931
4215
|
// tab-title / system-notification / chime alerts when one finishes its turn
|
|
3932
4216
|
// or starts blocking on the user. Registered only while the master toggle
|