dsh-tui-theme 0.2.0
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/LICENSE +21 -0
- package/README.md +90 -0
- package/cordis.patch.yml +15 -0
- package/lib/types/autoTheme.d.ts +63 -0
- package/lib/types/autoTheme.d.ts.map +1 -0
- package/lib/types/autoTheme.js +191 -0
- package/lib/types/index.d.ts +28 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +86 -0
- package/lib/types/settingsSection.d.ts +38 -0
- package/lib/types/settingsSection.d.ts.map +1 -0
- package/lib/types/settingsSection.js +111 -0
- package/lib/types/statusLine.d.ts +38 -0
- package/lib/types/statusLine.d.ts.map +1 -0
- package/lib/types/statusLine.js +84 -0
- package/lib/types/themeAssets.d.ts +30 -0
- package/lib/types/themeAssets.d.ts.map +1 -0
- package/lib/types/themeAssets.js +62 -0
- package/package.json +67 -0
- package/themes/pink-ansi.json +102 -0
- package/themes/pink-day.json +102 -0
- package/themes/pink-night.json +102 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 xiaoxiaohaigui
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
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
|
+
设置里的“跟随终端背景”开启后(本部署默认开启):
|
|
29
|
+
|
|
30
|
+
- 每次启动检测终端背景(跟随系统主题的终端会随系统深浅变化),亮 → `pink-day`,暗 → `pink-night`,写入 `~/.dsh-tui/theme.json`,与宿主 `auto` 伪主题同款行为、同一亮度阈值;
|
|
31
|
+
- 检测结果缓存在 `~/.dsh-tui/theme-follow.json`,因此切换在**启动瞬间**就生效;首次开启或系统刚翻转后的那次启动可能滞后一拍(与宿主 auto “重新选择或重启后跟上”一致);
|
|
32
|
+
- 开启此功能后 `/theme` 的选择由插件接管(每次启动都会按背景覆写);关闭它即可恢复手动选择;
|
|
33
|
+
- `DSH_TUI_THEME` 环境变量仍然最优先(宿主行为,插件不覆盖环境变量)。
|
|
34
|
+
|
|
35
|
+
## 安装
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
# 方式一:本地路径(开发/自用)
|
|
39
|
+
dsh plugin --profile dsh-tui add -w /path/to/dsh-tui-theme
|
|
40
|
+
|
|
41
|
+
# 方式二:从 GitHub(发布后)
|
|
42
|
+
dsh plugin --profile dsh-tui add -w github:<你的用户名>/dsh-tui-theme
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
重启 dsh-TUI 后插件自动把三套主题复制进 `~/.dsh-tui/themes/`(**仅缺失时复制,绝不覆盖你已有的同名文件**),然后:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
# 在 dsh-TUI 里
|
|
49
|
+
/theme # 选择器:夜樱 / 昼樱 / 樱·ANSI
|
|
50
|
+
/theme pink-night # 或直接切换(开启跟随后由插件接管)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 配置
|
|
54
|
+
|
|
55
|
+
配置有三层,优先级:`/settings` 用户层 > `cordis.yml` 配置层 > 内置默认值。
|
|
56
|
+
|
|
57
|
+
`/settings` 里找到 **pink-theme** 区块即可编辑:
|
|
58
|
+
|
|
59
|
+
| 字段 | 默认 | 说明 |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| `followSystem` | 部署默认开 | 跟随终端背景:昼樱 ↔ 夜樱自动切换 |
|
|
62
|
+
| `showGlyph` | `true` | 花符:开 = ✿ 开头,关 = 不显示 |
|
|
63
|
+
| `showClock` | `true` | 显示 HH:MM 时钟 |
|
|
64
|
+
| `showTurns` | `true` | 显示当前会话轮数(`N✦`,自本次启动起计) |
|
|
65
|
+
|
|
66
|
+
三项装饰全关时状态行整体消失。另有仅 profile 层的开关(`cordis.patch.yml`,不出现在 /settings):`autoInstallThemes`、`statusEnabled`。
|
|
67
|
+
|
|
68
|
+
> 已知宿主限制:底栏的上下文进度条分段色(蓝色系)与空余段配色是宿主硬编码的,主题与插件均无法覆盖;自定义浅色主题(pink-day)会拿到深色空余段配色(宿主按 `themeName === 'light'` 判断,不识别自定义浅色主题)。这需要上游修复。
|
|
69
|
+
|
|
70
|
+
## 卸载
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
dsh plugin --profile dsh-tui remove -w dsh-tui-theme # 移除插件
|
|
74
|
+
rm ~/.dsh-tui/themes/pink-{night,day,ansi}.json # 可选:删除主题文件
|
|
75
|
+
rm ~/.dsh-tui/theme-follow.json # 可选:删除跟随缓存
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## 开发
|
|
79
|
+
|
|
80
|
+
```sh
|
|
81
|
+
pnpm install && pnpm build # tsc -> lib/types/
|
|
82
|
+
npm run verify # 沙盒测试(临时 HOME + 伪造 TTY,不碰真实 ~/.dsh-tui)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
主题调色板改起来最直接:编辑 `themes/*.json` 后重新 `npm run verify`,再删掉 `~/.dsh-tui/themes/` 下对应文件让插件重装。
|
|
86
|
+
|
|
87
|
+
## 兼容性
|
|
88
|
+
|
|
89
|
+
- dsh-TUI 的 `dsh-tui-extensions` 扩展面(tuiStatus / tuiSettingsSections);旧版 profile 缺这些服务时插件自动退化为“仅安装主题”,不会报错。
|
|
90
|
+
- Node `^22.19 || >=24`,纯 ESM,MIT。
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
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
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal-background follow: the pink pair's `auto`.
|
|
3
|
+
*
|
|
4
|
+
* The host's `auto` pseudo-theme resolves builtin light/dark via an OSC 11
|
|
5
|
+
* query at mount and keeps the result in memory only — user themes cannot
|
|
6
|
+
* ride it. This module gives pink-day/pink-night the same behavior from the
|
|
7
|
+
* plugin side:
|
|
8
|
+
*
|
|
9
|
+
* - at apply() (which runs before the React tree mounts and reads
|
|
10
|
+
* ~/.dsh-tui/theme.json), the cached detection writes the resolved theme
|
|
11
|
+
* name into the pref synchronously — always in time for this boot;
|
|
12
|
+
* - a fresh OSC 11 query then refreshes the cache for the next boot. The
|
|
13
|
+
* very first enabling (or a system flip between boots) lands one boot
|
|
14
|
+
* late — the same cadence as the host's own "re-select auto or restart".
|
|
15
|
+
*
|
|
16
|
+
* The pref write mirrors the host's writeThemePref byte-for-byte
|
|
17
|
+
* ({"theme": name}, 2-space indent) and the light test mirrors the host's
|
|
18
|
+
* luminance threshold so both sides always agree on light/dark.
|
|
19
|
+
* @module dsh-tui-theme/autoTheme
|
|
20
|
+
*/
|
|
21
|
+
interface FollowCache {
|
|
22
|
+
light: boolean;
|
|
23
|
+
at: number;
|
|
24
|
+
}
|
|
25
|
+
/** The persisted theme pref ({ theme: name }), host format. */
|
|
26
|
+
export declare function readThemePref(dataDir: string): string | undefined;
|
|
27
|
+
/** Persist the theme pref in the host's exact format. */
|
|
28
|
+
export declare function writeThemePref(name: string, dataDir: string): boolean;
|
|
29
|
+
/** The cached detection, if one exists. */
|
|
30
|
+
export declare function readFollowCache(dataDir: string): FollowCache | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* Map a detected background to this pair's theme name.
|
|
33
|
+
* @param light - True for a light terminal background.
|
|
34
|
+
*/
|
|
35
|
+
export declare function themeForBackground(light: boolean): string;
|
|
36
|
+
/**
|
|
37
|
+
* Apply the follow behavior synchronously from cache: writes the resolved
|
|
38
|
+
* theme name into the pref when it differs. Safe before mount — pure fs.
|
|
39
|
+
* @param dataDir - The host data directory (~/.dsh-tui).
|
|
40
|
+
* @returns The applied theme name, or undefined when no cache exists yet.
|
|
41
|
+
*/
|
|
42
|
+
export declare function applyCachedFollow(dataDir: string): string | undefined;
|
|
43
|
+
/**
|
|
44
|
+
* Query the terminal background (OSC 11) and refresh the cache + pref.
|
|
45
|
+
* Best effort: no TTY, an unresponsive terminal, or a parse failure just
|
|
46
|
+
* leaves the previous state intact. Runs before the host's own stdin
|
|
47
|
+
* parsing is mounted; raw mode is restored to whatever it was.
|
|
48
|
+
* @param dataDir - The host data directory (~/.dsh-tui).
|
|
49
|
+
* @param stdout - Injectable for tests.
|
|
50
|
+
* @param stdin - Injectable for tests.
|
|
51
|
+
* @param setTimeoutFn - Injectable for tests.
|
|
52
|
+
* @returns The detected light-ness, or undefined when unavailable.
|
|
53
|
+
*/
|
|
54
|
+
export declare function refreshDetectedBackground(dataDir: string, stdout?: NodeJS.WriteStream, stdin?: NodeJS.ReadStream, setTimeoutFn?: typeof setTimeout): Promise<boolean | undefined>;
|
|
55
|
+
/**
|
|
56
|
+
* The whole follow sequence for apply(): cached value now (pre-mount),
|
|
57
|
+
* fresh detection for the next boot.
|
|
58
|
+
* @param dataDir - The host data directory (~/.dsh-tui).
|
|
59
|
+
* @param log - Info sink for the applied/refreshed outcomes.
|
|
60
|
+
*/
|
|
61
|
+
export declare function runFollowSystem(dataDir: string, log: (message: string) => void): void;
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=autoTheme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"autoTheme.d.ts","sourceRoot":"","sources":["../../src/autoTheme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAgBH,UAAU,WAAW;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;CACX;AAgCD,+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;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,MAAM,CAAC,WAA4B,EAC3C,KAAK,GAAE,MAAM,CAAC,UAA0B,EACxC,YAAY,GAAE,OAAO,UAAuB,GAC3C,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAmD9B;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC7B,IAAI,CAYN"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal-background follow: the pink pair's `auto`.
|
|
3
|
+
*
|
|
4
|
+
* The host's `auto` pseudo-theme resolves builtin light/dark via an OSC 11
|
|
5
|
+
* query at mount and keeps the result in memory only — user themes cannot
|
|
6
|
+
* ride it. This module gives pink-day/pink-night the same behavior from the
|
|
7
|
+
* plugin side:
|
|
8
|
+
*
|
|
9
|
+
* - at apply() (which runs before the React tree mounts and reads
|
|
10
|
+
* ~/.dsh-tui/theme.json), the cached detection writes the resolved theme
|
|
11
|
+
* name into the pref synchronously — always in time for this boot;
|
|
12
|
+
* - a fresh OSC 11 query then refreshes the cache for the next boot. The
|
|
13
|
+
* very first enabling (or a system flip between boots) lands one boot
|
|
14
|
+
* late — the same cadence as the host's own "re-select auto or restart".
|
|
15
|
+
*
|
|
16
|
+
* The pref write mirrors the host's writeThemePref byte-for-byte
|
|
17
|
+
* ({"theme": name}, 2-space indent) and the light test mirrors the host's
|
|
18
|
+
* luminance threshold so both sides always agree on light/dark.
|
|
19
|
+
* @module dsh-tui-theme/autoTheme
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
22
|
+
import { homedir } from 'node:os';
|
|
23
|
+
import { join } from 'node:path';
|
|
24
|
+
/** The pair this feature switches between (light terminal → day). */
|
|
25
|
+
const LIGHT_THEME = 'pink-day';
|
|
26
|
+
const DARK_THEME = 'pink-night';
|
|
27
|
+
const QUERY = '\x1b]11;?\x07';
|
|
28
|
+
// OSC 11 reply: rgb:RRRR/GGGG/BBBB with 1-4 hex digits per channel, BEL or
|
|
29
|
+
// ST terminated.
|
|
30
|
+
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\\)/;
|
|
31
|
+
const DETECT_TIMEOUT_MS = 400;
|
|
32
|
+
function homeDir() {
|
|
33
|
+
return homedir() || process.env.USERPROFILE || process.env.HOME || '';
|
|
34
|
+
}
|
|
35
|
+
function prefPath(dir) {
|
|
36
|
+
return join(dir, 'theme.json');
|
|
37
|
+
}
|
|
38
|
+
function cachePath(dir) {
|
|
39
|
+
return join(dir, 'theme-follow.json');
|
|
40
|
+
}
|
|
41
|
+
function readJsonSync(path) {
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function writeJsonSync(path, value) {
|
|
50
|
+
try {
|
|
51
|
+
mkdirSync(join(path, '..'), { recursive: true });
|
|
52
|
+
writeFileSync(path, JSON.stringify(value, null, 2));
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/** The persisted theme pref ({ theme: name }), host format. */
|
|
60
|
+
export function readThemePref(dataDir) {
|
|
61
|
+
const parsed = readJsonSync(prefPath(dataDir));
|
|
62
|
+
return typeof parsed?.theme === 'string' ? parsed.theme : undefined;
|
|
63
|
+
}
|
|
64
|
+
/** Persist the theme pref in the host's exact format. */
|
|
65
|
+
export function writeThemePref(name, dataDir) {
|
|
66
|
+
return writeJsonSync(prefPath(dataDir), { theme: name });
|
|
67
|
+
}
|
|
68
|
+
/** The cached detection, if one exists. */
|
|
69
|
+
export function readFollowCache(dataDir) {
|
|
70
|
+
const cached = readJsonSync(cachePath(dataDir));
|
|
71
|
+
return typeof cached?.light === 'boolean' ? cached : undefined;
|
|
72
|
+
}
|
|
73
|
+
function writeFollowCache(light, dataDir) {
|
|
74
|
+
writeJsonSync(cachePath(dataDir), { light, at: Date.now() });
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Map a detected background to this pair's theme name.
|
|
78
|
+
* @param light - True for a light terminal background.
|
|
79
|
+
*/
|
|
80
|
+
export function themeForBackground(light) {
|
|
81
|
+
return light ? LIGHT_THEME : DARK_THEME;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Apply the follow behavior synchronously from cache: writes the resolved
|
|
85
|
+
* theme name into the pref when it differs. Safe before mount — pure fs.
|
|
86
|
+
* @param dataDir - The host data directory (~/.dsh-tui).
|
|
87
|
+
* @returns The applied theme name, or undefined when no cache exists yet.
|
|
88
|
+
*/
|
|
89
|
+
export function applyCachedFollow(dataDir) {
|
|
90
|
+
const cached = readFollowCache(dataDir);
|
|
91
|
+
if (cached === undefined)
|
|
92
|
+
return undefined;
|
|
93
|
+
const target = themeForBackground(cached.light);
|
|
94
|
+
if (readThemePref(dataDir) !== target) {
|
|
95
|
+
writeThemePref(target, dataDir);
|
|
96
|
+
}
|
|
97
|
+
return target;
|
|
98
|
+
}
|
|
99
|
+
/** Scale a 1-4 digit hex OSC channel to 8-bit. */
|
|
100
|
+
function channel8(hex) {
|
|
101
|
+
const value = parseInt(hex, 16);
|
|
102
|
+
if (hex.length >= 3)
|
|
103
|
+
return Math.round(value / (16 ** hex.length - 1) * 255);
|
|
104
|
+
return value * (hex.length === 2 ? 1 : 17);
|
|
105
|
+
}
|
|
106
|
+
/** The host's luminance test (ThemeProvider.isLightBackground). */
|
|
107
|
+
function isLightBackground(r, g, b) {
|
|
108
|
+
return 0.299 * r + 0.587 * g + 0.114 * b > 140;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Query the terminal background (OSC 11) and refresh the cache + pref.
|
|
112
|
+
* Best effort: no TTY, an unresponsive terminal, or a parse failure just
|
|
113
|
+
* leaves the previous state intact. Runs before the host's own stdin
|
|
114
|
+
* parsing is mounted; raw mode is restored to whatever it was.
|
|
115
|
+
* @param dataDir - The host data directory (~/.dsh-tui).
|
|
116
|
+
* @param stdout - Injectable for tests.
|
|
117
|
+
* @param stdin - Injectable for tests.
|
|
118
|
+
* @param setTimeoutFn - Injectable for tests.
|
|
119
|
+
* @returns The detected light-ness, or undefined when unavailable.
|
|
120
|
+
*/
|
|
121
|
+
export function refreshDetectedBackground(dataDir, stdout = process.stdout, stdin = process.stdin, setTimeoutFn = setTimeout) {
|
|
122
|
+
return new Promise(resolve => {
|
|
123
|
+
if (stdout.isTTY !== true || stdin.isTTY !== true) {
|
|
124
|
+
resolve(undefined);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const wasRaw = stdin.isRaw === true;
|
|
128
|
+
try {
|
|
129
|
+
stdin.setRawMode?.(true);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
resolve(undefined);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
let buffer = '';
|
|
136
|
+
let settled = false;
|
|
137
|
+
const finish = (light) => {
|
|
138
|
+
if (settled)
|
|
139
|
+
return;
|
|
140
|
+
settled = true;
|
|
141
|
+
clearTimeout(timer);
|
|
142
|
+
stdin.removeListener('data', onData);
|
|
143
|
+
try {
|
|
144
|
+
stdin.setRawMode?.(wasRaw);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// Stream already torn down — nothing to restore.
|
|
148
|
+
}
|
|
149
|
+
if (light !== undefined) {
|
|
150
|
+
writeFollowCache(light, dataDir);
|
|
151
|
+
const target = themeForBackground(light);
|
|
152
|
+
if (readThemePref(dataDir) !== target) {
|
|
153
|
+
writeThemePref(target, dataDir);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
resolve(light);
|
|
157
|
+
};
|
|
158
|
+
const onData = (chunk) => {
|
|
159
|
+
buffer += typeof chunk === 'string' ? chunk : chunk.toString('latin1');
|
|
160
|
+
const match = REPLY.exec(buffer);
|
|
161
|
+
if (match === null)
|
|
162
|
+
return;
|
|
163
|
+
finish(isLightBackground(channel8(match[1] ?? ''), channel8(match[2] ?? ''), channel8(match[3] ?? '')));
|
|
164
|
+
};
|
|
165
|
+
const timer = setTimeoutFn(() => finish(undefined), DETECT_TIMEOUT_MS);
|
|
166
|
+
if (stdin.isPaused())
|
|
167
|
+
stdin.resume();
|
|
168
|
+
stdin.on('data', onData);
|
|
169
|
+
stdout.write(QUERY);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The whole follow sequence for apply(): cached value now (pre-mount),
|
|
174
|
+
* fresh detection for the next boot.
|
|
175
|
+
* @param dataDir - The host data directory (~/.dsh-tui).
|
|
176
|
+
* @param log - Info sink for the applied/refreshed outcomes.
|
|
177
|
+
*/
|
|
178
|
+
export function runFollowSystem(dataDir, log) {
|
|
179
|
+
const applied = applyCachedFollow(dataDir);
|
|
180
|
+
if (applied !== undefined && existsSync(prefPath(dataDir))) {
|
|
181
|
+
log(`follow: applied cached background (${applied})`);
|
|
182
|
+
}
|
|
183
|
+
void refreshDetectedBackground(dataDir).then(light => {
|
|
184
|
+
if (light === undefined) {
|
|
185
|
+
log('follow: terminal background unavailable, keeping current choice');
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
log(`follow: detected ${light ? 'light' : 'dark'} terminal → ${themeForBackground(light)}`);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-tui-theme — 樱花粉主题插件(dsh-TUI 生态)。
|
|
3
|
+
*
|
|
4
|
+
* 个性化全部走既有接缝,不注册快捷键、不注册命令、不拦截任何输入:
|
|
5
|
+
* - 接缝四(主题):内置三套粉色 JSON(pink-night / pink-day / pink-ansi),
|
|
6
|
+
* 启动时复制进 ~/.dsh-tui/themes/(仅缺失时,绝不覆盖用户已有文件);
|
|
7
|
+
* - 自动跟随:检测终端/系统背景色(OSC 11,与宿主同阈值),在昼樱/夜樱
|
|
8
|
+
* 间自动切换——pink 版的 auto;
|
|
9
|
+
* - 接缝十一(状态行):输入框上方一行小装饰(✿ · 时钟 · 本轮轮数);
|
|
10
|
+
* - 接缝六(设置区块):/settings 里一个可编辑面板,即时生效。
|
|
11
|
+
*
|
|
12
|
+
* 遵循 #183 纪律:所有宿主服务用 ctx.inject 软探测,缺席即静默降级;
|
|
13
|
+
* 插件缺失或宿主较旧时行为退化为“什么都没发生”,绝不拖垮启动。
|
|
14
|
+
* @module dsh-tui-theme
|
|
15
|
+
*/
|
|
16
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
17
|
+
import { type PinkSettingsDoc } from './settingsSection.js';
|
|
18
|
+
export declare const name = "dsh-tui-theme";
|
|
19
|
+
/** Configurable knobs; every key has a sane default. */
|
|
20
|
+
export type Config = PinkSettingsDoc;
|
|
21
|
+
export declare const Config: Schemastery<Config>;
|
|
22
|
+
/**
|
|
23
|
+
* Wire the pink theme plugin.
|
|
24
|
+
* @param ctx - Cordis context (the plugin's own activation).
|
|
25
|
+
* @param config - Validated plugin config (schema defaults applied).
|
|
26
|
+
*/
|
|
27
|
+
export declare function apply(ctx: Context, config?: Config): void;
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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,CAOrC,CAAA;AAiBF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,CAsC7D"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-tui-theme — 樱花粉主题插件(dsh-TUI 生态)。
|
|
3
|
+
*
|
|
4
|
+
* 个性化全部走既有接缝,不注册快捷键、不注册命令、不拦截任何输入:
|
|
5
|
+
* - 接缝四(主题):内置三套粉色 JSON(pink-night / pink-day / pink-ansi),
|
|
6
|
+
* 启动时复制进 ~/.dsh-tui/themes/(仅缺失时,绝不覆盖用户已有文件);
|
|
7
|
+
* - 自动跟随:检测终端/系统背景色(OSC 11,与宿主同阈值),在昼樱/夜樱
|
|
8
|
+
* 间自动切换——pink 版的 auto;
|
|
9
|
+
* - 接缝十一(状态行):输入框上方一行小装饰(✿ · 时钟 · 本轮轮数);
|
|
10
|
+
* - 接缝六(设置区块):/settings 里一个可编辑面板,即时生效。
|
|
11
|
+
*
|
|
12
|
+
* 遵循 #183 纪律:所有宿主服务用 ctx.inject 软探测,缺席即静默降级;
|
|
13
|
+
* 插件缺失或宿主较旧时行为退化为“什么都没发生”,绝不拖垮启动。
|
|
14
|
+
* @module dsh-tui-theme
|
|
15
|
+
*/
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import z from '@deepseek-ai/schemastery';
|
|
18
|
+
import { installBundledThemes, homeDir } from './themeAssets.js';
|
|
19
|
+
import { startStatusLine } from './statusLine.js';
|
|
20
|
+
import { runFollowSystem } from './autoTheme.js';
|
|
21
|
+
import { registerPinkSettings } from './settingsSection.js';
|
|
22
|
+
export const name = 'dsh-tui-theme';
|
|
23
|
+
// Explicit annotation: the inferred z.dict output references cosmokit's Dict
|
|
24
|
+
// through a pnpm-virtual path, which is not portable in declaration emit
|
|
25
|
+
// (TS2883). Mirrors the official plugin-template's workaround.
|
|
26
|
+
//
|
|
27
|
+
// statusEnabled / autoInstallThemes stay cordis-config knobs only (invisible
|
|
28
|
+
// in /settings): turning the garnish off entirely is what uninstalling is
|
|
29
|
+
// for, per user feedback. followSystem defaults off for generic installs;
|
|
30
|
+
// the shipped cordis.patch.yml opts this deployment in.
|
|
31
|
+
export const Config = z.object({
|
|
32
|
+
autoInstallThemes: z.boolean().default(true),
|
|
33
|
+
statusEnabled: z.boolean().default(true),
|
|
34
|
+
followSystem: z.boolean().default(false),
|
|
35
|
+
showGlyph: z.boolean().default(true),
|
|
36
|
+
showClock: z.boolean().default(true),
|
|
37
|
+
showTurns: z.boolean().default(true),
|
|
38
|
+
});
|
|
39
|
+
const DEFAULTS = {
|
|
40
|
+
autoInstallThemes: true,
|
|
41
|
+
statusEnabled: true,
|
|
42
|
+
followSystem: false,
|
|
43
|
+
showGlyph: true,
|
|
44
|
+
showClock: true,
|
|
45
|
+
showTurns: true,
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Wire the pink theme plugin.
|
|
49
|
+
* @param ctx - Cordis context (the plugin's own activation).
|
|
50
|
+
* @param config - Validated plugin config (schema defaults applied).
|
|
51
|
+
*/
|
|
52
|
+
export function apply(ctx, config = {}) {
|
|
53
|
+
// The cordis.yml layer: schema defaults plus explicit ?? fallbacks so a
|
|
54
|
+
// bare composition still resolves every knob.
|
|
55
|
+
const cordis = {
|
|
56
|
+
autoInstallThemes: config.autoInstallThemes ?? DEFAULTS.autoInstallThemes,
|
|
57
|
+
statusEnabled: config.statusEnabled ?? DEFAULTS.statusEnabled,
|
|
58
|
+
followSystem: config.followSystem ?? DEFAULTS.followSystem,
|
|
59
|
+
showGlyph: config.showGlyph ?? DEFAULTS.showGlyph,
|
|
60
|
+
showClock: config.showClock ?? DEFAULTS.showClock,
|
|
61
|
+
showTurns: config.showTurns ?? DEFAULTS.showTurns,
|
|
62
|
+
};
|
|
63
|
+
if (cordis.autoInstallThemes) {
|
|
64
|
+
const result = installBundledThemes();
|
|
65
|
+
for (const file of result.installed) {
|
|
66
|
+
ctx.logger.info(`dsh-tui-theme: installed bundled theme "${file}" into ~/.dsh-tui/themes/`);
|
|
67
|
+
}
|
|
68
|
+
for (const file of result.failed) {
|
|
69
|
+
ctx.logger.warn(`dsh-tui-theme: could not install bundled theme "${file}"`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Background follow runs before the React tree mounts (which reads the
|
|
73
|
+
// theme pref), so the cached detection decides this boot.
|
|
74
|
+
if (cordis.followSystem) {
|
|
75
|
+
runFollowSystem(join(homeDir(), '.dsh-tui'), message => {
|
|
76
|
+
ctx.logger.info(`dsh-tui-theme: ${message}`);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// The /settings user layer (settings.yaml) overrides the cordis layer and
|
|
80
|
+
// lands live through scope.watch; both override the hardcoded defaults.
|
|
81
|
+
let effective = cordis;
|
|
82
|
+
registerPinkSettings(ctx, cordis, doc => {
|
|
83
|
+
effective = { ...cordis, ...doc };
|
|
84
|
+
});
|
|
85
|
+
startStatusLine(ctx, () => effective);
|
|
86
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /settings integration (seam six: tuiSettingsSections over the dsh settings
|
|
3
|
+
* service).
|
|
4
|
+
*
|
|
5
|
+
* Registers the `pink-theme` settings namespace and a declarative editing
|
|
6
|
+
* section for it. Storage, schema validation, and layered resolution stay on
|
|
7
|
+
* the dsh settings service; the TUI only renders. Fields carry no schema
|
|
8
|
+
* defaults on purpose — an unset user layer must fall through to the cordis
|
|
9
|
+
* config layer (mirrors the host's own lang/fullscreen fields), and format()
|
|
10
|
+
* displays the effective value instead of a misleading blank.
|
|
11
|
+
*
|
|
12
|
+
* Both services are consumed through `ctx.inject`, not apply-time `get`
|
|
13
|
+
* probes: this row may start before the host's service rows, and the inject
|
|
14
|
+
* fires whenever each service actually registers.
|
|
15
|
+
* @module dsh-tui-pink-theme/settingsSection
|
|
16
|
+
*/
|
|
17
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
18
|
+
import type { StatusOptions } from './statusLine.js';
|
|
19
|
+
/** The plugin's settings document (every field optional at the user layer). */
|
|
20
|
+
export type PinkSettingsDoc = StatusOptions & {
|
|
21
|
+
/** Install bundled theme JSONs on boot (cordis-config layer only). */
|
|
22
|
+
autoInstallThemes?: boolean;
|
|
23
|
+
/** Follow the terminal/system background: pink-day ↔ pink-night. */
|
|
24
|
+
followSystem?: boolean;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Register the settings namespace (mirror the resolved document to the
|
|
28
|
+
* caller) and, separately, the /settings section for it. Each part waits for
|
|
29
|
+
* its own service; neither is required for the other.
|
|
30
|
+
*
|
|
31
|
+
* @param ctx - The plugin's own activation context.
|
|
32
|
+
* @param cordis - The cordis-config layer (shown as the effective value for
|
|
33
|
+
* still-unset fields).
|
|
34
|
+
* @param onDoc - Called with the defined-valued subset of the settings doc,
|
|
35
|
+
* initially and on every committed edit.
|
|
36
|
+
*/
|
|
37
|
+
export declare function registerPinkSettings(ctx: Context, cordis: StatusOptions, onDoc: (doc: PinkSettingsDoc) => void): void;
|
|
38
|
+
//# sourceMappingURL=settingsSection.d.ts.map
|
|
@@ -0,0 +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,CAuCN"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* /settings integration (seam six: tuiSettingsSections over the dsh settings
|
|
3
|
+
* service).
|
|
4
|
+
*
|
|
5
|
+
* Registers the `pink-theme` settings namespace and a declarative editing
|
|
6
|
+
* section for it. Storage, schema validation, and layered resolution stay on
|
|
7
|
+
* the dsh settings service; the TUI only renders. Fields carry no schema
|
|
8
|
+
* defaults on purpose — an unset user layer must fall through to the cordis
|
|
9
|
+
* config layer (mirrors the host's own lang/fullscreen fields), and format()
|
|
10
|
+
* displays the effective value instead of a misleading blank.
|
|
11
|
+
*
|
|
12
|
+
* Both services are consumed through `ctx.inject`, not apply-time `get`
|
|
13
|
+
* probes: this row may start before the host's service rows, and the inject
|
|
14
|
+
* fires whenever each service actually registers.
|
|
15
|
+
* @module dsh-tui-pink-theme/settingsSection
|
|
16
|
+
*/
|
|
17
|
+
import { settingsNamespace } from '@deepseek-ai/dsh-settings';
|
|
18
|
+
import z from '@deepseek-ai/schemastery';
|
|
19
|
+
/**
|
|
20
|
+
* Register the settings namespace (mirror the resolved document to the
|
|
21
|
+
* caller) and, separately, the /settings section for it. Each part waits for
|
|
22
|
+
* its own service; neither is required for the other.
|
|
23
|
+
*
|
|
24
|
+
* @param ctx - The plugin's own activation context.
|
|
25
|
+
* @param cordis - The cordis-config layer (shown as the effective value for
|
|
26
|
+
* still-unset fields).
|
|
27
|
+
* @param onDoc - Called with the defined-valued subset of the settings doc,
|
|
28
|
+
* initially and on every committed edit.
|
|
29
|
+
*/
|
|
30
|
+
export function registerPinkSettings(ctx, cordis, onDoc) {
|
|
31
|
+
ctx.inject(['settings'], settingsCtx => {
|
|
32
|
+
const settings = settingsCtx.settings;
|
|
33
|
+
const scope = settings.register(settingsNamespace('dsh-tui-theme'), z.object({
|
|
34
|
+
followSystem: z.boolean(),
|
|
35
|
+
showGlyph: z.boolean(),
|
|
36
|
+
showClock: z.boolean(),
|
|
37
|
+
showTurns: z.boolean(),
|
|
38
|
+
}));
|
|
39
|
+
const emit = (doc) => {
|
|
40
|
+
if (doc === null || typeof doc !== 'object')
|
|
41
|
+
return;
|
|
42
|
+
const clean = {};
|
|
43
|
+
for (const [key, value] of Object.entries(doc)) {
|
|
44
|
+
if (value !== undefined)
|
|
45
|
+
clean[key] = value;
|
|
46
|
+
}
|
|
47
|
+
onDoc(clean);
|
|
48
|
+
};
|
|
49
|
+
emit(scope.get());
|
|
50
|
+
scope.watch(emit);
|
|
51
|
+
});
|
|
52
|
+
ctx.inject(['tuiSettingsSections'], sectionsCtx => {
|
|
53
|
+
const sections = sectionsCtx
|
|
54
|
+
.tuiSettingsSections;
|
|
55
|
+
try {
|
|
56
|
+
const unregister = sections.register(sectionDefinition(cordis));
|
|
57
|
+
sectionsCtx.effect(() => () => unregister());
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
// A duplicate registration (hot reload race) or a stricter host must
|
|
61
|
+
// not take the plugin — or the TUI — down.
|
|
62
|
+
sectionsCtx.logger.warn(`dsh-tui-pink-theme: settings section registration failed: ${String(error)}`);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/** The declarative /settings block (labels bilingual, zh via descriptions). */
|
|
67
|
+
function sectionDefinition(cordis) {
|
|
68
|
+
return {
|
|
69
|
+
ns: 'dsh-tui-theme',
|
|
70
|
+
title: 'pink-theme',
|
|
71
|
+
descriptions: { zh: 'pink-theme' },
|
|
72
|
+
fields: [
|
|
73
|
+
{
|
|
74
|
+
path: ['followSystem'],
|
|
75
|
+
label: 'Follow terminal background',
|
|
76
|
+
descriptions: { zh: '跟随终端背景' },
|
|
77
|
+
hint: 'Auto-switch between Pink Day and Pink Night by terminal/system background. First enabling or a background flip applies from the next boot.',
|
|
78
|
+
hintDescriptions: {
|
|
79
|
+
zh: '按终端/系统背景色在昼樱与夜樱间自动切换。首次开启或背景刚翻转后,自下次启动生效。',
|
|
80
|
+
},
|
|
81
|
+
kind: 'boolean',
|
|
82
|
+
format: (value) => String(value ?? cordis.followSystem),
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
path: ['showGlyph'],
|
|
86
|
+
label: 'Blossom ✿',
|
|
87
|
+
descriptions: { zh: '花符 ✿' },
|
|
88
|
+
hint: 'Lead the decorative line above the prompt with ✿.',
|
|
89
|
+
hintDescriptions: { zh: '输入框上方装饰行的开头花符。' },
|
|
90
|
+
kind: 'boolean',
|
|
91
|
+
format: (value) => String(value ?? cordis.showGlyph),
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
path: ['showClock'],
|
|
95
|
+
label: 'Clock',
|
|
96
|
+
descriptions: { zh: '时钟' },
|
|
97
|
+
kind: 'boolean',
|
|
98
|
+
format: (value) => String(value ?? cordis.showClock),
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
path: ['showTurns'],
|
|
102
|
+
label: 'Turn count',
|
|
103
|
+
descriptions: { zh: '轮数' },
|
|
104
|
+
hint: 'Turns of the live session counted since the TUI started.',
|
|
105
|
+
hintDescriptions: { zh: '自本次启动以来当前会话的轮数。' },
|
|
106
|
+
kind: 'boolean',
|
|
107
|
+
format: (value) => String(value ?? cordis.showTurns),
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blossom status line (seam eleven: tuiStatus).
|
|
3
|
+
*
|
|
4
|
+
* One keyed contribution above the prompt: a ✿ glyph, a wall clock, and the
|
|
5
|
+
* live turn count of the current session (seam one, read-only — nothing is
|
|
6
|
+
* ever appended to the session log). The host owns rendering and
|
|
7
|
+
* sanitization; text is scalars only.
|
|
8
|
+
* @module dsh-tui-pink-theme/statusLine
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
export interface StatusOptions {
|
|
12
|
+
/** Master switch (cordis-config layer only; not surfaced in /settings). */
|
|
13
|
+
statusEnabled?: boolean;
|
|
14
|
+
/** Lead the line with the ✿ glyph. */
|
|
15
|
+
showGlyph?: boolean;
|
|
16
|
+
/** Include the HH:MM clock. */
|
|
17
|
+
showClock?: boolean;
|
|
18
|
+
/** Include the live turn count of the current session. */
|
|
19
|
+
showTurns?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/** Fully-resolved status knobs. */
|
|
22
|
+
export type EffectiveStatus = Required<StatusOptions>;
|
|
23
|
+
/**
|
|
24
|
+
* Start the status line inside the `tuiStatus` inject.
|
|
25
|
+
*
|
|
26
|
+
* The inject (not a ctx.get probe) is load-bearing: this plugin's row may
|
|
27
|
+
* start before the dsh-tui-extensions row mounts its services, and a plain
|
|
28
|
+
* `get(name, false)` at apply-time silently misses that ordering. The inject
|
|
29
|
+
* callback fires when the service registers — now or later — and property
|
|
30
|
+
* access inside it carries the caller binding the host's caller checks and
|
|
31
|
+
* effect ledger want. Hosts that never provide the seam simply never run
|
|
32
|
+
* the callback: the intended degradation, not an error.
|
|
33
|
+
* @param ctx - The plugin's own activation context.
|
|
34
|
+
* @param getEffective - Current knobs (re-read on every render so /settings
|
|
35
|
+
* edits land live without restarting anything).
|
|
36
|
+
*/
|
|
37
|
+
export declare function startStatusLine(ctx: Context, getEffective: () => EffectiveStatus): void;
|
|
38
|
+
//# sourceMappingURL=statusLine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"statusLine.d.ts","sourceRoot":"","sources":["../../src/statusLine.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAKlD,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;CACpB;AAED,mCAAmC;AACnC,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,aAAa,CAAC,CAAA;AAoBrD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,GAAG,IAAI,CAoDvF"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Blossom status line (seam eleven: tuiStatus).
|
|
3
|
+
*
|
|
4
|
+
* One keyed contribution above the prompt: a ✿ glyph, a wall clock, and the
|
|
5
|
+
* live turn count of the current session (seam one, read-only — nothing is
|
|
6
|
+
* ever appended to the session log). The host owns rendering and
|
|
7
|
+
* sanitization; text is scalars only.
|
|
8
|
+
* @module dsh-tui-pink-theme/statusLine
|
|
9
|
+
*/
|
|
10
|
+
const GLYPH = '✿';
|
|
11
|
+
const STATUS_KEY = 'dsh-tui-theme';
|
|
12
|
+
const CLOCK_TICK_MS = 15_000;
|
|
13
|
+
function clockText() {
|
|
14
|
+
// "HH:MM" from toTimeString()'s "HH:MM:SS GMT…" prefix — locale independent.
|
|
15
|
+
return new Date().toTimeString().slice(0, 5);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Start the status line inside the `tuiStatus` inject.
|
|
19
|
+
*
|
|
20
|
+
* The inject (not a ctx.get probe) is load-bearing: this plugin's row may
|
|
21
|
+
* start before the dsh-tui-extensions row mounts its services, and a plain
|
|
22
|
+
* `get(name, false)` at apply-time silently misses that ordering. The inject
|
|
23
|
+
* callback fires when the service registers — now or later — and property
|
|
24
|
+
* access inside it carries the caller binding the host's caller checks and
|
|
25
|
+
* effect ledger want. Hosts that never provide the seam simply never run
|
|
26
|
+
* the callback: the intended degradation, not an error.
|
|
27
|
+
* @param ctx - The plugin's own activation context.
|
|
28
|
+
* @param getEffective - Current knobs (re-read on every render so /settings
|
|
29
|
+
* edits land live without restarting anything).
|
|
30
|
+
*/
|
|
31
|
+
export function startStatusLine(ctx, getEffective) {
|
|
32
|
+
ctx.inject(['tuiStatus'], statusCtx => {
|
|
33
|
+
const status = statusCtx.tuiStatus;
|
|
34
|
+
const turns = new Map();
|
|
35
|
+
let current;
|
|
36
|
+
let dispose;
|
|
37
|
+
const render = () => {
|
|
38
|
+
try {
|
|
39
|
+
const eff = getEffective();
|
|
40
|
+
const parts = [];
|
|
41
|
+
if (eff.showGlyph)
|
|
42
|
+
parts.push(GLYPH);
|
|
43
|
+
if (eff.showClock)
|
|
44
|
+
parts.push(clockText());
|
|
45
|
+
if (eff.showTurns && current !== undefined) {
|
|
46
|
+
parts.push(`${turns.get(current) ?? 0}✦`);
|
|
47
|
+
}
|
|
48
|
+
const text = parts.join(' · ');
|
|
49
|
+
// The trailing identity must be the inject-scoped context (the same
|
|
50
|
+
// activation the traceable binds as caller) — the plugin's outer ctx
|
|
51
|
+
// is a different activation view and would be silently rejected.
|
|
52
|
+
dispose = status.set(STATUS_KEY, text === '' ? undefined : text, statusCtx) ?? dispose;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Display garnish only: a rendering hiccup must never travel upward.
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
ctx.on('session/event', (session, event) => {
|
|
59
|
+
current = session;
|
|
60
|
+
if (event?.type === 'turn/end') {
|
|
61
|
+
turns.set(session, (turns.get(session) ?? 0) + 1);
|
|
62
|
+
}
|
|
63
|
+
render();
|
|
64
|
+
});
|
|
65
|
+
ctx.on('session/disposed', session => {
|
|
66
|
+
turns.delete(session);
|
|
67
|
+
if (current === session)
|
|
68
|
+
current = undefined;
|
|
69
|
+
render();
|
|
70
|
+
});
|
|
71
|
+
const timer = setInterval(render, CLOCK_TICK_MS);
|
|
72
|
+
timer.unref?.();
|
|
73
|
+
statusCtx.effect(() => () => {
|
|
74
|
+
clearInterval(timer);
|
|
75
|
+
try {
|
|
76
|
+
dispose?.();
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// The host store is already gone on teardown — nothing to clear.
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
render();
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled-theme installation (seam four: static theme assets).
|
|
3
|
+
*
|
|
4
|
+
* Copies the package's themes/*.json into ~/.dsh-tui/themes/ on boot. Only
|
|
5
|
+
* files that do not exist yet are written — a user's edited or same-named
|
|
6
|
+
* theme file is never overwritten. Every failure is contained per file: a
|
|
7
|
+
* theme garnish must never break the TUI's boot.
|
|
8
|
+
* @module dsh-tui-pink-theme/themeAssets
|
|
9
|
+
*/
|
|
10
|
+
export interface ThemeInstallResult {
|
|
11
|
+
/** Files newly written into the target directory. */
|
|
12
|
+
readonly installed: readonly string[];
|
|
13
|
+
/** Files already present in the target directory (left untouched). */
|
|
14
|
+
readonly skipped: readonly string[];
|
|
15
|
+
/** Files that could not be installed (per-file failures). */
|
|
16
|
+
readonly failed: readonly string[];
|
|
17
|
+
}
|
|
18
|
+
export declare function homeDir(): string;
|
|
19
|
+
/** The package's bundled themes/ directory (sibling of the built lib/). */
|
|
20
|
+
export declare function bundledThemesDir(): string;
|
|
21
|
+
/** The host's user-theme directory (~/.dsh-tui/themes). */
|
|
22
|
+
export declare function themesTargetDir(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Install every bundled theme JSON that the target directory is missing.
|
|
25
|
+
* @param targetDir - Destination directory (defaults to ~/.dsh-tui/themes).
|
|
26
|
+
* @param sourceDir - Bundled assets (defaults to this package's themes/).
|
|
27
|
+
* @returns Per-file outcome. Never throws.
|
|
28
|
+
*/
|
|
29
|
+
export declare function installBundledThemes(targetDir?: string, sourceDir?: string): ThemeInstallResult;
|
|
30
|
+
//# sourceMappingURL=themeAssets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"themeAssets.d.ts","sourceRoot":"","sources":["../../src/themeAssets.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,MAAM,WAAW,kBAAkB;IACjC,qDAAqD;IACrD,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC,sEAAsE;IACtE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CACnC;AAID,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,2EAA2E;AAC3E,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,2DAA2D;AAC3D,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,GAAE,MAA0B,EACrC,SAAS,GAAE,MAA2B,GACrC,kBAAkB,CA2BpB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundled-theme installation (seam four: static theme assets).
|
|
3
|
+
*
|
|
4
|
+
* Copies the package's themes/*.json into ~/.dsh-tui/themes/ on boot. Only
|
|
5
|
+
* files that do not exist yet are written — a user's edited or same-named
|
|
6
|
+
* theme file is never overwritten. Every failure is contained per file: a
|
|
7
|
+
* theme garnish must never break the TUI's boot.
|
|
8
|
+
* @module dsh-tui-pink-theme/themeAssets
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
// Same resolution order as the host's utils/paths.ts homeDir(): os.homedir()
|
|
15
|
+
// first, USERPROFILE/HOME spellings as stripped-down fallbacks.
|
|
16
|
+
export function homeDir() {
|
|
17
|
+
return homedir() || process.env.USERPROFILE || process.env.HOME || '';
|
|
18
|
+
}
|
|
19
|
+
/** The package's bundled themes/ directory (sibling of the built lib/). */
|
|
20
|
+
export function bundledThemesDir() {
|
|
21
|
+
return join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'themes');
|
|
22
|
+
}
|
|
23
|
+
/** The host's user-theme directory (~/.dsh-tui/themes). */
|
|
24
|
+
export function themesTargetDir() {
|
|
25
|
+
return join(homeDir(), '.dsh-tui', 'themes');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Install every bundled theme JSON that the target directory is missing.
|
|
29
|
+
* @param targetDir - Destination directory (defaults to ~/.dsh-tui/themes).
|
|
30
|
+
* @param sourceDir - Bundled assets (defaults to this package's themes/).
|
|
31
|
+
* @returns Per-file outcome. Never throws.
|
|
32
|
+
*/
|
|
33
|
+
export function installBundledThemes(targetDir = themesTargetDir(), sourceDir = bundledThemesDir()) {
|
|
34
|
+
const installed = [];
|
|
35
|
+
const skipped = [];
|
|
36
|
+
const failed = [];
|
|
37
|
+
let files;
|
|
38
|
+
try {
|
|
39
|
+
files = readdirSync(sourceDir).filter(entry => entry.toLowerCase().endsWith('.json'));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return { installed, skipped, failed: [sourceDir] };
|
|
43
|
+
}
|
|
44
|
+
for (const file of files) {
|
|
45
|
+
try {
|
|
46
|
+
const target = join(targetDir, file);
|
|
47
|
+
if (existsSync(target)) {
|
|
48
|
+
skipped.push(file);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const text = readFileSync(join(sourceDir, file), 'utf8');
|
|
52
|
+
JSON.parse(text); // our own asset, but never write a corrupt file out
|
|
53
|
+
mkdirSync(targetDir, { recursive: true });
|
|
54
|
+
writeFileSync(target, text);
|
|
55
|
+
installed.push(file);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
failed.push(file);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { installed, skipped, failed };
|
|
62
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "dsh-tui-theme",
|
|
3
|
+
"version": "0.2.0",
|
|
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
|
+
"type": "module",
|
|
6
|
+
"main": "lib/types/index.js",
|
|
7
|
+
"types": "lib/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"import": "./lib/types/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"lib",
|
|
18
|
+
"themes",
|
|
19
|
+
"cordis.patch.yml"
|
|
20
|
+
],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": "^22.19 || >=24"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc -p tsconfig.json",
|
|
26
|
+
"verify": "node scripts/verify.mjs"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"author": "xiaoxiaohaigui",
|
|
30
|
+
"keywords": [
|
|
31
|
+
"dsh",
|
|
32
|
+
"dsh-tui",
|
|
33
|
+
"theme",
|
|
34
|
+
"plugin",
|
|
35
|
+
"terminal",
|
|
36
|
+
"pink",
|
|
37
|
+
"deepseek-harness"
|
|
38
|
+
],
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/xiaoxiaohaigui/dsh-tui-theme.git"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/xiaoxiaohaigui/dsh-tui-theme/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/xiaoxiaohaigui/dsh-tui-theme#readme",
|
|
47
|
+
"dsh": {
|
|
48
|
+
"bundle": {
|
|
49
|
+
"patch": "./cordis.patch.yml"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
57
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.1.1-rc.1",
|
|
58
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6 || ^0.1.1-rc.1"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
62
|
+
"@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.1.1-rc.1",
|
|
63
|
+
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6 || ^0.1.1-rc.1",
|
|
64
|
+
"@types/node": "^22.0.0",
|
|
65
|
+
"typescript": "^6.0.3"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pink-ansi",
|
|
3
|
+
"displayName": "樱 · Pink ANSI",
|
|
4
|
+
"base": "dark-ansi",
|
|
5
|
+
"colors": {
|
|
6
|
+
"autoAccept": "ansi:magentaBright",
|
|
7
|
+
"bashBorder": "ansi:magentaBright",
|
|
8
|
+
"claude": "ansi:magentaBright",
|
|
9
|
+
"toolNameMutate": "ansi:yellowBright",
|
|
10
|
+
"toolNameExec": "ansi:cyanBright",
|
|
11
|
+
"claudeShimmer": "ansi:magentaBright",
|
|
12
|
+
"claudeBlue_FOR_SYSTEM_SPINNER": "ansi:magentaBright",
|
|
13
|
+
"claudeBlueShimmer_FOR_SYSTEM_SPINNER": "ansi:magentaBright",
|
|
14
|
+
"permission": "ansi:magentaBright",
|
|
15
|
+
"permissionShimmer": "ansi:magentaBright",
|
|
16
|
+
"planMode": "ansi:cyanBright",
|
|
17
|
+
"ide": "ansi:magenta",
|
|
18
|
+
"promptBorder": "ansi:white",
|
|
19
|
+
"promptBorderShimmer": "ansi:whiteBright",
|
|
20
|
+
"text": "ansi:whiteBright",
|
|
21
|
+
"inverseText": "ansi:black",
|
|
22
|
+
"inactive": "ansi:white",
|
|
23
|
+
"inactiveShimmer": "ansi:whiteBright",
|
|
24
|
+
"subtle": "ansi:white",
|
|
25
|
+
"suggestion": "ansi:magentaBright",
|
|
26
|
+
"remember": "ansi:magentaBright",
|
|
27
|
+
"background": "ansi:magentaBright",
|
|
28
|
+
"success": "ansi:greenBright",
|
|
29
|
+
"error": "ansi:redBright",
|
|
30
|
+
"warning": "ansi:yellowBright",
|
|
31
|
+
"merged": "ansi:magentaBright",
|
|
32
|
+
"warningShimmer": "ansi:yellowBright",
|
|
33
|
+
"diffAdded": "ansi:green",
|
|
34
|
+
"diffRemoved": "ansi:red",
|
|
35
|
+
"diffAddedDimmed": "ansi:green",
|
|
36
|
+
"diffRemovedDimmed": "ansi:red",
|
|
37
|
+
"diffAddedWord": "ansi:greenBright",
|
|
38
|
+
"diffRemovedWord": "ansi:redBright",
|
|
39
|
+
"toolCardBackground": "ansi:blackBright",
|
|
40
|
+
"toolCardBackgroundDim": "ansi:black",
|
|
41
|
+
"toolDotExec": "ansi:greenBright",
|
|
42
|
+
"toolDotRead": "ansi:cyanBright",
|
|
43
|
+
"toolDotWrite": "ansi:magentaBright",
|
|
44
|
+
"toolDotWeb": "ansi:magenta",
|
|
45
|
+
"toolDotTask": "ansi:redBright",
|
|
46
|
+
"syntaxKeyword": "ansi:magentaBright",
|
|
47
|
+
"syntaxString": "ansi:greenBright",
|
|
48
|
+
"syntaxComment": "ansi:blackBright",
|
|
49
|
+
"syntaxNumber": "ansi:yellowBright",
|
|
50
|
+
"syntaxFunction": "ansi:cyanBright",
|
|
51
|
+
"syntaxType": "ansi:blueBright",
|
|
52
|
+
"syntaxVariable": "ansi:white",
|
|
53
|
+
"syntaxOperator": "ansi:white",
|
|
54
|
+
"syntaxPunctuation": "ansi:blackBright",
|
|
55
|
+
"syntaxConstant": "ansi:redBright",
|
|
56
|
+
"red_FOR_SUBAGENTS_ONLY": "ansi:redBright",
|
|
57
|
+
"blue_FOR_SUBAGENTS_ONLY": "ansi:blueBright",
|
|
58
|
+
"green_FOR_SUBAGENTS_ONLY": "ansi:greenBright",
|
|
59
|
+
"yellow_FOR_SUBAGENTS_ONLY": "ansi:yellowBright",
|
|
60
|
+
"purple_FOR_SUBAGENTS_ONLY": "ansi:magentaBright",
|
|
61
|
+
"orange_FOR_SUBAGENTS_ONLY": "ansi:redBright",
|
|
62
|
+
"pink_FOR_SUBAGENTS_ONLY": "ansi:magentaBright",
|
|
63
|
+
"cyan_FOR_SUBAGENTS_ONLY": "ansi:cyanBright",
|
|
64
|
+
"professionalBlue": "ansi:magentaBright",
|
|
65
|
+
"chromeYellow": "ansi:yellowBright",
|
|
66
|
+
"clawd_body": "ansi:magentaBright",
|
|
67
|
+
"clawd_background": "ansi:black",
|
|
68
|
+
"userMessageBackgroundHover": "ansi:magenta",
|
|
69
|
+
"messageActionsBackground": "ansi:blackBright",
|
|
70
|
+
"selectionBg": "ansi:magenta",
|
|
71
|
+
"bashMessageBackgroundColor": "ansi:black",
|
|
72
|
+
"memoryBackgroundColor": "ansi:blackBright",
|
|
73
|
+
"rate_limit_fill": "ansi:magentaBright",
|
|
74
|
+
"rate_limit_empty": "ansi:white",
|
|
75
|
+
"fastMode": "ansi:redBright",
|
|
76
|
+
"fastModeShimmer": "ansi:redBright",
|
|
77
|
+
"briefLabelYou": "ansi:yellowBright",
|
|
78
|
+
"briefLabelClaude": "ansi:magentaBright",
|
|
79
|
+
"rainbow_red": "ansi:red",
|
|
80
|
+
"rainbow_orange": "ansi:redBright",
|
|
81
|
+
"rainbow_yellow": "ansi:yellow",
|
|
82
|
+
"rainbow_green": "ansi:green",
|
|
83
|
+
"rainbow_blue": "ansi:cyan",
|
|
84
|
+
"rainbow_indigo": "ansi:blue",
|
|
85
|
+
"rainbow_violet": "ansi:magenta",
|
|
86
|
+
"rainbow_red_shimmer": "ansi:redBright",
|
|
87
|
+
"rainbow_orange_shimmer": "ansi:yellow",
|
|
88
|
+
"rainbow_yellow_shimmer": "ansi:yellowBright",
|
|
89
|
+
"rainbow_green_shimmer": "ansi:greenBright",
|
|
90
|
+
"rainbow_blue_shimmer": "ansi:cyanBright",
|
|
91
|
+
"rainbow_indigo_shimmer": "ansi:blueBright",
|
|
92
|
+
"rainbow_violet_shimmer": "ansi:magentaBright",
|
|
93
|
+
"subagentBullet": "ansi:magentaBright",
|
|
94
|
+
"subagentDescription": "ansi:whiteBright",
|
|
95
|
+
"subagentModel": "ansi:white",
|
|
96
|
+
"subagentElapsed": "ansi:white",
|
|
97
|
+
"subagentToolName": "ansi:cyanBright",
|
|
98
|
+
"subagentStatusRunning": "ansi:magentaBright",
|
|
99
|
+
"subagentStatusCompleted": "ansi:greenBright",
|
|
100
|
+
"subagentStatusFailed": "ansi:redBright"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pink-day",
|
|
3
|
+
"displayName": "昼樱 Pink Day",
|
|
4
|
+
"base": "light",
|
|
5
|
+
"colors": {
|
|
6
|
+
"autoAccept": "#8F6BAC",
|
|
7
|
+
"bashBorder": "#C05C7E",
|
|
8
|
+
"claude": "#D5517F",
|
|
9
|
+
"toolNameMutate": "#8A6A00",
|
|
10
|
+
"toolNameExec": "#0E7A8A",
|
|
11
|
+
"claudeShimmer": "#E879A0",
|
|
12
|
+
"claudeBlue_FOR_SYSTEM_SPINNER": "#D5517F",
|
|
13
|
+
"claudeBlueShimmer_FOR_SYSTEM_SPINNER": "#E879A0",
|
|
14
|
+
"permission": "#D5517F",
|
|
15
|
+
"permissionShimmer": "#E879A0",
|
|
16
|
+
"planMode": "#43916B",
|
|
17
|
+
"ide": "#C43D6C",
|
|
18
|
+
"promptBorder": "#EFC4D4",
|
|
19
|
+
"promptBorderShimmer": "#D988A9",
|
|
20
|
+
"text": "rgb(61,43,51)",
|
|
21
|
+
"inverseText": "#FBF3F0",
|
|
22
|
+
"inactive": "#9A8790",
|
|
23
|
+
"inactiveShimmer": "#9E6E82",
|
|
24
|
+
"subtle": "#B3A3AC",
|
|
25
|
+
"suggestion": "#D5517F",
|
|
26
|
+
"remember": "#B03A63",
|
|
27
|
+
"background": "#D5517F",
|
|
28
|
+
"success": "#3E9468",
|
|
29
|
+
"error": "#C24D5C",
|
|
30
|
+
"warning": "#B97F2E",
|
|
31
|
+
"merged": "#8F6BAC",
|
|
32
|
+
"warningShimmer": "#CE9840",
|
|
33
|
+
"diffAdded": "#DFF0E2",
|
|
34
|
+
"diffRemoved": "#F7E0E4",
|
|
35
|
+
"diffAddedDimmed": "#E8F3EA",
|
|
36
|
+
"diffRemovedDimmed": "#F9E9EC",
|
|
37
|
+
"diffAddedWord": "#8CCFA4",
|
|
38
|
+
"diffRemovedWord": "#E6A3B0",
|
|
39
|
+
"toolCardBackground": "#F9ECF1",
|
|
40
|
+
"toolCardBackgroundDim": "#F2E0E9",
|
|
41
|
+
"toolDotExec": "#4E7A5A",
|
|
42
|
+
"toolDotRead": "#3F7E8F",
|
|
43
|
+
"toolDotWrite": "#7A5CA8",
|
|
44
|
+
"toolDotWeb": "#C24D78",
|
|
45
|
+
"toolDotTask": "#B04A5A",
|
|
46
|
+
"syntaxKeyword": "#C24470",
|
|
47
|
+
"syntaxString": "#3F805F",
|
|
48
|
+
"syntaxComment": "#8B7F88",
|
|
49
|
+
"syntaxNumber": "#A6602B",
|
|
50
|
+
"syntaxFunction": "#2E7E8A",
|
|
51
|
+
"syntaxType": "#7E55A4",
|
|
52
|
+
"syntaxVariable": "#3D2B33",
|
|
53
|
+
"syntaxOperator": "#5F5260",
|
|
54
|
+
"syntaxPunctuation": "#A3959E",
|
|
55
|
+
"syntaxConstant": "#A84472",
|
|
56
|
+
"red_FOR_SUBAGENTS_ONLY": "#BE5A52",
|
|
57
|
+
"blue_FOR_SUBAGENTS_ONLY": "#4A6BB8",
|
|
58
|
+
"green_FOR_SUBAGENTS_ONLY": "#4E9675",
|
|
59
|
+
"yellow_FOR_SUBAGENTS_ONLY": "#B98A34",
|
|
60
|
+
"purple_FOR_SUBAGENTS_ONLY": "#9678B4",
|
|
61
|
+
"orange_FOR_SUBAGENTS_ONLY": "#C97F4A",
|
|
62
|
+
"pink_FOR_SUBAGENTS_ONLY": "#C24D78",
|
|
63
|
+
"cyan_FOR_SUBAGENTS_ONLY": "#4E95A0",
|
|
64
|
+
"professionalBlue": "#CE5A80",
|
|
65
|
+
"chromeYellow": "#C99A3F",
|
|
66
|
+
"clawd_body": "#E879A0",
|
|
67
|
+
"clawd_background": "#FBF3F0",
|
|
68
|
+
"userMessageBackgroundHover": "#F0DCE6",
|
|
69
|
+
"messageActionsBackground": "#F3E4EB",
|
|
70
|
+
"selectionBg": "#F3D7E0",
|
|
71
|
+
"bashMessageBackgroundColor": "#F5EAE0",
|
|
72
|
+
"memoryBackgroundColor": "#F3E4EB",
|
|
73
|
+
"rate_limit_fill": "#D988A9",
|
|
74
|
+
"rate_limit_empty": "#E8DAD2",
|
|
75
|
+
"fastMode": "#D98E4A",
|
|
76
|
+
"fastModeShimmer": "#E2A465",
|
|
77
|
+
"briefLabelYou": "#A67600",
|
|
78
|
+
"briefLabelClaude": "#D5517F",
|
|
79
|
+
"rainbow_red": "#D07F7A",
|
|
80
|
+
"rainbow_orange": "#D09C6E",
|
|
81
|
+
"rainbow_yellow": "#C9B25C",
|
|
82
|
+
"rainbow_green": "#7FB08C",
|
|
83
|
+
"rainbow_blue": "#7E96C9",
|
|
84
|
+
"rainbow_indigo": "#948CC2",
|
|
85
|
+
"rainbow_violet": "#B586B0",
|
|
86
|
+
"rainbow_red_shimmer": "#DFA4A0",
|
|
87
|
+
"rainbow_orange_shimmer": "#DFBCA8",
|
|
88
|
+
"rainbow_yellow_shimmer": "#DCCB92",
|
|
89
|
+
"rainbow_green_shimmer": "#A3CBB0",
|
|
90
|
+
"rainbow_blue_shimmer": "#AEBFE0",
|
|
91
|
+
"rainbow_indigo_shimmer": "#B4ACDA",
|
|
92
|
+
"rainbow_violet_shimmer": "#CBAEC7",
|
|
93
|
+
"subagentBullet": "#C05C7E",
|
|
94
|
+
"subagentDescription": "#3D2B33",
|
|
95
|
+
"subagentModel": "#9A8790",
|
|
96
|
+
"subagentElapsed": "#9A8790",
|
|
97
|
+
"subagentToolName": "#D5517F",
|
|
98
|
+
"subagentStatusRunning": "#D5517F",
|
|
99
|
+
"subagentStatusCompleted": "#3E9468",
|
|
100
|
+
"subagentStatusFailed": "#C24D5C"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pink-night",
|
|
3
|
+
"displayName": "夜樱 Pink Night",
|
|
4
|
+
"base": "dark",
|
|
5
|
+
"colors": {
|
|
6
|
+
"autoAccept": "#B598D9",
|
|
7
|
+
"bashBorder": "#E08CA8",
|
|
8
|
+
"claude": "#F27BA6",
|
|
9
|
+
"toolNameMutate": "#E5C07B",
|
|
10
|
+
"toolNameExec": "#72C4CF",
|
|
11
|
+
"claudeShimmer": "#F8AFC6",
|
|
12
|
+
"claudeBlue_FOR_SYSTEM_SPINNER": "#F27BA6",
|
|
13
|
+
"claudeBlueShimmer_FOR_SYSTEM_SPINNER": "#F8AFC6",
|
|
14
|
+
"permission": "#F2A0BF",
|
|
15
|
+
"permissionShimmer": "#F8C1D6",
|
|
16
|
+
"planMode": "#7FB596",
|
|
17
|
+
"ide": "#E0709A",
|
|
18
|
+
"promptBorder": "#6E4A58",
|
|
19
|
+
"promptBorderShimmer": "#E08CA8",
|
|
20
|
+
"text": "#F0E4E9",
|
|
21
|
+
"inverseText": "#2B1E25",
|
|
22
|
+
"inactive": "#A8929C",
|
|
23
|
+
"inactiveShimmer": "#C4B0B9",
|
|
24
|
+
"subtle": "#77646D",
|
|
25
|
+
"suggestion": "#F2A0BF",
|
|
26
|
+
"remember": "#F2A0BF",
|
|
27
|
+
"background": "#D9648F",
|
|
28
|
+
"success": "#8FC9A4",
|
|
29
|
+
"error": "#E66A6A",
|
|
30
|
+
"warning": "#E3B56C",
|
|
31
|
+
"merged": "#B598D9",
|
|
32
|
+
"warningShimmer": "#F0CB8E",
|
|
33
|
+
"diffAdded": "#2E3A2F",
|
|
34
|
+
"diffRemoved": "#402A31",
|
|
35
|
+
"diffAddedDimmed": "#2F3630",
|
|
36
|
+
"diffRemovedDimmed": "#392830",
|
|
37
|
+
"diffAddedWord": "#5E9B72",
|
|
38
|
+
"diffRemovedWord": "#C96B7B",
|
|
39
|
+
"toolCardBackground": "#2E2229",
|
|
40
|
+
"toolCardBackgroundDim": "#241A20",
|
|
41
|
+
"toolDotExec": "#7FB596",
|
|
42
|
+
"toolDotRead": "#7FB6C9",
|
|
43
|
+
"toolDotWrite": "#C29BDF",
|
|
44
|
+
"toolDotWeb": "#F27BA6",
|
|
45
|
+
"toolDotTask": "#E0A0AE",
|
|
46
|
+
"syntaxKeyword": "#E08CA8",
|
|
47
|
+
"syntaxString": "#8FC9A4",
|
|
48
|
+
"syntaxComment": "#857481",
|
|
49
|
+
"syntaxNumber": "#E3B56C",
|
|
50
|
+
"syntaxFunction": "#7FC4CE",
|
|
51
|
+
"syntaxType": "#C29BDF",
|
|
52
|
+
"syntaxVariable": "#DDD2D8",
|
|
53
|
+
"syntaxOperator": "#A8929C",
|
|
54
|
+
"syntaxPunctuation": "#8A7680",
|
|
55
|
+
"syntaxConstant": "#E0709A",
|
|
56
|
+
"red_FOR_SUBAGENTS_ONLY": "#D9756C",
|
|
57
|
+
"blue_FOR_SUBAGENTS_ONLY": "#8FA6DA",
|
|
58
|
+
"green_FOR_SUBAGENTS_ONLY": "#6FBB8E",
|
|
59
|
+
"yellow_FOR_SUBAGENTS_ONLY": "#D9B25E",
|
|
60
|
+
"purple_FOR_SUBAGENTS_ONLY": "#B598D9",
|
|
61
|
+
"orange_FOR_SUBAGENTS_ONLY": "#DE9464",
|
|
62
|
+
"pink_FOR_SUBAGENTS_ONLY": "#F2A0C0",
|
|
63
|
+
"cyan_FOR_SUBAGENTS_ONLY": "#6FB8BE",
|
|
64
|
+
"professionalBlue": "#E08CA8",
|
|
65
|
+
"chromeYellow": "#E3B56C",
|
|
66
|
+
"clawd_body": "#F2A0C0",
|
|
67
|
+
"clawd_background": "#000000",
|
|
68
|
+
"userMessageBackgroundHover": "#7E3A55",
|
|
69
|
+
"messageActionsBackground": "#362A31",
|
|
70
|
+
"selectionBg": "#55303E",
|
|
71
|
+
"bashMessageBackgroundColor": "#2E242A",
|
|
72
|
+
"memoryBackgroundColor": "#322630",
|
|
73
|
+
"rate_limit_fill": "#F27BA6",
|
|
74
|
+
"rate_limit_empty": "#43333B",
|
|
75
|
+
"fastMode": "#E09A58",
|
|
76
|
+
"fastModeShimmer": "#ECB478",
|
|
77
|
+
"briefLabelYou": "#FFDF80",
|
|
78
|
+
"briefLabelClaude": "#F27BA6",
|
|
79
|
+
"rainbow_red": "#DE9490",
|
|
80
|
+
"rainbow_orange": "#DEAC84",
|
|
81
|
+
"rainbow_yellow": "#DCC28A",
|
|
82
|
+
"rainbow_green": "#9CC3AA",
|
|
83
|
+
"rainbow_blue": "#A3AEDB",
|
|
84
|
+
"rainbow_indigo": "#B3A7DC",
|
|
85
|
+
"rainbow_violet": "#CFA3C8",
|
|
86
|
+
"rainbow_red_shimmer": "#EBADAA",
|
|
87
|
+
"rainbow_orange_shimmer": "#EBC3A8",
|
|
88
|
+
"rainbow_yellow_shimmer": "#E9D3A8",
|
|
89
|
+
"rainbow_green_shimmer": "#B4D2C0",
|
|
90
|
+
"rainbow_blue_shimmer": "#BCC6E6",
|
|
91
|
+
"rainbow_indigo_shimmer": "#C6BDE9",
|
|
92
|
+
"rainbow_violet_shimmer": "#DCBDD8",
|
|
93
|
+
"subagentBullet": "#E0A0BE",
|
|
94
|
+
"subagentDescription": "#F0E4E9",
|
|
95
|
+
"subagentModel": "#A8929C",
|
|
96
|
+
"subagentElapsed": "#A8929C",
|
|
97
|
+
"subagentToolName": "#F27BA6",
|
|
98
|
+
"subagentStatusRunning": "#F27BA6",
|
|
99
|
+
"subagentStatusCompleted": "#8FC9A4",
|
|
100
|
+
"subagentStatusFailed": "#E66A6A"
|
|
101
|
+
}
|
|
102
|
+
}
|