dsh-tui-theme 0.3.1 → 0.3.2
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.md +121 -120
- package/cordis.patch.yml +16 -16
- package/lib/types/autoTheme.d.ts +12 -2
- package/lib/types/autoTheme.d.ts.map +1 -1
- package/lib/types/autoTheme.js +58 -9
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +7 -2
- package/lib/types/settingsSection.d.ts.map +1 -1
- package/lib/types/settingsSection.js +30 -23
- package/lib/types/statusLine.d.ts.map +1 -1
- package/lib/types/statusLine.js +4 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,120 +1,121 @@
|
|
|
1
|
-
# dsh-tui-theme 🌸
|
|
2
|
-
|
|
3
|
-
[dsh-TUI](https://github.com/ccch1mneyyy/dsh-TUI) 的樱花粉主题插件。一个包带来四种个性化,全部走官方接缝:
|
|
4
|
-
|
|
5
|
-
| 个性化 | 接缝 | 说明 |
|
|
6
|
-
| --- | --- | --- |
|
|
7
|
-
| **三套粉色主题** | 主题(静态资产) | `pink-night` 夜樱 / `pink-day` 昼樱 / `pink-ansi` 樱·ANSI,启动时自动装进 `~/.dsh-tui/themes/` |
|
|
8
|
-
| **自动跟随背景** | 启动时序 | 检测终端/系统背景色(OSC 11,与宿主同阈值),在昼樱/夜樱间自动切换——pink 版的 auto |
|
|
9
|
-
| **花符状态行** | `tuiStatus` | 输入框上方一行小装饰:✿ · 时钟 · 实时轮数(默认仅粉主题下显示) |
|
|
10
|
-
| **设置面板** | `tuiSettingsSections` | `/settings` 里一个可编辑区块,改完即时生效 |
|
|
11
|
-
|
|
12
|
-
**明确不做的事**:不注册快捷键、不注册/修改任何命令、不拦截输入、不追加会话事件、不注入 system prompt。卸载即无痕(可选删除主题文件)。
|
|
13
|
-
|
|
14
|
-
## 主题预览
|
|
15
|
-
|
|
16
|
-
| 主题 | 基底 | 风格 |
|
|
17
|
-
| --- | --- | --- |
|
|
18
|
-
| `pink-night` 夜樱 | dark | 深梅紫底、玫瑰粉强调、粉鲸鱼吉祥物,96 键全覆盖 |
|
|
19
|
-
| `pink-day` 昼樱 | light | 象牙粉底、墨梅正文、柔和玫瑰强调(已通过宿主浅色身份判定) |
|
|
20
|
-
| `pink-ansi` 樱·ANSI | dark-ansi | 16 色 ANSI 回退,品牌色映射到 magenta 系 |
|
|
21
|
-
|
|
22
|
-
三套均通过 dsh-TUI 官方校验器(零警告、全键覆盖)与 WCAG 对比度检查(正文 ≥ 11:1)。
|
|
23
|
-
|
|
24
|
-
> 小知识:`pink-day` 的 `text` 写成 `rgb(61,43,51)` 而非 hex——宿主按 `text` 墨色亮度自动判定主题深浅,且只认 `rgb()` 格式。
|
|
25
|
-
|
|
26
|
-
## 截图
|
|
27
|
-
|
|
28
|
-
实测于 dsh-tui 0.9.0:
|
|
29
|
-
|
|
30
|
-
| 昼樱 `pink-day` | 夜樱 `pink-night` |
|
|
31
|
-
| :---: | :---: |
|
|
32
|
-
|  |  |
|
|
33
|
-
|
|
34
|
-
`/settings` 里的 pink-theme 区块(跟随终端背景 / 花符 / 时钟 / 轮数,保存即时生效):
|
|
35
|
-
|
|
36
|
-

|
|
37
|
-
|
|
38
|
-
> 图中底栏上下文进度条的蓝色分段与 ❯ 提示符是宿主硬编码的,见下文[宿主限制](#受宿主限制目前无法定制的部分)。
|
|
39
|
-
|
|
40
|
-
## 自动跟随终端背景
|
|
41
|
-
|
|
42
|
-
设置里的“跟随终端背景”开启后(本部署默认开启):
|
|
43
|
-
|
|
44
|
-
- 每次启动检测终端背景(跟随系统主题的终端会随系统深浅变化),亮 → `pink-day`,暗 → `pink-night`,写入 `~/.dsh-tui/theme.json`,与宿主 `auto` 伪主题同款行为、同一亮度阈值;
|
|
45
|
-
- 检测结果缓存在 `~/.dsh-tui/theme-follow.json`,因此切换在**启动瞬间**就生效;首次开启或系统刚翻转后的那次启动可能滞后一拍(与宿主 auto “重新选择或重启后跟上”一致);
|
|
46
|
-
- 开启此功能后 `/theme` 的选择由插件接管(每次启动都会按背景覆写);关闭它即可恢复手动选择;
|
|
47
|
-
- `DSH_TUI_THEME` 环境变量仍然最优先(宿主行为,插件不覆盖环境变量)。
|
|
48
|
-
|
|
49
|
-
## 安装
|
|
50
|
-
|
|
51
|
-
```sh
|
|
52
|
-
# 方式一:从 npm(已发布)
|
|
53
|
-
dsh plugin --profile dsh-tui add -w dsh-tui-theme
|
|
54
|
-
|
|
55
|
-
# 方式二:从 GitHub
|
|
56
|
-
dsh plugin --profile dsh-tui add -w github:xiaoxiaohaigui/dsh-tui-theme
|
|
57
|
-
|
|
58
|
-
# 方式三:本地路径(开发/自用)
|
|
59
|
-
dsh plugin --profile dsh-tui add -w /path/to/dsh-tui-theme
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
重启 dsh-TUI 后插件自动把三套主题复制进 `~/.dsh-tui/themes/`(**仅缺失时复制,绝不覆盖你已有的同名文件**),然后:
|
|
63
|
-
|
|
64
|
-
```sh
|
|
65
|
-
# 在 dsh-TUI 里
|
|
66
|
-
/theme # 选择器:夜樱 / 昼樱 / 樱·ANSI
|
|
67
|
-
/theme pink-night # 或直接切换(开启跟随后由插件接管)
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## 配置
|
|
71
|
-
|
|
72
|
-
配置有三层,优先级:`/settings` 用户层 > `cordis.yml` 配置层 > 内置默认值。
|
|
73
|
-
|
|
74
|
-
`/settings` 里找到 **pink-theme** 区块即可编辑:
|
|
75
|
-
|
|
76
|
-
| 字段 | 默认 | 说明 |
|
|
77
|
-
| --- | --- | --- |
|
|
78
|
-
| `followSystem` | 部署默认开 | 跟随终端背景:昼樱 ↔ 夜樱自动切换 |
|
|
79
|
-
| `showGlyph` | `true` | 花符:开 = ✿ 开头,关 = 不显示 |
|
|
80
|
-
| `showClock` | `true` | 显示 HH:MM 时钟 |
|
|
81
|
-
| `showTurns` | `true` | 显示当前会话轮数(`N✦`,自本次启动起计) |
|
|
82
|
-
| `statusScope` | `pink-only` | 状态行展示:`pink-only` 仅樱花粉主题 / `all-themes` 所有主题 |
|
|
83
|
-
|
|
84
|
-
三项装饰全关时状态行整体消失。另有仅 profile 层的开关(`cordis.patch.yml`,不出现在 /settings):`autoInstallThemes`、`statusEnabled`。
|
|
85
|
-
|
|
86
|
-
## 受宿主限制、目前无法定制的部分
|
|
87
|
-
|
|
88
|
-
以下元素的颜色/形态由 dsh-TUI 宿主**硬编码**,不读取任何主题键,主题 JSON 与插件接缝都覆盖不到(dsh-TUI 0.9.0 实测):
|
|
89
|
-
|
|
90
|
-
| 元素 | 现状 | 位置(宿主源码) |
|
|
91
|
-
| --- | --- | --- |
|
|
92
|
-
| 输入框 ❯ 提示符 | 默认态无颜色参数(终端默认前景色,模型工作时变暗);最高推理档充能动画用**写死的蓝色 ramp**(深色端 `#82B9FF` / 浅色端 `#1E5FEB`) | `EffortChargeGlyph.tsx`、`trajectory/effortIgnition.ts` |
|
|
93
|
-
| 底栏上下文进度条分段色 | system / prompt / assistant / thinking / tools 五段为**写死的藏青→品牌蓝系**(`#22305F`→`#5A7CFF`),永远不随主题变化 | `screens/StatusMetrics.ts` |
|
|
94
|
-
| 进度条空余段配色 | 宿主按 `themeName === 'light'` **字符串比较**取浅色配色——自定义浅色主题(如 pink-day)不等于 `'light'`,会拿到深色空余段,在浅色终端上偏深 | `screens/StatusLine.tsx` |
|
|
95
|
-
| 状态行文字颜色 | 插件状态行(tuiStatus)由宿主统一以**无色 + 终端 dim** 渲染,插件无法指定颜色(✿ 行因此继承终端默认前景色) | `screens/Chat.tsx` |
|
|
96
|
-
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
rm ~/.dsh-tui/
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
npm
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
-
|
|
1
|
+
# dsh-tui-theme 🌸
|
|
2
|
+
|
|
3
|
+
[dsh-TUI](https://github.com/ccch1mneyyy/dsh-TUI) 的樱花粉主题插件。一个包带来四种个性化,全部走官方接缝:
|
|
4
|
+
|
|
5
|
+
| 个性化 | 接缝 | 说明 |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| **三套粉色主题** | 主题(静态资产) | `pink-night` 夜樱 / `pink-day` 昼樱 / `pink-ansi` 樱·ANSI,启动时自动装进 `~/.dsh-tui/themes/` |
|
|
8
|
+
| **自动跟随背景** | 启动时序 | 检测终端/系统背景色(OSC 11,与宿主同阈值),在昼樱/夜樱间自动切换——pink 版的 auto |
|
|
9
|
+
| **花符状态行** | `tuiStatus` | 输入框上方一行小装饰:✿ · 时钟 · 实时轮数(默认仅粉主题下显示) |
|
|
10
|
+
| **设置面板** | `tuiSettingsSections` | `/settings` 里一个可编辑区块,改完即时生效 |
|
|
11
|
+
|
|
12
|
+
**明确不做的事**:不注册快捷键、不注册/修改任何命令、不拦截输入、不追加会话事件、不注入 system prompt。卸载即无痕(可选删除主题文件)。
|
|
13
|
+
|
|
14
|
+
## 主题预览
|
|
15
|
+
|
|
16
|
+
| 主题 | 基底 | 风格 |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| `pink-night` 夜樱 | dark | 深梅紫底、玫瑰粉强调、粉鲸鱼吉祥物,96 键全覆盖 |
|
|
19
|
+
| `pink-day` 昼樱 | light | 象牙粉底、墨梅正文、柔和玫瑰强调(已通过宿主浅色身份判定) |
|
|
20
|
+
| `pink-ansi` 樱·ANSI | dark-ansi | 16 色 ANSI 回退,品牌色映射到 magenta 系 |
|
|
21
|
+
|
|
22
|
+
三套均通过 dsh-TUI 官方校验器(零警告、全键覆盖)与 WCAG 对比度检查(正文 ≥ 11:1)。
|
|
23
|
+
|
|
24
|
+
> 小知识:`pink-day` 的 `text` 写成 `rgb(61,43,51)` 而非 hex——宿主按 `text` 墨色亮度自动判定主题深浅,且只认 `rgb()` 格式。
|
|
25
|
+
|
|
26
|
+
## 截图
|
|
27
|
+
|
|
28
|
+
实测于 dsh-tui 0.9.0:
|
|
29
|
+
|
|
30
|
+
| 昼樱 `pink-day` | 夜樱 `pink-night` |
|
|
31
|
+
| :---: | :---: |
|
|
32
|
+
|  |  |
|
|
33
|
+
|
|
34
|
+
`/settings` 里的 pink-theme 区块(跟随终端背景 / 花符 / 时钟 / 轮数,保存即时生效):
|
|
35
|
+
|
|
36
|
+

|
|
37
|
+
|
|
38
|
+
> 图中底栏上下文进度条的蓝色分段与 ❯ 提示符是宿主硬编码的,见下文[宿主限制](#受宿主限制目前无法定制的部分)。
|
|
39
|
+
|
|
40
|
+
## 自动跟随终端背景
|
|
41
|
+
|
|
42
|
+
设置里的“跟随终端背景”开启后(本部署默认开启):
|
|
43
|
+
|
|
44
|
+
- 每次启动检测终端背景(跟随系统主题的终端会随系统深浅变化),亮 → `pink-day`,暗 → `pink-night`,写入 `~/.dsh-tui/theme.json`,与宿主 `auto` 伪主题同款行为、同一亮度阈值;
|
|
45
|
+
- 检测结果缓存在 `~/.dsh-tui/theme-follow.json`,因此切换在**启动瞬间**就生效;首次开启或系统刚翻转后的那次启动可能滞后一拍(与宿主 auto “重新选择或重启后跟上”一致);
|
|
46
|
+
- 开启此功能后 `/theme` 的选择由插件接管(每次启动都会按背景覆写);关闭它即可恢复手动选择;
|
|
47
|
+
- `DSH_TUI_THEME` 环境变量仍然最优先(宿主行为,插件不覆盖环境变量)。
|
|
48
|
+
|
|
49
|
+
## 安装
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
# 方式一:从 npm(已发布)
|
|
53
|
+
dsh plugin --profile dsh-tui add -w dsh-tui-theme
|
|
54
|
+
|
|
55
|
+
# 方式二:从 GitHub
|
|
56
|
+
dsh plugin --profile dsh-tui add -w github:xiaoxiaohaigui/dsh-tui-theme
|
|
57
|
+
|
|
58
|
+
# 方式三:本地路径(开发/自用)
|
|
59
|
+
dsh plugin --profile dsh-tui add -w /path/to/dsh-tui-theme
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
重启 dsh-TUI 后插件自动把三套主题复制进 `~/.dsh-tui/themes/`(**仅缺失时复制,绝不覆盖你已有的同名文件**),然后:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
# 在 dsh-TUI 里
|
|
66
|
+
/theme # 选择器:夜樱 / 昼樱 / 樱·ANSI
|
|
67
|
+
/theme pink-night # 或直接切换(开启跟随后由插件接管)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 配置
|
|
71
|
+
|
|
72
|
+
配置有三层,优先级:`/settings` 用户层 > `cordis.yml` 配置层 > 内置默认值。
|
|
73
|
+
|
|
74
|
+
`/settings` 里找到 **pink-theme** 区块即可编辑:
|
|
75
|
+
|
|
76
|
+
| 字段 | 默认 | 说明 |
|
|
77
|
+
| --- | --- | --- |
|
|
78
|
+
| `followSystem` | 部署默认开 | 跟随终端背景:昼樱 ↔ 夜樱自动切换 |
|
|
79
|
+
| `showGlyph` | `true` | 花符:开 = ✿ 开头,关 = 不显示 |
|
|
80
|
+
| `showClock` | `true` | 显示 HH:MM 时钟 |
|
|
81
|
+
| `showTurns` | `true` | 显示当前会话轮数(`N✦`,自本次启动起计) |
|
|
82
|
+
| `statusScope` | `pink-only` | 状态行展示:`pink-only` 仅樱花粉主题 / `all-themes` 所有主题 |
|
|
83
|
+
|
|
84
|
+
三项装饰全关时状态行整体消失。另有仅 profile 层的开关(`cordis.patch.yml`,不出现在 /settings):`autoInstallThemes`、`statusEnabled`。
|
|
85
|
+
|
|
86
|
+
## 受宿主限制、目前无法定制的部分
|
|
87
|
+
|
|
88
|
+
以下元素的颜色/形态由 dsh-TUI 宿主**硬编码**,不读取任何主题键,主题 JSON 与插件接缝都覆盖不到(dsh-TUI 0.9.0 实测):
|
|
89
|
+
|
|
90
|
+
| 元素 | 现状 | 位置(宿主源码) |
|
|
91
|
+
| --- | --- | --- |
|
|
92
|
+
| 输入框 ❯ 提示符 | 默认态无颜色参数(终端默认前景色,模型工作时变暗);最高推理档充能动画用**写死的蓝色 ramp**(深色端 `#82B9FF` / 浅色端 `#1E5FEB`) | `EffortChargeGlyph.tsx`、`trajectory/effortIgnition.ts` |
|
|
93
|
+
| 底栏上下文进度条分段色 | system / prompt / assistant / thinking / tools 五段为**写死的藏青→品牌蓝系**(`#22305F`→`#5A7CFF`),永远不随主题变化 | `screens/StatusMetrics.ts` |
|
|
94
|
+
| 进度条空余段配色 | 宿主按 `themeName === 'light'` **字符串比较**取浅色配色——自定义浅色主题(如 pink-day)不等于 `'light'`,会拿到深色空余段,在浅色终端上偏深 | `screens/StatusLine.tsx` |
|
|
95
|
+
| 状态行文字颜色 | 插件状态行(tuiStatus)由宿主统一以**无色 + 终端 dim** 渲染,插件无法指定颜色(✿ 行因此继承终端默认前景色) | `screens/Chat.tsx` |
|
|
96
|
+
| 输入框块状光标 | 宿主挂载期间隐藏终端原生光标(`?25l`),输入框光标由应用以**反色字符**自绘(`<Text inverse>`),颜色即主题 text/background 的反色——OSC 12 光标色只能染到不可见的原生光标,插件无法给输入光标上色(辅助功能模式 `CLAUDE_CODE_ACCESSIBILITY=1` 下原生光标才可见) | `ink/components/App.tsx`、`components/PromptInput.tsx` |
|
|
97
|
+
| 主界面组件与布局 | 顶栏像素鲸鱼、工具卡、输入框等宿主组件不可被插件替换或改布局——平台规则(内建优先,无组件替换接缝);主题能碰的只有颜色层 | 宿主架构约定 |
|
|
98
|
+
|
|
99
|
+
这些都需要上游 dsh-TUI 修改(例如:把充能色/进度条分段色接入主题键、空余段判断改用 `isLightThemeActive()`、给输入光标增加主题键)。上游修复前,任何社区主题包都受同样约束。
|
|
100
|
+
|
|
101
|
+
## 卸载
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
dsh plugin --profile dsh-tui remove -w dsh-tui-theme # 移除插件
|
|
105
|
+
rm ~/.dsh-tui/themes/pink-{night,day,ansi}.json # 可选:删除主题文件
|
|
106
|
+
rm ~/.dsh-tui/theme-follow.json # 可选:删除跟随缓存
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## 开发
|
|
110
|
+
|
|
111
|
+
```sh
|
|
112
|
+
npm install && npm run build # tsc -> lib/types/
|
|
113
|
+
npm run verify # 沙盒测试(临时 HOME + 伪造 TTY,不碰真实 ~/.dsh-tui)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
主题调色板改起来最直接:编辑 `themes/*.json` 后重新 `npm run verify`,再删掉 `~/.dsh-tui/themes/` 下对应文件让插件重装。
|
|
117
|
+
|
|
118
|
+
## 兼容性
|
|
119
|
+
|
|
120
|
+
- **dsh-TUI 版本下限:0.8.8**(全功能,含状态行与设置面板;0.9.0 实测)。更旧的宿主缺 `dsh-tui-extensions` 扩展面时,插件自动降级为“仅安装三套主题 + 背景跟随”,不报错。
|
|
121
|
+
- Node `^22.19 || >=24`,纯 ESM,MIT。
|
package/cordis.patch.yml
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
# Bundle patch for dsh-tui-theme: insert the plugin as a profile row.
|
|
2
|
-
# The plugin only consumes optional seams (themes dir, tuiStatus,
|
|
3
|
-
# tuiSettingsSections, settings) and never registers shortcuts or commands.
|
|
4
|
-
# followSystem opts this deployment into background-follow (the schema
|
|
5
|
-
# default is off for generic installs).
|
|
6
|
-
- insert:
|
|
7
|
-
- id: dsh-tui-theme
|
|
8
|
-
name: 'dsh-tui-theme'
|
|
9
|
-
config:
|
|
10
|
-
autoInstallThemes: true
|
|
11
|
-
statusEnabled: true
|
|
12
|
-
followSystem: true
|
|
13
|
-
showGlyph: true
|
|
14
|
-
showClock: true
|
|
15
|
-
showTurns: true
|
|
16
|
-
statusScope: pink-only
|
|
1
|
+
# Bundle patch for dsh-tui-theme: insert the plugin as a profile row.
|
|
2
|
+
# The plugin only consumes optional seams (themes dir, tuiStatus,
|
|
3
|
+
# tuiSettingsSections, settings) and never registers shortcuts or commands.
|
|
4
|
+
# followSystem opts this deployment into background-follow (the schema
|
|
5
|
+
# default is off for generic installs).
|
|
6
|
+
- insert:
|
|
7
|
+
- id: dsh-tui-theme
|
|
8
|
+
name: 'dsh-tui-theme'
|
|
9
|
+
config:
|
|
10
|
+
autoInstallThemes: true
|
|
11
|
+
statusEnabled: true
|
|
12
|
+
followSystem: true
|
|
13
|
+
showGlyph: true
|
|
14
|
+
showClock: true
|
|
15
|
+
showTurns: true
|
|
16
|
+
statusScope: pink-only
|
package/lib/types/autoTheme.d.ts
CHANGED
|
@@ -45,19 +45,29 @@ export declare function applyCachedFollow(dataDir: string): string | undefined;
|
|
|
45
45
|
* Best effort: no TTY, an unresponsive terminal, or a parse failure just
|
|
46
46
|
* leaves the previous state intact. Runs before the host's own stdin
|
|
47
47
|
* parsing is mounted; raw mode is restored to whatever it was.
|
|
48
|
+
*
|
|
49
|
+
* Keystroke safety: the 'data' listener consumes every byte of the window
|
|
50
|
+
* (the terminal multiplexes replies and keypresses on one stream). Anything
|
|
51
|
+
* that is not part of the OSC 11 reply is re-emitted through stdin.emit on
|
|
52
|
+
* teardown, so input typed mid-window reaches the host parser intact.
|
|
48
53
|
* @param dataDir - The host data directory (~/.dsh-tui).
|
|
54
|
+
* @param isActive - Live follow gate: a false return (follow turned off
|
|
55
|
+
* mid-window) makes finish() skip the pref write — off preserves the
|
|
56
|
+
* manual choice even for an in-flight detection.
|
|
49
57
|
* @param stdout - Injectable for tests.
|
|
50
58
|
* @param stdin - Injectable for tests.
|
|
51
59
|
* @param setTimeoutFn - Injectable for tests.
|
|
52
60
|
* @returns The detected light-ness, or undefined when unavailable.
|
|
53
61
|
*/
|
|
54
|
-
export declare function refreshDetectedBackground(dataDir: string, stdout?: NodeJS.WriteStream, stdin?: NodeJS.ReadStream, setTimeoutFn?: typeof setTimeout): Promise<boolean | undefined>;
|
|
62
|
+
export declare function refreshDetectedBackground(dataDir: string, isActive?: () => boolean, stdout?: NodeJS.WriteStream, stdin?: NodeJS.ReadStream, setTimeoutFn?: typeof setTimeout): Promise<boolean | undefined>;
|
|
55
63
|
/**
|
|
56
64
|
* The whole follow sequence for apply(): cached value now (pre-mount),
|
|
57
65
|
* fresh detection for the next boot.
|
|
58
66
|
* @param dataDir - The host data directory (~/.dsh-tui).
|
|
67
|
+
* @param isActive - Live follow gate, re-queried when an in-flight reply
|
|
68
|
+
* lands (follow may be switched off in /settings during the window).
|
|
59
69
|
* @param log - Info sink for the applied/refreshed outcomes.
|
|
60
70
|
*/
|
|
61
|
-
export declare function runFollowSystem(dataDir: string, log: (message: string) => void): void;
|
|
71
|
+
export declare function runFollowSystem(dataDir: string, isActive: () => boolean, log: (message: string) => void): void;
|
|
62
72
|
export {};
|
|
63
73
|
//# sourceMappingURL=autoTheme.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autoTheme.d.ts","sourceRoot":"","sources":["../../src/autoTheme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;
|
|
1
|
+
{"version":3,"file":"autoTheme.d.ts","sourceRoot":"","sources":["../../src/autoTheme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAqBH,UAAU,WAAW;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;CACX;AA4BD,+DAA+D;AAC/D,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGjE;AAED,yDAAyD;AACzD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAErE;AAED,2CAA2C;AAC3C,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAGxE;AAMD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQrE;AAcD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,MAAM,OAAoB,EACpC,MAAM,GAAE,MAAM,CAAC,WAA4B,EAC3C,KAAK,GAAE,MAAM,CAAC,UAA0B,EACxC,YAAY,GAAE,OAAO,UAAuB,GAC3C,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAgF9B;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,OAAO,EACvB,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC7B,IAAI,CAYN"}
|
package/lib/types/autoTheme.js
CHANGED
|
@@ -27,6 +27,12 @@ const QUERY = '\x1b]11;?\x07';
|
|
|
27
27
|
// OSC 11 reply: rgb:RRRR/GGGG/BBBB with 1-4 hex digits per channel, BEL or
|
|
28
28
|
// ST terminated.
|
|
29
29
|
const REPLY = /\x1b\]11;rgb:([0-9a-fA-F]{1,4})\/([0-9a-fA-F]{1,4})\/([0-9a-fA-F]{1,4})(?:\x07|\x1b\\)/;
|
|
30
|
+
// DA1 sentinel: every terminal since VT100 answers CSI c. The host's own
|
|
31
|
+
// terminal-querier uses this to avoid timeouts — a DA1 reply arriving before
|
|
32
|
+
// any OSC 11 reply proves the terminal will never answer OSC 11, so the
|
|
33
|
+
// window closes immediately instead of running out the full timeout.
|
|
34
|
+
const DA1_QUERY = '\x1b[c';
|
|
35
|
+
const DA1_REPLY = /\x1b\[\?[0-9;]*c/;
|
|
30
36
|
const DETECT_TIMEOUT_MS = 400;
|
|
31
37
|
function prefPath(dir) {
|
|
32
38
|
return join(dir, 'theme.json');
|
|
@@ -108,13 +114,21 @@ function isLightBackground(r, g, b) {
|
|
|
108
114
|
* Best effort: no TTY, an unresponsive terminal, or a parse failure just
|
|
109
115
|
* leaves the previous state intact. Runs before the host's own stdin
|
|
110
116
|
* parsing is mounted; raw mode is restored to whatever it was.
|
|
117
|
+
*
|
|
118
|
+
* Keystroke safety: the 'data' listener consumes every byte of the window
|
|
119
|
+
* (the terminal multiplexes replies and keypresses on one stream). Anything
|
|
120
|
+
* that is not part of the OSC 11 reply is re-emitted through stdin.emit on
|
|
121
|
+
* teardown, so input typed mid-window reaches the host parser intact.
|
|
111
122
|
* @param dataDir - The host data directory (~/.dsh-tui).
|
|
123
|
+
* @param isActive - Live follow gate: a false return (follow turned off
|
|
124
|
+
* mid-window) makes finish() skip the pref write — off preserves the
|
|
125
|
+
* manual choice even for an in-flight detection.
|
|
112
126
|
* @param stdout - Injectable for tests.
|
|
113
127
|
* @param stdin - Injectable for tests.
|
|
114
128
|
* @param setTimeoutFn - Injectable for tests.
|
|
115
129
|
* @returns The detected light-ness, or undefined when unavailable.
|
|
116
130
|
*/
|
|
117
|
-
export function refreshDetectedBackground(dataDir, stdout = process.stdout, stdin = process.stdin, setTimeoutFn = setTimeout) {
|
|
131
|
+
export function refreshDetectedBackground(dataDir, isActive = () => true, stdout = process.stdout, stdin = process.stdin, setTimeoutFn = setTimeout) {
|
|
118
132
|
return new Promise(resolve => {
|
|
119
133
|
if (stdout.isTTY !== true || stdin.isTTY !== true) {
|
|
120
134
|
resolve(undefined);
|
|
@@ -130,19 +144,41 @@ export function refreshDetectedBackground(dataDir, stdout = process.stdout, stdi
|
|
|
130
144
|
}
|
|
131
145
|
let buffer = '';
|
|
132
146
|
let settled = false;
|
|
133
|
-
|
|
147
|
+
// Push the window's leftover bytes back for the host's input parser.
|
|
148
|
+
// unshift() returns them to the stream's internal buffer, which both the
|
|
149
|
+
// host's pull-mode 'readable' pump (mounted later) and any flowing-mode
|
|
150
|
+
// 'data' listener consume; the emit() fallback covers a torn-down stream
|
|
151
|
+
// where unshift refuses to work. Keystrokes typed during detection used
|
|
152
|
+
// to be silently dropped here. latin1 is byte-exact.
|
|
153
|
+
const replayLeftover = (leftover) => {
|
|
154
|
+
if (leftover === '')
|
|
155
|
+
return;
|
|
156
|
+
try {
|
|
157
|
+
stdin.unshift(Buffer.from(leftover, 'latin1'));
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
try {
|
|
161
|
+
stdin.emit('data', Buffer.from(leftover, 'latin1'));
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// Best effort: input preservation must never throw from a detector.
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
const finish = (light, leftover) => {
|
|
134
169
|
if (settled)
|
|
135
170
|
return;
|
|
136
171
|
settled = true;
|
|
137
172
|
clearTimeout(timer);
|
|
138
173
|
stdin.removeListener('data', onData);
|
|
174
|
+
replayLeftover(leftover);
|
|
139
175
|
try {
|
|
140
176
|
stdin.setRawMode?.(wasRaw);
|
|
141
177
|
}
|
|
142
178
|
catch {
|
|
143
179
|
// Stream already torn down — nothing to restore.
|
|
144
180
|
}
|
|
145
|
-
if (light !== undefined) {
|
|
181
|
+
if (light !== undefined && isActive()) {
|
|
146
182
|
writeFollowCache(light, dataDir);
|
|
147
183
|
const target = themeForBackground(light);
|
|
148
184
|
if (readThemePref(dataDir) !== target) {
|
|
@@ -154,29 +190,42 @@ export function refreshDetectedBackground(dataDir, stdout = process.stdout, stdi
|
|
|
154
190
|
const onData = (chunk) => {
|
|
155
191
|
buffer += typeof chunk === 'string' ? chunk : chunk.toString('latin1');
|
|
156
192
|
const match = REPLY.exec(buffer);
|
|
157
|
-
if (match
|
|
193
|
+
if (match !== null) {
|
|
194
|
+
const light = isLightBackground(channel8(match[1] ?? ''), channel8(match[2] ?? ''), channel8(match[3] ?? ''));
|
|
195
|
+
const leftover = buffer.slice(0, match.index) + buffer.slice(match.index + match[0].length);
|
|
196
|
+
finish(light, leftover);
|
|
158
197
|
return;
|
|
159
|
-
|
|
198
|
+
}
|
|
199
|
+
// DA1 arrived before any OSC 11 reply: this terminal skips OSC 11
|
|
200
|
+
// entirely (host querier pattern) — stop waiting. Bytes after the DA1
|
|
201
|
+
// reply can only be input, so they come along.
|
|
202
|
+
const da1 = DA1_REPLY.exec(buffer);
|
|
203
|
+
if (da1 !== null) {
|
|
204
|
+
const leftover = buffer.slice(0, da1.index) + buffer.slice(da1.index + da1[0].length);
|
|
205
|
+
finish(undefined, leftover);
|
|
206
|
+
}
|
|
160
207
|
};
|
|
161
|
-
const timer = setTimeoutFn(() => finish(undefined), DETECT_TIMEOUT_MS);
|
|
208
|
+
const timer = setTimeoutFn(() => finish(undefined, buffer), DETECT_TIMEOUT_MS);
|
|
162
209
|
if (stdin.isPaused())
|
|
163
210
|
stdin.resume();
|
|
164
211
|
stdin.on('data', onData);
|
|
165
|
-
stdout.write(QUERY);
|
|
212
|
+
stdout.write(QUERY + DA1_QUERY);
|
|
166
213
|
});
|
|
167
214
|
}
|
|
168
215
|
/**
|
|
169
216
|
* The whole follow sequence for apply(): cached value now (pre-mount),
|
|
170
217
|
* fresh detection for the next boot.
|
|
171
218
|
* @param dataDir - The host data directory (~/.dsh-tui).
|
|
219
|
+
* @param isActive - Live follow gate, re-queried when an in-flight reply
|
|
220
|
+
* lands (follow may be switched off in /settings during the window).
|
|
172
221
|
* @param log - Info sink for the applied/refreshed outcomes.
|
|
173
222
|
*/
|
|
174
|
-
export function runFollowSystem(dataDir, log) {
|
|
223
|
+
export function runFollowSystem(dataDir, isActive, log) {
|
|
175
224
|
const applied = applyCachedFollow(dataDir);
|
|
176
225
|
if (applied !== undefined && existsSync(prefPath(dataDir))) {
|
|
177
226
|
log(`follow: applied cached background (${applied})`);
|
|
178
227
|
}
|
|
179
|
-
void refreshDetectedBackground(dataDir).then(light => {
|
|
228
|
+
void refreshDetectedBackground(dataDir, isActive).then(light => {
|
|
180
229
|
if (light === undefined) {
|
|
181
230
|
log('follow: terminal background unavailable, keeping current choice');
|
|
182
231
|
}
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEjF,eAAO,MAAM,IAAI,kBAAkB,CAAA;AAEnC,wDAAwD;AACxD,MAAM,MAAM,MAAM,GAAG,eAAe,CAAA;AAUpC,eAAO,MAAM,MAAM,EAAE,WAAW,CAAC,MAAM,CAQrC,CAAA;AAyBF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,OAAO,EAAwB,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAEjF,eAAO,MAAM,IAAI,kBAAkB,CAAA;AAEnC,wDAAwD;AACxD,MAAM,MAAM,MAAM,GAAG,eAAe,CAAA;AAUpC,eAAO,MAAM,MAAM,EAAE,WAAW,CAAC,MAAM,CAQrC,CAAA;AAyBF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,CAsE7D"}
|
package/lib/types/index.js
CHANGED
|
@@ -90,8 +90,11 @@ export function apply(ctx, config = {}) {
|
|
|
90
90
|
// write would, and toggling followSystem in /settings re-decides live.
|
|
91
91
|
let followActive;
|
|
92
92
|
const dataDir = join(homeDir(), '.dsh-tui');
|
|
93
|
+
// Live gate for in-flight detections: a reply that lands after the user
|
|
94
|
+
// turned follow off must not rewrite the pref.
|
|
95
|
+
const followEnabled = () => followActive === true;
|
|
93
96
|
const startFollow = () => {
|
|
94
|
-
runFollowSystem(dataDir, message => {
|
|
97
|
+
runFollowSystem(dataDir, followEnabled, message => {
|
|
95
98
|
ctx.logger.info(`dsh-tui-theme: ${message}`);
|
|
96
99
|
});
|
|
97
100
|
};
|
|
@@ -112,11 +115,13 @@ export function apply(ctx, config = {}) {
|
|
|
112
115
|
// applies instead. The delay lets any pending service registration (and
|
|
113
116
|
// its callback) land first — it no-ops once followActive is settled, and
|
|
114
117
|
// stays inside the host's own 300ms pre-mount settings gate either way.
|
|
115
|
-
setTimeout(() => {
|
|
118
|
+
const fallbackTimer = setTimeout(() => {
|
|
116
119
|
if (followActive === undefined && cordis.followSystem) {
|
|
117
120
|
followActive = true;
|
|
118
121
|
startFollow();
|
|
119
122
|
}
|
|
120
123
|
}, FOLLOW_FALLBACK_MS);
|
|
124
|
+
fallbackTimer.unref?.();
|
|
125
|
+
ctx.effect(() => () => clearTimeout(fallbackTimer));
|
|
121
126
|
startStatusLine(ctx, () => effective);
|
|
122
127
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settingsSection.d.ts","sourceRoot":"","sources":["../../src/settingsSection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,+EAA+E;AAC/E,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG;IAC5C,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,oEAAoE;IACpE,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAcD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,GACpC,IAAI,
|
|
1
|
+
{"version":3,"file":"settingsSection.d.ts","sourceRoot":"","sources":["../../src/settingsSection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,+EAA+E;AAC/E,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG;IAC5C,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,oEAAoE;IACpE,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAcD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,GACpC,IAAI,CAoDN"}
|
|
@@ -30,29 +30,36 @@ import z from '@deepseek-ai/schemastery';
|
|
|
30
30
|
export function registerPinkSettings(ctx, cordis, onDoc) {
|
|
31
31
|
ctx.inject(['settings'], settingsCtx => {
|
|
32
32
|
const settings = settingsCtx.settings;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
33
|
+
try {
|
|
34
|
+
const scope = settings.register(settingsNamespace('dsh-tui-theme'), z.object({
|
|
35
|
+
followSystem: z.boolean(),
|
|
36
|
+
showGlyph: z.boolean(),
|
|
37
|
+
showClock: z.boolean(),
|
|
38
|
+
showTurns: z.boolean(),
|
|
39
|
+
statusScope: z.union(['pink-only', 'all-themes']),
|
|
40
|
+
}));
|
|
41
|
+
const emit = (doc) => {
|
|
42
|
+
if (doc === null || typeof doc !== 'object')
|
|
43
|
+
return;
|
|
44
|
+
const clean = {};
|
|
45
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
46
|
+
if (value !== undefined)
|
|
47
|
+
clean[key] = value;
|
|
48
|
+
}
|
|
49
|
+
onDoc(clean);
|
|
50
|
+
};
|
|
51
|
+
// Own the watcher on the inject-scoped ledger so it survives exactly as
|
|
52
|
+
// long as this activation (scope.watch's disposer is otherwise leaked).
|
|
53
|
+
settingsCtx.effect(() => {
|
|
54
|
+
emit(scope.get());
|
|
55
|
+
return scope.watch(emit);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
// A duplicate registration (hot reload race) or a stricter host must
|
|
60
|
+
// not take the plugin — or the TUI — down.
|
|
61
|
+
settingsCtx.logger.warn(`dsh-tui-pink-theme: settings namespace registration failed: ${String(error)}`);
|
|
62
|
+
}
|
|
56
63
|
});
|
|
57
64
|
ctx.inject(['tuiSettingsSections'], sectionsCtx => {
|
|
58
65
|
const sections = sectionsCtx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"statusLine.d.ts","sourceRoot":"","sources":["../../src/statusLine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAOlD,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;AAEpD,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,iEAAiE;IACjE,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED,mCAAmC;AACnC,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAA;AAwCrD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"statusLine.d.ts","sourceRoot":"","sources":["../../src/statusLine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAOlD,mDAAmD;AACnD,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;AAEpD,MAAM,WAAW,aAAa;IAC5B,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,sCAAsC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,iEAAiE;IACjE,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED,mCAAmC;AACnC,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAA;AAwCrD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,GAAG,IAAI,CA8DvF"}
|
package/lib/types/statusLine.js
CHANGED
|
@@ -61,6 +61,7 @@ export function startStatusLine(ctx, getEffective) {
|
|
|
61
61
|
const turns = new Map();
|
|
62
62
|
let current;
|
|
63
63
|
let dispose;
|
|
64
|
+
let lastText;
|
|
64
65
|
const render = () => {
|
|
65
66
|
try {
|
|
66
67
|
const eff = getEffective();
|
|
@@ -79,6 +80,9 @@ export function startStatusLine(ctx, getEffective) {
|
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
82
|
const text = parts.join(' · ');
|
|
83
|
+
if (text === lastText)
|
|
84
|
+
return;
|
|
85
|
+
lastText = text;
|
|
82
86
|
// The trailing identity must be the inject-scoped context (the same
|
|
83
87
|
// activation the traceable binds as caller) — the plugin's outer ctx
|
|
84
88
|
// is a different activation view and would be silently rejected.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-tui-theme",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Sakura-pink themes for dsh-TUI with terminal-background auto-follow (pink-day/pink-night), a blossom status line, and a /settings section. No shortcuts, no commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/types/index.js",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "tsc -p tsconfig.json",
|
|
27
|
+
"preverify": "npm run build",
|
|
27
28
|
"verify": "node scripts/verify.mjs"
|
|
28
29
|
},
|
|
29
30
|
"license": "MIT",
|