dsh-neotui 0.4.2 → 0.4.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/CHANGELOG.md +1 -2
- package/README.md +1 -1
- package/package.json +1 -1
- package/src/ascii-logo.js +43 -0
- package/src/logo.js +38 -38
- package/src/views.js +128 -39
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.4.2 — 2026-08-23
|
|
4
3
|
|
|
5
4
|
### Added
|
|
6
5
|
|
|
7
|
-
-
|
|
6
|
+
- 欢迎页改版:**吉祥物 logo 默认关闭**(此前的 40×19 象限块/58×27 半块等实现保留,`Ctrl+R` 面板可随时启用预设/自定义/关闭;视觉迭代未能达成理想效果,先回退到仅标题)(真实色 255 色调色板,数据内嵌 `src/logo.js`,去背景、无残块、裁剪掉原图底部黑条);自定义 logo JSON 支持象限格式(`[mask,fg,bg]`)与旧半块格式(`[top,bottom]`);品牌行用 **`█▀▄` 半块像素字** 绘制(`DEEPSEEK` / `DSH NEOTUI` 两行大字标,5×7 像素字模),**每约 3.5 秒一道斜向高光扫过字标**(0.9s 扫程,纯 TUI 渲染并随主题语义色,扫过字标全宽含末尾 `I`;`DSH NEOTUI` 用深灰底保证白色高光可见);**logo 三态** `Ctrl+R` buffer:预设(内置)/ 自定义(复用 Ctrl+O 与新建工作区的 `UploadPicker` 单选 `{palette, grid}` 40×19 JSON)/ 关闭(仅标题),持久化到配置文件;版本更新提示收敛到底部(仅「可更新/失败/检查中」时显示,可点击检查,**最新则不提示**);模式选择从内联 4 列表收敛为底部提示「模式: <当前> · F9 打开模式选择(支持自定义)」——与 WebUI 鼓励自定义预设的方向一致;矮视口(<32 行或 <62 列)回退为普通文本品牌行。
|
|
8
7
|
- 启动动画(经典游戏启动器风格,**opt-in meme**):纯黑 → 白色从屏幕中心圆形扩散至全屏(1s)→ 四行健康游戏忠告逐句淡入(2s)→ 忠告淡出的同时 `DEEPSEEK` 半块大字交叉淡入(2s)→ 底部闪烁「按任意键进入游戏」,任意键进入主界面——全程纯 ANSI(`█▀▄`),无图片协议;默认关闭,`--launcher-anime`(或 `DSH_TUI_LAUNCHER_ANIME=1`)显式启用,`DSH_TUI_NO_SPLASH=1` 强制关闭;`--script` 模式不播放,窄小终端自动跳过。
|
|
9
8
|
|
|
10
9
|
## 0.4.1 — 2026-08-22
|
package/README.md
CHANGED
|
@@ -210,7 +210,7 @@ TUI 支持:
|
|
|
210
210
|
- 工具审批、AskUser 单选/多选、Plan Review;
|
|
211
211
|
- Goal、TODO、后台任务和 Subagent;
|
|
212
212
|
- 独立插件清单及 `/` 筛选;
|
|
213
|
-
-
|
|
213
|
+
- 空白会话欢迎页:**默认不显示吉祥物 logo**(`Ctrl+R` 打开 logo 面板:预设(40×19 象限块 80×38 真实色)/ 自定义 JSON / 关闭);品牌行用 **`█▀▄` 半块像素字**绘制(`DEEPSEEK` / `DSH NEOTUI` 两行大字标),**每约 3.5 秒一道斜向高光扫过字标**(纯 TUI 渲染,随主题语义色);**logo 三态**(`Ctrl+R` 打开 buffer):预设(内置)/ 自定义(Yazi 风格文件选择器单选 JSON,格式 `{palette, grid}` 40×19)/ 关闭(仅标题),选择持久化到配置文件;版本更新提示收敛到底部(`DeepSeek Harness 可更新 v…` / `dsh-neotui 可更新 v…`,可点击检查,**最新时不显示**);模式选择为底部提示(`F9 打开模式选择(支持自定义)`);矮视口(<29 行或 <60 列)回退为普通文本品牌行;
|
|
214
214
|
- 11 套配色主题:dark、light、gruvbox、nord、solarized-dark、solarized-light、dracula、onedark、catppuccin-mocha、tokyonight、monokai;`Ctrl+D`、`/theme` 或命令面板打开**配色选择器**——每行带该方案的色板(面板/用户/强调/成功/警告/错误六色),**光标移动即对整套 TUI 即时预览**(未持久化,标题栏显示「预览: <name>」),`Enter` 或双击应用并停留继续调整,`Esc` 恢复已提交主题并关闭;选择持久化到 `$DSH_HOME/tui-theme.txt`。
|
|
215
215
|
- 启动动画(经典游戏启动器风格,**默认关闭的 meme**):纯黑 → 白色从屏幕中心圆扩散到全屏(约 1s)→ 四行健康游戏忠告("抵制不良游戏,拒绝盗版游戏。"等,逐句淡入,约 2s)→ 忠告淡出同时 `DEEPSEEK` 半块大字交叉淡入(约 2s)→ 底部闪烁「按任意键进入游戏」,**按任意键进入**。全程纯 ANSI(`█▀▄`);`dsh-tui --launcher-anime`(或 `DSH_TUI_LAUNCHER_ANIME=1`)显式启用,`DSH_TUI_NO_SPLASH=1` 强制关闭,`--script` 模式不播放。
|
|
216
216
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// ascii-logo.js — Welcome-page ASCII-art logo (52x24 character grid).
|
|
2
|
+
// Aspect-true for 1:2 terminal cells: 52 cols / 24 rows = 2.17, matching the
|
|
3
|
+
// source face (740x680 px). Each cell = [rampIndex, colorIndex] (hex byte
|
|
4
|
+
// each; 0,0 = empty). RAMP lightest '.' (no space keeps bright areas textured).
|
|
5
|
+
export const ASCII_LOGO_COLS = 52;
|
|
6
|
+
export const ASCII_LOGO_ROWS = 24;
|
|
7
|
+
export const ASCII_LOGO_RAMP = ".:-=+*#%@";
|
|
8
|
+
export const ASCII_LOGO_PALETTE = [
|
|
9
|
+
"fefeff","fefef7","fdfcfd","fdfbfd","fef9fd","fcfafe","fcfafb","fff2ed",
|
|
10
|
+
"ffefe8","feeee2","feecdf","feebe0","ffeae1","ffeadc","fdeadc","fceadd",
|
|
11
|
+
"fee9e2","ffe9de","fde9de","ffe9dd","ffe9dc","ffe9db","fce9d8","ffe8e3",
|
|
12
|
+
"ffe7de","fee8dd","fce8dd","ffe8dc","fee8d9","fde6da","fee5d9","fde3d8",
|
|
13
|
+
"ffe2d8","ffe1d9","fedacf","fed0c5","f9fefc","fafcfb","f6fdfc","fbfbfd",
|
|
14
|
+
"fbfbfb","fbfbf8","fbfafd","fafaf8","f6f9fd","f8f7fa","f7ede6","f8e6dc",
|
|
15
|
+
"f7dfd5","f9dbd1","f8d5cd","f1f1f7","e9eaf5","ece5e6","e9dbd9","d7d8e7",
|
|
16
|
+
"eed2ce","d9d5e5","d4d1e0","d0d5e8","d0cee0","cfcddc","cecbe2","cfcbda",
|
|
17
|
+
"b5e4fa","c0d7f4","b9ceeb","d0cadc","ccc9d8","c9c8da","c7c8de","c8c8d8",
|
|
18
|
+
"e2bcbc","e8b5b1","c8c5da","c8c3d0","bfc6e2","c5c6d8","c0bed5","abc1e9",
|
|
19
|
+
"a7b8e0","a5b5e1","e7b1af","ebaaaa","dab2b5","c2abbc","f4a19d","d49ea0",
|
|
20
|
+
"bf93a2","c58381","a3b0d6","9baddf","96aedb","95abdb","96aadc","95a9d8",
|
|
21
|
+
"97a6ce","9e9fc6","94a1c4","8faee1","91a7d8","8fa3d8","89a2d0","8c9bc7",
|
|
22
|
+
"8c96c3","839bcf","7697c3","8692c0","888ebf","8090bf","808cbf","7d8cbe",
|
|
23
|
+
"7d8ab7","778dc1","778bbe","718db9","8383a7","7a84b7","7685b4","7585ba",
|
|
24
|
+
"7188bc","7185b8","7085bb","7281b6","6e85b6","6d82b9","6885b3","6c81b5",
|
|
25
|
+
"6c80b6","6780b8","cc716d","9a7684","7778a5","707eb2","717ca8","72769e",
|
|
26
|
+
"6c7fb5","697eb9","697eb2","6b7bb1","687eb5","677eb1","677db7","687db2",
|
|
27
|
+
"677daf","687cb7","677cb3","677cb1","6774a6","657fb7","657fb3","667db2",
|
|
28
|
+
"647db6","647db3","667cb6","667cb4","657bb5","667bb2","657ab3","647ab4",
|
|
29
|
+
"657baf","657aaf","6679b0","6578b0","6479b0","6478ad","637db5","637cb5",
|
|
30
|
+
"627cb5","627bb4","607eb2","607cb3","6379b3","617ab3","6279ae","6278ae",
|
|
31
|
+
"6278a8","5f79ab","5b77a6","6175aa","5e74ae","5f72a7","5974a1","5d70a7",
|
|
32
|
+
"5c6f98","586fa5","576da4","576ba4","586a9b","586799","536ba2","53689f",
|
|
33
|
+
"4f689d","6b6486","586495","54659c","53628f","4e639c","4f6389","505e94",
|
|
34
|
+
"495e99","715861","7a4945","594d64","4c5b92","4c5781","485a8f","475084",
|
|
35
|
+
"455a94","445991","435891","435792","42578c","425691","43568d","41558e",
|
|
36
|
+
"42538f","465286","3f538d","3f5087","3c4c86","3a487e","32477e","724245",
|
|
37
|
+
"523e4d","3e4678","3c4184","5f3640","3f3857","552b30","391d2b","33457d",
|
|
38
|
+
"2e4076","313c6e","293672","2c345f","253163","252a57","221d38","1b2e61",
|
|
39
|
+
"11295c","152350","0c2357","0f1f56","0d1d50","121d46","0a1e53","101a48",
|
|
40
|
+
"0b1849","161536","071746","030e3c","080e29","090728","0c0605",
|
|
41
|
+
];
|
|
42
|
+
export const ASCII_LOGO_GRID =
|
|
43
|
+
"0000000000000000000000000000000000000000000000000000000004b9002c0005002c002a0034013e036205c805c905c60147013e000105de0367025d0367002800040005002705ce000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005ce002d0029002e013d0035003507f607f505c905c9013c013807f904a6049606f00481047e024f013a00040003024c0034024c04b90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d0003013b0006002e05c20250049e0498049a06df06df05dd05c905d404ae06e906df06dd07f1048a0372049704ab024301380145000100280006000305cc0000000000000000000000000000000000000000000000000000000000000000000000000487002c002e000200290475035f04ac049904ad06df049204a805bb04a904a7049904a704a7049b06f007f304b506eb04a70499049a049a025c07ef0144013e00340006003500000000000000000000000000000000000000000000000000000000000000000000002600290029013d0252037204a604aa0482049d049b04aa04b504a004aa04aa04aa04aa04ac04ae04a00499049904990499048204990499025e0360025c013b013e05cc05c707fb0000000000000000000000000000000000000000000000000000000000000001025601460138025d04b1036004b604a904b0049e04ae06f0049d04ad04ad04ad04ad04ae04ae04ae04ae04aa04aa04a8025f036a0497025f036004a60473048c07f8014b0256013d013800000000000000000000000000000000000000000000000000000001002b014c0138025204b104b604a504b604a20365049104a504b804b10360036b0365025c0252037203650365049b049c025c04a1036704a205d8047904b90023002106e105c3013f013e013e06e500000000000000000000000000000000000000000000000001440035013a025204a20498049d04a004ba036a04ae04a505d704b004b1036004a60366036604a2048e04ad048f049b04b504960496049904aa049907f704ac04ab06e7001900150011013a014005cd0000000000000000000000000000000000000000000005d50144013f025004b004aa04ac04aa0482049d04a80482049b07f10493048d0496048f048f047e04940481048a049e04800256047e05bf048f048f049b0492049b049a04ac04ad05c2000b0019000805cc07f2001e001105cb000f01320000000000000000000007fb014b0252049f05c904aa04aa049905db048a0492048a04b602550480047a047a0480047a047a04b1047804780491047a0255025504860478048800300031000b05cc048704a604a205c606e1001a001e000c035a0249000b024a024a00000000000000000000025204a304af05d204ae04a804a8049905d4047e05dd047b05ce06e507ef036904860370036f036f036f036e036f06ed036d01390013001202580473036e047706e101390019001f000c000906e105c200110031001b00300124024a05cb000007f7048e04b405d105c407f405cd0499049b0499048a047e05d1047905bc04c2000a000e0019048503690477036c036c036c05d00476025604750016001500150015000f025806ec05c404770480048605c205cb0022001a001505cb000c00310254025402530000000000000000000007f306f0049b05d10493048d047e047e05bc06e707fa001807ff07ff07ff0008013d04880137047703690369036205cc000f0031024a0132013907e7000f0014025804710478047804bd05bf05c006f005e0025400100015001c000f00150000000000000000000005da06f0049c05d1049e04930480047806ea013307e70140002d06e206e906d9024c0009002f03710484024906e504c2000906e006e600310132003105e006e60484036d0371048c04a605bc05c005c606e806e906e402530014001a001b00000000000007fc05da05da07f4048d05d6048a048b05c80477036c013906e0000406df02500141025000020010000f000f0017000f00120015001c00200023012301240124012401330368047605bd0486049407f405dc07f9036106ed06de06de06de06de06ea000006ed036805da05da05cf06ea025c07f505cd0252048605c3036f036c0009013301230133013200320020001200120012000900170012001300150020002100230023002106e207fe05cc05cd047c036006f006eb05c4025b0363014d024d014d024d014d06ec05da05d706e2036103610369036305ce07f107f805d104a4036605be05c506ec048707ff002101230032001f001c0015001500120012000f000f000f000f001e001e0020001e001707ef04860486047805db05d807f405d405d805d704ba05bb07f500000000000001420142014207fc0143026107f505cf05cd05d505d106df05c4047c047c036e06e1001e001f001f001e000f001506e00383045a035a04830383038300130012001200180249024a05c507fc05d905d9048c05d306eb05d404a305d804ba04b804ba05cd0000000000000000000005c007f505d505d104b804b805d805c005c4049805dc05d805d605d905c20016000b001200120015001505cb0257025702570257001a000d00120012000b06e105c105d305d70492048e049005d105d607f705d604b405d705bc04b406f004b807f7000007fc07fc0141025105c9047e04a305db05bc05db05d604b40490049304a405bc05d905c405be06e00030001300120012001200130015001200120019013105c305c005c605bf04b6047b04980490049005d105d405d607f107fc07fa048b05d507f70264025c036401430141014105c6048d049105d805c005d805d805d605dc0366048004940372047b06df04ba05bf04ba05bf05cb06e40139000a000d0139035a035a024a048804b304b704b205c0048b0366047d06e80142014d002500060006002a0001048b048607f9026404b601410141025204b004a105d205bb00010007002c024f013f013f036a0366047b047b06f004b304b304b3014d07ef02530254035a035a02540254025301380147024d04b204b20373047d047407f20147014e002c05c7003505c304b9002507fd07fc06e205c304b3025d047f047f048b047905c107f2002900290006000600060148024b06f0047d047406ea05be07fc024f0147014b014b0148014806e701470148014601460148024f024f07f6036b036b07f307f6013e00060034002a05ce05bf0027002a07fa06ee06e206e20474047407f7047f037405c405c007f6002a00290004013b013b0034014804b70252036b07f207fd06ef0147014701470146014b04750362036204880144013c07fa07fa07fd013f036e036a0491047d00340148002900030025002a000602560006002d06ec06e2";
|
package/src/logo.js
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
// logo.js — Welcome-page logo data (DeepSeek mascot face, 40x19
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
1
|
+
// logo.js — Welcome-page logo data (DeepSeek mascot face, 40x19 quadrant).
|
|
2
|
+
// 40x19 cells × 2x2 quadrant pixels = 80x38 effective resolution.
|
|
3
|
+
// Each cell = 4 bytes: [type, maskByte, fg, bg]; type 0 = quadrant with
|
|
4
|
+
// maskByte low nibble = character quads, high nibble = second-color quads.
|
|
5
|
+
// Ordered luminance pairs (bright = fg), near-identical pairs collapsed.
|
|
6
6
|
export const LOGO_COLS = 40;
|
|
7
7
|
export const LOGO_ROWS = 19;
|
|
8
8
|
export const LOGO_PALETTE = [
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"657ab3","
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
9
|
+
"fefffd","fcfcfc","fcfbfb","fcfafc","fef2ea","feede3","feebde","feeae4",
|
|
10
|
+
"fee9de","ffe9dd","fdeae1","fdeadd","fdeadc","ffe8de","ffe8dc","fce8dc",
|
|
11
|
+
"fee6df","fde7da","fee5dc","fbfcfc","fbfdf9","fbfbfb","fbfafc","fbfaf5",
|
|
12
|
+
"fbebe2","fbe6df","f7fbfc","f7f8f9","f7f2ee","f3f4f3","eeecee","e2e9f1",
|
|
13
|
+
"fee3d9","fee2d7","fae2d8","fdded4","fddcd2","f6e0d7","ebe4e0","e1dfe8",
|
|
14
|
+
"f8d8cf","f8d2c8","ebd7d6","dfd8e1","f9d0c7","f4cac4","e7cecb","e7c8c2",
|
|
15
|
+
"d5d9ea","d4d2e1","d1d0dd","d0cede","ceccda","b6def8","bed4f3","cbcee2",
|
|
16
|
+
"c2ccec","cdcadc","cbc8d9","cac9db","c9c6d5","c7c7d7","c6c6d8","c0c8e1",
|
|
17
|
+
"eabbb9","dbb9c0","e4adaf","d6b0ae","c5c4d9","bfbecf","b3bbda","b5b5ce",
|
|
18
|
+
"b6acb7","a6b6e1","97b1e6","94aedf","a0acd4","97acdf","9aa9d2","94a9db",
|
|
19
|
+
"92a9d8","e9a3a3","de9896","b39da5","c78d8b","96a5d5","9ba5c3","98a0c3",
|
|
20
|
+
"978fab","91a6d6","8fa3d7","8ea0ca","8f9dcb","8c96c2","8c91b9","7ea1cc",
|
|
21
|
+
"879ac3","8498cb","8294c6","7893c2","818ebe","8590b5","7b8dbe","738db6",
|
|
22
|
+
"6d8cb9","7b88ba","7c85b8","7d85af","7787ba","7089bb","7386b8","7385b8",
|
|
23
|
+
"7084b6","6a84b4","7782ab","7282b4","7180af","6f82b5","707eb1","6b81b6",
|
|
24
|
+
"6b80b3","6a7eb6","6980b7","687fb7","687eb6","697fb2","6680b7","647eb6",
|
|
25
|
+
"af6c68","8c748a","77799a","6e7caf","6d78a8","6d6783","6a7db6","687db3",
|
|
26
|
+
"687cb1","6979ad","696d90","677db6","667cb6","667cb0","677bb4","667aad",
|
|
27
|
+
"6673a4","647db6","647cb5","637cb5","627cb4","627bb4","647cb1","617caa",
|
|
28
|
+
"657ab3","647ab0","637ab2","6279b2","6277b0","6278ab","6275ac","63739f",
|
|
29
|
+
"607aae","5f76ad","5d75ad","5d72a8","5973a8","5d70a6","5d6f98","576fa2",
|
|
30
|
+
"5c6b9e","576b9e","556b9f","59679c","576898","566888","526aa1","4c689e",
|
|
31
|
+
"5166a0","586498","5d6280","4e6497","4d619e","4d609a","4b5e96","495a94",
|
|
32
|
+
"525a84","4f5c71","475c93","455993","455892","445792","44588d","42587c",
|
|
33
|
+
"794e4d","654c51","5c506e","78403d","5f3b51","51557a","4d4e7a","4d3f5d",
|
|
34
|
+
"523745","45558d","42558f","41558d","45528a","40538d","415088","434572",
|
|
35
|
+
"44436c","423c5a","3e538c","3c5285","3d4f83","384b83","354881","394677",
|
|
36
|
+
"354275","30447a","393f75","3a4064","323f6d","303a6e","34395c","2b3e72",
|
|
37
|
+
"293a6c","29386f","583136","38324d","2f3155","263363","592527","432025",
|
|
38
|
+
"2d2945","2b1b33","213266","193166","1e2b5a","192c5f","1f244b","182755",
|
|
39
|
+
"1c1a38","132a5e","12255d","122256","0b2258","0e1e52","131e44","081c49",
|
|
40
|
+
"10173c","11152c","06174d","071444","0e0f2b","010c39","07061f",
|
|
41
41
|
];
|
|
42
42
|
export const LOGO_GRID =
|
|
43
|
-
"
|
|
43
|
+
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000088600002e17c6000f150000cf1635000f1600001f284900af34b200ef64b6008fb5ed003f35d5001f1e34001f38c7004f51df004f516e009f40dc008f023d004f0435002f16ba0004330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004c18ba000c1e00002e183200cf1c33000f1c0000cf33b400ef2875003f93fc003fa4ed004fb6e200df31b500ef31a0005fccf400cf7fd7002fa1f5000f7b00002f9df600cf2862004f28fd004f2c86008f2854001f1454001d1486008c1e6100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008ba00001f16b3000f1c00000f1600008f33ff00ef01ac00ef4b95000f8d0000cfa3e2003f8dd7001fa3d7000f9300000f9600000f9400008f94af008f8daf003fb5ec008f7ad7004fa3f3000f8000000f780000ef50ad008f32fd00cf033a00cf0433000f0200009d17b300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f1bba000f1400002f1733006f47b9004f4ea6000f7d00008f94ec002f9cbe000f9300000f9d00000f8c00000f9500000f9500000f9500000f9500000f9600000f8000000f8000000f9200000f7f00000f9400005f4c95002f5a7e002f50fa00cf1447004f1f83002f3ce5008cb4ec000000000000000000000000000000000000000000000000000000000000000000000000000000000008c000002e0234003f0446009f2046001f4e83000f5100000f5100008f78d3001f6deb00bf639b008f99ec000f7b00000f9d00000f9c00000fa200000f9c00000f9c00007f4e9c000fa500000f7800003f5193001f5ba1000f9700006f4b9e00ef5c9a00cf64e400df4b84008f48ef000f3b00000f3a00009d38c3000000000000000000000000000000000000000000000000000000000000000000000000002a1b20000f0300003f173500ef205600ef508e000f780000df4e8d000f9d0000cf5a9e00cf509d004f9db700af4e9e000f5a00000f5100000f5100005f5090005f5a8e00cf5a9a00ef5b7800df4ea400ef6297000f880000ef5b9b008f9bbc000f8000001f5fe8004f242f003f13b3001f2cdf000f3400008f3bdc0004fe000000000000000000000000000000000000000000000000000000000000000000006e3ddd003f3a83006f66fc00ef5697000f9600000f960000af7fda000f9900000fa300000f9d00005f99e2000f880000ef6288000f8000000f7d00000f7b00000f7d00000f7800000f7b0000df7aef000f7b00000f8c00000f8d00000f8d00001fa3f2000f9300000f850000df108b004f1ae3002f2154008f2ce8008da0f6004c07c2000c0e00001f2bc5009d13c200000000000000000000000000000000005f58f300af47b900ef519b000f8d00000f9400000f8000005f87d6000f8c00000f8d00007f44b200af42ac000f7600000f7100000f7000000f7000008f71a6000f6f00000f7700000f6f0000df43c600cf73c700cf88cf00cf85c6003f08c3003f248b003f138e008f58c9000f8500000fb20000cf30b3004f102e002f23e8005f2d81009f42c400ef1a5500ef26430008f40000000000008cdafe008c5d79001f6ffe008fa8ee001f9bd3000f9600000f8d00000f7c00005f7abe008f88e2008f6fe9003f2fd0003f2bdc007f65d1008f6ade000f6500000f6500002f65dd004f6584008f75e5008f65df001f0742000f0900001f2953003f446a008f6ab7002f85e300cf1ab200cf0f83004f29e8000f1000003f12c3003f0f82000f2300000f0900002f2ac4008f41c400df415200000000000269000003f6000003fb00005fcbf6007f99da000fa200000f8c00000f7800000f7100005fb7d600af83d1005f2fae000f2600000f0b00002f2ad2008f5885005f4de5004f5e6c000f5e00003f5e6b001f426b001f1a66000f0d00000f1200000f0700000f0600002f1244003f2adc005f6fe6000f7400008f77a700df86b1001f59e300cf21c5002f2ec2000f1000002f0ac1002f29c1001f29550000000000000000000000000008fc00002fcdf4001f67b1000fb700000f8800000f8e00000f7000009f90e1008f2ae8007f3de4007f1efa003fdbdf003fd7ea005f2bc9003f06e5008f2b6b007f42b9003f425e005f46c6004f104900cf2ac1002f19ff003f25ff003f24ff00af54e8004f2681000f6600008f6ade000f7700000fa600004fb1e000afb8db002fdbe300cf2fc800cf0f43000f2300000f10000000000000000000004c5d8a00ef56cd000fcd0000af508f008fbbf5004f7eb7002f79aa004f6a91002f65c2007f25e3004f1f81009f37e6007f2060003f3760001f05df000f0900000f0c00000f0600000f0d00000f0900000f0f00000f2100000f2500000f2d00000f2d00000f2a0000ef24ae008f5ead001f74f0007f4e77008f90f3001f90f4003f4ffb003f57d8003f4fd600bf5cd9000fd800007f47c8004c5b6300cf6dcd00ef5ecd00cfcdde00efcae6001f56f8005f5db700bf85b8006f4e9f002f71ee008f77de002f6bd2004f295e000f2e00000f2900000f2500000f2200000f0a00000f0a00000f0b00000f0d00000f0c00000f0c00000f1300000f2200000f2500000f2500003f21d100bf30c3003f6aa600bf637400ef50d700afbcf6001fbff400cf58bf008f58fc000f4700008f48e600cf40fc00df47dd003f40cd003f39d8003f394f004f39fa009f394d00ef58e100ef4bf0000fbf00004fccfb007f6d8a002f74a9003f84a900df44c9000f0800000f2400000f2200000f1200000f0d0000cf0ae3004f2154008f30e800cf0c5500cf0d81000f0600000f0a00000f0900000f230000af2bd100cf6db600cf74ce001fceee003f8acc00afbeeb000fbd0000bf8fbd007f9db1000fa50000ef5bcc0004fb00000000000000000000000000006eb1f8007f4ad5001fbbfb007f9fd300af8ecf001fbdf6008fb8f4007f89bd00bf7ed500cf74cb00cf6dce00ef6cef001f19f1000f0900000f0a00000f0a00003f12c4000f5200000f5200000f5200002f2de7000f0e00000f0900000f060000cf10dc00ef86b0004fbdf3007f7dd6000f790000af8ebb004fbcfb001fcbf5005fa4cb00bf9ebd004fa3cb009f89ec000faa00008e5bf30008fc00007f36d5001f36d500af36e0007f6ed7000f9a0000efb1cc009fb1ce008fcef2005f9fb8000f8900002f88d600bf8fcb002fb5f4002fb0b3001f5fe300cf09c6004f0c2f000f1100000f0a00000f1200000f0900000f0700008f1ae700cf2682001fafc3002fafec008fabfb002f8ad5000f8900000f790000afa4e0000fbe00000fbe00007f1cdd003f149a006f7ee6008f9fd3004f4bab002f4c7700af63e0000f3600008f36e0001f67bf000f8e0000ef8ebc007f02cf003f20d3004fd3f7007f39b4004f9ef000bf628e00bf6488001f78d7005fa5eb000fa400000fa800003f4dac004f52e3001f43c100cf105500cf0855001f53e7001f4355008f42f1009f5ced000fa500002f8ed3007f4c71004f6298008f67d8003f48e6007f0145001f1535008f1649008f1c8b002f14f9000f7900001f74aa00cf4bef002fa4f800ef36aa00ef3772008f7ea8007f6ea6008fabed000f1700000f0400002f042800bf013e000f3800009f73f600cf5b68002f71bf005f98d8009f9eeb003f47d4000f4500002f45ea003f3c41003f3f52008f43f1003f3f52001f3aea000f3b0000af3f48001f58ef00cfa8f700ef5064008f69b4005f48ad007f143f006f1eae003f15af000faf00005f1baf000f1400005fe5fa007fd0e5000fd000000e6800004f72ee006f68b4009f69ad000fad0000efb4f9000f0200000f1600003f1c2c00cf1d33006f1e3500af3fad001f68ee000f6400008feffa001f38ff00cf3efd00cf3ffa00cf3cf1000f3f00004f48f1004f58ff00df3ee500cf3cfd00cf3cdf00cf3fd100cf20d1002f3aef004f6068001f98f5008f61dd009f2882001f143d003f02a7009f1b6c008f1fc3000f0400003f03fd00bf1be5000fd000";
|
package/src/views.js
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
|
|
21
21
|
import { T, themeName, cycleTheme } from "./theme.js";
|
|
22
22
|
import { LOGO_COLS, LOGO_ROWS, LOGO_PALETTE, LOGO_GRID } from "./logo.js";
|
|
23
|
+
import { ASCII_LOGO_COLS, ASCII_LOGO_ROWS, ASCII_LOGO_RAMP, ASCII_LOGO_PALETTE, ASCII_LOGO_GRID } from "./ascii-logo.js";
|
|
23
24
|
// Live theme accessor: K.K.DIM etc. resolve against the active palette at render time.
|
|
24
25
|
const K = new Proxy({}, { get(_k, key) { return T[key]; } });
|
|
25
26
|
|
|
@@ -2758,17 +2759,24 @@ export class ChatView extends Widget {
|
|
|
2758
2759
|
// 40x19 half-block logo, per mode: preset (bundled), custom (JSON file)
|
|
2759
2760
|
// or none (title only). Centered on the 40-col block.
|
|
2760
2761
|
this.welcomeVersionRows = [];
|
|
2761
|
-
|
|
2762
|
+
// the logo only fits when the view is tall enough for its own grid;
|
|
2763
|
+
// otherwise the welcome falls back to the plain text brand lines.
|
|
2764
|
+
const needRows = this.app.logoMode === "none" ? 0
|
|
2765
|
+
: this.app.logoStyle === "ascii" && this.app.logoMode !== "custom" ? ASCII_LOGO_ROWS
|
|
2766
|
+
: this.app.logoMode === "custom" ? (this.app.logoData?.grid?.length ?? LOGO_ROWS)
|
|
2767
|
+
: (this.app.logoStyle === "ascii" ? ASCII_LOGO_ROWS : LOGO_ROWS);
|
|
2768
|
+
const logoH = this.view.w >= 62 && h >= needRows + 6 ? this.#renderWelcomeLogo(screen, cx, top) : 0;
|
|
2769
|
+
const logoDrawn = logoH > 0;
|
|
2762
2770
|
// Brand lines: TUI-drawn half-block wordmarks (like the splash), each with
|
|
2763
2771
|
// its clickable version/update row on the right; the diagonal shimmer
|
|
2764
2772
|
// sweeps across every ~4s. Falls back to spaced text on short/narrow views.
|
|
2765
|
-
const brandY = top + (logoDrawn ?
|
|
2766
|
-
const useWordmarks = h >= 32 && this.view.w >= 62;
|
|
2773
|
+
const brandY = top + (logoDrawn ? logoH + 1 : 0);
|
|
2774
|
+
const useWordmarks = h >= (logoH > 0 ? logoH + 13 : 32) && this.view.w >= 62;
|
|
2767
2775
|
if (useWordmarks) {
|
|
2768
2776
|
// The band must run PAST the right edge: the final I of DSH NEOTUI sits
|
|
2769
2777
|
// at x0+57 (plus the ~0.6/row diagonal offset), so phase 1 lands ~x0+72.
|
|
2770
2778
|
const bandX = this.app.brandShimmer >= 0
|
|
2771
|
-
? (x + Math.floor((this.view.w - 58) / 2) - 8 + this.app.brandShimmer *
|
|
2779
|
+
? (x + Math.floor((this.view.w - 58) / 2) - 8 + this.app.brandShimmer * 88)
|
|
2772
2780
|
: null;
|
|
2773
2781
|
const glow = lerpColor(T.HEADING, 0xffffff, 0.18);
|
|
2774
2782
|
const w1Y = brandY;
|
|
@@ -2802,12 +2810,30 @@ export class ChatView extends Widget {
|
|
|
2802
2810
|
}
|
|
2803
2811
|
}
|
|
2804
2812
|
|
|
2805
|
-
/** The
|
|
2813
|
+
/** The welcome mascot for the current logo mode/style. Pixel style renders
|
|
2814
|
+
* 40x19 2x2 quadrant blocks (80x38 effective); ascii style renders the
|
|
2815
|
+
* 52x24 character grid (aspect-true for 1:2 terminal cells). Returns the
|
|
2816
|
+
* drawn height in rows (0 = not drawn). */
|
|
2806
2817
|
#renderWelcomeLogo(screen, cx, top) {
|
|
2807
|
-
if (this.app.logoMode === "none") return
|
|
2818
|
+
if (this.app.logoMode === "none") return 0;
|
|
2808
2819
|
const hr = this.view.y + this.view.h;
|
|
2809
|
-
|
|
2810
|
-
|
|
2820
|
+
if (this.app.logoStyle === "ascii" && this.app.logoMode !== "custom") {
|
|
2821
|
+
for (let r = 0; r < ASCII_LOGO_ROWS && top + r < hr; r++) {
|
|
2822
|
+
for (let c = 0; c < ASCII_LOGO_COLS; c++) {
|
|
2823
|
+
const i = (r * ASCII_LOGO_COLS + c) * 4;
|
|
2824
|
+
// empty cell = ramp 0 AND color 0; ramp 0 with a color = the '.' glyph
|
|
2825
|
+
const ci = parseInt(ASCII_LOGO_GRID.slice(i, i + 2), 16);
|
|
2826
|
+
const pi = parseInt(ASCII_LOGO_GRID.slice(i + 2, i + 4), 16);
|
|
2827
|
+
if (ci === 0 && pi === 0) continue;
|
|
2828
|
+
screen.put(cx - Math.floor((ASCII_LOGO_COLS - LOGO_COLS) / 2) + c, top + r,
|
|
2829
|
+
ASCII_LOGO_RAMP[ci], { fg: pi ? parseInt(ASCII_LOGO_PALETTE[pi - 1], 16) : T.FAINT, attrs: 1 });
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
return ASCII_LOGO_ROWS;
|
|
2833
|
+
}
|
|
2834
|
+
const custom = this.app.logoMode === "custom";
|
|
2835
|
+
let pal, grid, quadrant = !custom;
|
|
2836
|
+
if (custom) {
|
|
2811
2837
|
if (!this.app.logoData) {
|
|
2812
2838
|
if (this.app.logoPath) {
|
|
2813
2839
|
try { this.app.logoData = this.app.loadCustomLogo(this.app.logoPath); }
|
|
@@ -2816,33 +2842,76 @@ export class ChatView extends Widget {
|
|
|
2816
2842
|
if (!this.app.logoData) return false;
|
|
2817
2843
|
}
|
|
2818
2844
|
pal = this.app.logoData.palette; grid = this.app.logoData.grid;
|
|
2845
|
+
quadrant = this.app.logoData.quadrant !== false;
|
|
2819
2846
|
} else {
|
|
2820
2847
|
pal = LOGO_PALETTE; grid = LOGO_GRID;
|
|
2821
2848
|
}
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2849
|
+
// mask → glyph: bits tl/tr/bl/br (quadrant blocks render width 1).
|
|
2850
|
+
const GL = [" ", "▘", "▝", "▀", "▖", "▌", "▞", "▛", "▗", "▚", "▐", "▜", "▄", "▙", "▟", "█"];
|
|
2851
|
+
const rows = custom ? grid.length : LOGO_ROWS;
|
|
2852
|
+
const cols = custom ? (grid[0]?.length ?? LOGO_COLS) : LOGO_COLS;
|
|
2853
|
+
for (let r = 0; r < rows && top + r < hr; r++) {
|
|
2854
|
+
for (let c = 0; c < cols; c++) {
|
|
2855
|
+
let kind, mask, fi, bi;
|
|
2856
|
+
if (quadrant) {
|
|
2857
|
+
const cell = custom ? grid[r]?.[c] : null;
|
|
2858
|
+
if (cell) { kind = cell.length === 4 ? cell[0] : 0; mask = cell.length === 4 ? cell[1] : cell[0]; fi = cell.length === 4 ? cell[2] : cell[1]; bi = cell.length === 4 ? cell[3] : cell[2]; }
|
|
2859
|
+
else {
|
|
2860
|
+
// each cell = 4 bytes (type,mask,fg,bg) = 8 hex chars; type 0 =
|
|
2861
|
+
// quadrant (low nibble character quads, high nibble 2nd-color
|
|
2862
|
+
// quads), type 1 = braille (mouth region, 2x4 dots)
|
|
2863
|
+
const i = (r * LOGO_COLS + c) * 8;
|
|
2864
|
+
kind = parseInt(grid.slice(i, i + 2), 16);
|
|
2865
|
+
mask = parseInt(grid.slice(i + 2, i + 4), 16);
|
|
2866
|
+
fi = parseInt(grid.slice(i + 4, i + 6), 16);
|
|
2867
|
+
bi = parseInt(grid.slice(i + 6, i + 8), 16);
|
|
2868
|
+
}
|
|
2869
|
+
if (kind === 0 && mask === 0 && fi === 0 && bi === 0) continue;
|
|
2870
|
+
const fgc = fi ? (custom ? pal[fi] : parseInt(pal[fi - 1], 16)) : null;
|
|
2871
|
+
const bgc = bi ? (custom ? pal[bi] : parseInt(pal[bi - 1], 16)) : null;
|
|
2872
|
+
if (kind === 1) {
|
|
2873
|
+
// braille mouth cell: 8 dots (2 cols x 4 rows), fg/bg pair
|
|
2874
|
+
screen.put(cx + c, top + r, String.fromCharCode(0x2800 | (mask & 0xff)),
|
|
2875
|
+
{ fg: fgc ?? T.BG, bg: bgc ?? T.BG });
|
|
2876
|
+
continue;
|
|
2877
|
+
}
|
|
2878
|
+
if (kind === 2) {
|
|
2879
|
+
// half-block: one cell = one full-width pixel pair (top/bottom)
|
|
2880
|
+
screen.put(cx + c, top + r, "▀", { fg: fgc ?? T.BG, bg: bgc ?? T.BG });
|
|
2881
|
+
continue;
|
|
2882
|
+
}
|
|
2883
|
+
const fm = mask & 0x0f, cm = (mask >> 4) & 0x0f;
|
|
2884
|
+
if (fm === 0) continue;
|
|
2885
|
+
if (fm === 0x0f && bgc != null) {
|
|
2886
|
+
// full cell: second-color quads render as glyph bg → true 2×2.
|
|
2887
|
+
const glyph = GL[(~cm) & 0x0f];
|
|
2888
|
+
screen.put(cx + c, top + r, glyph, { fg: fgc ?? T.BG, bg: bgc });
|
|
2889
|
+
} else {
|
|
2890
|
+
// partial: character quads in fg over the transparent background.
|
|
2891
|
+
screen.put(cx + c, top + r, GL[fm], { fg: fgc ?? T.BG, bg: T.BG });
|
|
2892
|
+
}
|
|
2828
2893
|
} else {
|
|
2829
|
-
const
|
|
2830
|
-
topIdx
|
|
2831
|
-
|
|
2894
|
+
const cell = custom ? grid[r][c] : null;
|
|
2895
|
+
let topIdx, botIdx;
|
|
2896
|
+
if (cell) { topIdx = cell[0]; botIdx = cell[1]; }
|
|
2897
|
+
else {
|
|
2898
|
+
const i = (r * LOGO_COLS + c) * 4;
|
|
2899
|
+
topIdx = parseInt(grid.slice(i, i + 2), 16);
|
|
2900
|
+
botIdx = parseInt(grid.slice(i + 2, i + 4), 16);
|
|
2901
|
+
}
|
|
2902
|
+
const emptyT = custom ? topIdx < 0 : topIdx === 0;
|
|
2903
|
+
const emptyB = custom ? botIdx < 0 : botIdx === 0;
|
|
2904
|
+
if (emptyT && emptyB) continue;
|
|
2905
|
+
const pal32 = (pi) => (custom ? pal[pi] : parseInt(pal[pi], 16));
|
|
2906
|
+
const tcol = emptyT ? null : pal32(custom ? topIdx : topIdx - 1);
|
|
2907
|
+
const bcol = emptyB ? null : pal32(custom ? botIdx : botIdx - 1);
|
|
2908
|
+
const fgc = tcol ?? bcol ?? T.BG;
|
|
2909
|
+
const bgc = bcol ?? T.BG;
|
|
2910
|
+
screen.put(cx + c, top + r, tcol != null ? "▀" : "▄", { fg: fgc, bg: bgc });
|
|
2832
2911
|
}
|
|
2833
|
-
const custom = this.app.logoMode === "custom";
|
|
2834
|
-
const emptyT = custom ? topIdx < 0 : topIdx === 0;
|
|
2835
|
-
const emptyB = custom ? botIdx < 0 : botIdx === 0;
|
|
2836
|
-
if (emptyT && emptyB) continue;
|
|
2837
|
-
const pal32 = (pi) => (custom ? pal[pi] : parseInt(pal[pi], 16));
|
|
2838
|
-
const tcol = emptyT ? null : pal32(custom ? topIdx : topIdx - 1);
|
|
2839
|
-
const bcol = emptyB ? null : pal32(custom ? botIdx : botIdx - 1);
|
|
2840
|
-
const fgc = tcol ?? bcol ?? T.BG;
|
|
2841
|
-
const bgc = bcol ?? T.BG;
|
|
2842
|
-
screen.put(cx + c, top + r, tcol != null ? "▀" : "▄", { fg: fgc, bg: bgc });
|
|
2843
2912
|
}
|
|
2844
2913
|
}
|
|
2845
|
-
return
|
|
2914
|
+
return LOGO_ROWS;
|
|
2846
2915
|
}
|
|
2847
2916
|
|
|
2848
2917
|
/** Bottom update notices: only shown when a product has a pending update
|
|
@@ -3554,7 +3623,8 @@ export class App {
|
|
|
3554
3623
|
// Welcome logo mode: "preset" (bundled mascot), "custom" (JSON file) or
|
|
3555
3624
|
// "none" (title only). Persisted in the tui config file.
|
|
3556
3625
|
const logoCfg = loadTuiConfig().logo ?? {};
|
|
3557
|
-
this.logoMode = ["preset", "custom", "none"].includes(logoCfg.mode) ? logoCfg.mode : "
|
|
3626
|
+
this.logoMode = ["preset", "custom", "none"].includes(logoCfg.mode) ? logoCfg.mode : "none"; // mascot off by default; Ctrl+R re-enables
|
|
3627
|
+
this.logoStyle = logoCfg.style === "ascii" ? "ascii" : "pixel"; // pixel = quadrant blocks
|
|
3558
3628
|
this.logoPath = logoCfg.path ?? null;
|
|
3559
3629
|
this.logoData = null; // decoded custom logo: { palette, grid }
|
|
3560
3630
|
this.logoLoadError = null;
|
|
@@ -4465,13 +4535,19 @@ export class App {
|
|
|
4465
4535
|
{ label: `${mark("preset")}预设`, hint: "DeepSeek 吉祥物(内置)", action: "preset" },
|
|
4466
4536
|
{ label: `${mark("custom")}自定义…`, hint: `选择 JSON 文件(当前: ${this.logoPath ?? "—"})`, action: "custom" },
|
|
4467
4537
|
{ label: `${mark("none")}关闭`, hint: "不显示 logo,仅保留品牌字标", action: "none" },
|
|
4538
|
+
{ label: `${this.logoStyle === "ascii" ? "◉" : "○"} 字符样式`, hint: this.logoStyle === "ascii" ? "当前: ASCII 52×24(回车切回像素)" : "当前: 像素 80×38(回车切换为字符画)", action: "style" },
|
|
4468
4539
|
];
|
|
4469
4540
|
const pick = async (action) => {
|
|
4470
4541
|
if (action === "preset" || action === "none") {
|
|
4471
4542
|
this.logoMode = action;
|
|
4472
4543
|
this.logoData = null;
|
|
4473
4544
|
this.logoLoadError = null;
|
|
4474
|
-
saveTuiConfig({ logo: { mode: action, path: action === "preset" ? null : this.logoPath } });
|
|
4545
|
+
saveTuiConfig({ logo: { mode: action, path: action === "preset" ? null : this.logoPath, style: this.logoStyle } });
|
|
4546
|
+
this.closeOverlay();
|
|
4547
|
+
this.redraw();
|
|
4548
|
+
} else if (action === "style") {
|
|
4549
|
+
this.logoStyle = this.logoStyle === "ascii" ? "pixel" : "ascii";
|
|
4550
|
+
saveTuiConfig({ logo: { mode: this.logoMode, path: this.logoPath, style: this.logoStyle } });
|
|
4475
4551
|
this.closeOverlay();
|
|
4476
4552
|
this.redraw();
|
|
4477
4553
|
} else if (action === "custom") {
|
|
@@ -4505,10 +4581,12 @@ export class App {
|
|
|
4505
4581
|
loadCustomLogo(path) {
|
|
4506
4582
|
const data = JSON.parse(readFileSync(path, "utf8"));
|
|
4507
4583
|
const pal = data?.palette, grid = data?.grid;
|
|
4508
|
-
|
|
4509
|
-
|
|
4584
|
+
const rows = grid?.length, cols = grid?.[0]?.length;
|
|
4585
|
+
if (!Array.isArray(pal) || !Array.isArray(grid) || (rows !== LOGO_ROWS && rows !== 19)
|
|
4586
|
+
|| grid.some((row) => !Array.isArray(row) || row.length !== cols)
|
|
4587
|
+
|| (cols !== LOGO_COLS && cols !== 40)
|
|
4510
4588
|
|| pal.length < 1 || pal.length > 65535) {
|
|
4511
|
-
throw new Error(
|
|
4589
|
+
throw new Error(`需要 { palette:[…], grid:[...] } 且为 58×27 或 40×19(得到 ${rows ?? "?"}×${cols ?? "?"})`);
|
|
4512
4590
|
}
|
|
4513
4591
|
const norm = pal.map((c) => {
|
|
4514
4592
|
if (Array.isArray(c) && c.length >= 3) return ((c[0] << 16) | (c[1] << 8) | c[2]) >>> 0;
|
|
@@ -4516,15 +4594,25 @@ export class App {
|
|
|
4516
4594
|
return NaN;
|
|
4517
4595
|
});
|
|
4518
4596
|
if (norm.some((c) => Number.isNaN(c) || c < 0 || c > 0xffffff)) throw new Error("调色板条目必须是 [r,g,b] 或 #rrggbb");
|
|
4597
|
+
// Cells: [top, bottom] = half-block (legacy) or [mask, fg, bg] = quadrant.
|
|
4598
|
+
const fmt = grid[0][0]?.length === 3 ? "quadrant" : "half";
|
|
4519
4599
|
for (const row of grid) {
|
|
4520
4600
|
for (const cell of row) {
|
|
4521
|
-
if (!Array.isArray(cell) ||
|
|
4522
|
-
|
|
4523
|
-
|
|
4601
|
+
if (!Array.isArray(cell) || !Number.isInteger(cell[0])) throw new Error("grid 单元必须是数组");
|
|
4602
|
+
if (fmt === "quadrant") {
|
|
4603
|
+
if (cell.length !== 3 || cell[0] < 0 || cell[0] > 0xff || cell[1] < -1 || cell[2] < -1
|
|
4604
|
+
|| cell[1] >= norm.length || cell[2] >= norm.length) {
|
|
4605
|
+
throw new Error("quadrant 单元必须是 [maskByte, fg, bg] 且索引在调色板范围内(mask 低四位=角色象限,高四位=第二色象限)");
|
|
4606
|
+
}
|
|
4607
|
+
} else {
|
|
4608
|
+
if (cell.length !== 2 || cell[0] < -1 || cell[1] < -1
|
|
4609
|
+
|| cell[0] >= norm.length || cell[1] >= norm.length || !Number.isInteger(cell[1])) {
|
|
4610
|
+
throw new Error("half 单元必须是 [top, bottom] 且索引在调色板范围内");
|
|
4611
|
+
}
|
|
4524
4612
|
}
|
|
4525
4613
|
}
|
|
4526
4614
|
}
|
|
4527
|
-
return { palette: norm, grid };
|
|
4615
|
+
return { palette: norm, grid, quadrant: fmt === "quadrant" };
|
|
4528
4616
|
}
|
|
4529
4617
|
|
|
4530
4618
|
#applyCustomLogo(path) {
|
|
@@ -4533,7 +4621,7 @@ export class App {
|
|
|
4533
4621
|
this.logoMode = "custom";
|
|
4534
4622
|
this.logoPath = path;
|
|
4535
4623
|
this.logoLoadError = null;
|
|
4536
|
-
saveTuiConfig({ logo: { mode: "custom", path } });
|
|
4624
|
+
saveTuiConfig({ logo: { mode: "custom", path, style: this.logoStyle } });
|
|
4537
4625
|
this.closeOverlay();
|
|
4538
4626
|
this.redraw();
|
|
4539
4627
|
this.toast("自定义 logo 已应用");
|
|
@@ -5835,9 +5923,10 @@ export class App {
|
|
|
5835
5923
|
* ~3.5s) while a tall blank welcome is on screen. Separated from the tick
|
|
5836
5924
|
* loop so tests can drive it with fake timestamps. */
|
|
5837
5925
|
tickWelcomeShimmer(now) {
|
|
5926
|
+
const logoH = this.logoStyle === "ascii" ? ASCII_LOGO_ROWS : this.logoMode === "none" ? 0 : LOGO_ROWS;
|
|
5838
5927
|
const welcomeUp = this.chat.nodes.length === 0
|
|
5839
5928
|
&& (this.sessions.find((s) => s.sessionId === this.currentSession)?.blank ?? false)
|
|
5840
|
-
&& this.chat.view.h >=
|
|
5929
|
+
&& this.chat.view.h >= logoH + 13 && this.chat.view.w >= 62;
|
|
5841
5930
|
if (!welcomeUp) {
|
|
5842
5931
|
if (this.brandSweep0 >= 0 || this.brandShimmer >= 0) { this.brandSweep0 = -1; this.brandShimmer = -1; }
|
|
5843
5932
|
return;
|