dsh-pighub 0.9.68 → 0.9.70
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/lib/client.js +46 -30
- package/lib/index.js +1 -1
- package/lib/themes.js +24 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -131,6 +131,18 @@ window.__ModuleLoader__.load({
|
|
|
131
131
|
'.pighub-themelist{max-height:0;opacity:0;overflow:hidden;transition:max-height .2s ease,opacity .2s ease,margin .2s ease;margin:0}',
|
|
132
132
|
'.pighub-themelist.is-open{max-height:240px;opacity:1;margin:4px 0}',
|
|
133
133
|
'.pighub-themerow{display:flex;align-items:center;justify-content:space-between;gap:6px;margin:3px 0}',
|
|
134
|
+
'.pighub-themebtn{flex:1;text-align:left;font-size:11px;padding:5px 9px;border-radius:8px;cursor:pointer;',
|
|
135
|
+
'border:1px solid color-mix(in oklab,var(--dsw-alias-border-secondary,#3a3a3a) 80%,transparent);',
|
|
136
|
+
'background:var(--dsw-alias-bg-secondary,#232323);color:var(--dsw-alias-label-primary,#e6e6e6);} ',
|
|
137
|
+
'.pighub-themebtn:hover:not(:disabled){border-color:var(--dsw-alias-label-secondary,#7a7a7a)}',
|
|
138
|
+
'.pighub-themebtn.is-selected{border-color:#7fbf5a;background:color-mix(in oklab,#7fbf5a 18%,transparent);',
|
|
139
|
+
'color:#cfe8bd;font-weight:600;cursor:default}',
|
|
140
|
+
'.pighub-themebtn:disabled{opacity:1}',
|
|
141
|
+
'.pighub-themedel{font-size:11px;padding:4px 9px;border-radius:8px;cursor:pointer;color:#f0b7b7;',
|
|
142
|
+
'background:transparent;border:1px solid color-mix(in oklab,#7a3b3b 70%,transparent)}',
|
|
143
|
+
'.pighub-themedel:hover:not(:disabled){background:color-mix(in oklab,#7a3b3b 22%,transparent)}',
|
|
144
|
+
'.pighub-themedel:disabled{color:var(--dsw-alias-label-tertiary,#6b7280);border-color:transparent;',
|
|
145
|
+
'background:transparent;cursor:not-allowed;opacity:.55}',
|
|
134
146
|
'.pighub-themedel{font-size:11px;padding:2px 8px;border-radius:999px;cursor:pointer;color:#f0b7b7;',
|
|
135
147
|
'background:transparent;border:1px solid color-mix(in oklab,#7a3b3b 70%,transparent)}',
|
|
136
148
|
'.pighub-themedel.is-disabled{color:var(--dsw-alias-text-tertiary,#6b7280);border-color:transparent;cursor:default}',
|
|
@@ -628,6 +640,19 @@ window.__ModuleLoader__.load({
|
|
|
628
640
|
catch (error) { set({ error: String((error && error.message) || error) }); }
|
|
629
641
|
}
|
|
630
642
|
|
|
643
|
+
/* 主题切换/删除(用户口径 2026-11-14)。
|
|
644
|
+
* 必须定义在这里:设置浮层的 JSX 里直接调 request 会 ReferenceError
|
|
645
|
+
* (「点不动」的根因就是这个——探针实测 onClick 抛错 request is not defined)。 */
|
|
646
|
+
async function setTheme(name) {
|
|
647
|
+
try { apply(await request({ action: 'theme', name })); }
|
|
648
|
+
catch (error) { set({ error: String((error && error.message) || error) }); }
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
async function removeTheme(name) {
|
|
652
|
+
try { apply(await request({ action: 'theme-delete', name })); }
|
|
653
|
+
catch (error) { set({ error: String((error && error.message) || error) }); }
|
|
654
|
+
}
|
|
655
|
+
|
|
631
656
|
async function refreshCatalog() {
|
|
632
657
|
try {
|
|
633
658
|
set({ phase: state.data ? state.phase : 'loading' });
|
|
@@ -641,6 +666,7 @@ window.__ModuleLoader__.load({
|
|
|
641
666
|
|
|
642
667
|
return {
|
|
643
668
|
get, set, subscribe, toast, load, syncRev, draw, sort, setPet, place, setMotion, savePosition, updatePrefs, refreshCatalog,
|
|
669
|
+
setTheme, removeTheme,
|
|
644
670
|
// 拖动期间给 body 挂全局禁选:拖到面板外面时,选区不会落到下面的聊天区。
|
|
645
671
|
beginDrag: (drag) => { setBodyDragging(true); set({ drag }); },
|
|
646
672
|
moveDrag: (x, y, drag) => set({ drag: Object.assign({}, drag, { x, y }) }),
|
|
@@ -1729,38 +1755,28 @@ window.__ModuleLoader__.load({
|
|
|
1729
1755
|
}, themeMenuOpen ? '收起' : '更换…'),
|
|
1730
1756
|
),
|
|
1731
1757
|
h('div', { className: 'pighub-themelist' + (themeMenuOpen ? ' is-open' : '') },
|
|
1732
|
-
((data && data.themes) || [{ name: 'classic', title: 'classic(默认)' }]).map((item) =>
|
|
1733
|
-
|
|
1758
|
+
((data && data.themes) || [{ name: 'classic', title: 'classic(默认)' }]).map((item) => {
|
|
1759
|
+
const selected = activeTheme.name === item.name;
|
|
1760
|
+
const builtin = item.name === 'classic';
|
|
1761
|
+
return h('div', { key: 'theme-row-' + item.name, className: 'pighub-themerow' },
|
|
1762
|
+
/* 三态:选中(is-selected + aria-pressed) / 可用 / 不可用 —— 用真按钮语义,不用 span 假装按钮 */
|
|
1734
1763
|
h('button', {
|
|
1735
1764
|
type: 'button',
|
|
1736
|
-
className: 'pighub-
|
|
1737
|
-
'aria-pressed':
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
className: 'pighub-themedel',
|
|
1752
|
-
title: '删除这个主题(当前主题会回落 classic)',
|
|
1753
|
-
onClick: () => {
|
|
1754
|
-
request({ action: 'theme-delete', name: item.name })
|
|
1755
|
-
.then((payload) => {
|
|
1756
|
-
if (payload && payload.ok === false) { set({ phase: 'error', error: String(payload.error || 'theme_delete_failed') }); return; }
|
|
1757
|
-
return load();
|
|
1758
|
-
})
|
|
1759
|
-
.catch((error) => set({ phase: 'error', error: String((error && error.message) || error) }));
|
|
1760
|
-
},
|
|
1761
|
-
}, '删除'),
|
|
1762
|
-
),
|
|
1763
|
-
),
|
|
1765
|
+
className: 'pighub-themebtn' + (selected ? ' is-selected' : ''),
|
|
1766
|
+
'aria-pressed': selected ? 'true' : 'false',
|
|
1767
|
+
disabled: selected,
|
|
1768
|
+
title: selected ? '当前正在使用' : '切换到「' + (item.title || item.name) + '」',
|
|
1769
|
+
onClick: () => { if (!selected) STORE.setTheme(item.name); },
|
|
1770
|
+
}, (selected ? '● ' : '○ ') + (item.title || item.name)),
|
|
1771
|
+
h('button', {
|
|
1772
|
+
type: 'button',
|
|
1773
|
+
className: 'pighub-themedel',
|
|
1774
|
+
disabled: builtin,
|
|
1775
|
+
title: builtin ? '内置主题不可删除' : '删除「' + (item.title || item.name) + '」',
|
|
1776
|
+
onClick: () => { if (!builtin) STORE.removeTheme(item.name); },
|
|
1777
|
+
}, builtin ? '内置' : '删除'),
|
|
1778
|
+
);
|
|
1779
|
+
}),
|
|
1764
1780
|
),
|
|
1765
1781
|
/* 「更换主题包」:用隐藏的 <input type=file accept=.zip> 唤起系统文件选择器(Electron 里就是原生对话框),
|
|
1766
1782
|
* 选中的 zip 原样 POST 到 /pighub/theme-import;宿主校验并解压后**自动切到新主题**,这里只负责刷新快照。 */
|
package/lib/index.js
CHANGED
|
@@ -44,7 +44,7 @@ export const inject = ['tools'];
|
|
|
44
44
|
import { startDesktopPet, stopDesktopPet, syncDesktopPet, desktopPetState, sayOnDesktop, reclaimStaleSessions } from './desktop-pet.js';
|
|
45
45
|
|
|
46
46
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
47
|
-
const VERSION = '0.9.
|
|
47
|
+
const VERSION = '0.9.70';
|
|
48
48
|
/** 桌面桌宠的精灵边长(CSS px 基准,= manifest 里 sizeRatio 的中位数)。 */
|
|
49
49
|
const DESKTOP_PET_SIZE = 96;
|
|
50
50
|
/** 全局尺寸系数(用户口径 2026-11-13:「整体猪猪缩小到 75% 大小」)。三层一起乘。 */
|
package/lib/themes.js
CHANGED
|
@@ -98,6 +98,11 @@ export function listThemes(builtinDir, userThemesDir) {
|
|
|
98
98
|
try { isDir = statSync(dir).isDirectory(); } catch { isDir = false; }
|
|
99
99
|
if (!isDir) continue;
|
|
100
100
|
seen.add(entry);
|
|
101
|
+
/* 用户口径 2026-11-14:「被拒绝的非法场景应该直接不出现在列表」。
|
|
102
|
+
* 所以这里对目录也走一遍准入校验:不合规的主题**根本不列出来**,
|
|
103
|
+
* 而不是列出来、点了一下什么都不发生。 */
|
|
104
|
+
const check = validateThemeDir(dir, entry);
|
|
105
|
+
if (!check.ok) continue;
|
|
101
106
|
const theme = readThemeDir(dir, entry);
|
|
102
107
|
items.push({ name: entry, title: theme.title || entry, builtin: false });
|
|
103
108
|
}
|
|
@@ -157,3 +162,22 @@ export function validateThemePack(meta, files, prefix) {
|
|
|
157
162
|
}
|
|
158
163
|
return { ok: true, reason: '', name };
|
|
159
164
|
}
|
|
165
|
+
|
|
166
|
+
/** 目录版准入校验:读目录里的文件清单 → 复用 validateThemePack。不合规的主题不进列表。 */
|
|
167
|
+
export function validateThemeDir(dir, name) {
|
|
168
|
+
try {
|
|
169
|
+
if (!dir || !existsSync(dir)) return { ok: false, reason: 'dir_missing' };
|
|
170
|
+
const jsonPath = join(dir, 'theme.json');
|
|
171
|
+
if (!existsSync(jsonPath)) return { ok: false, reason: 'theme_json_missing' };
|
|
172
|
+
let meta;
|
|
173
|
+
try { meta = JSON.parse(readFileSync(jsonPath, 'utf8')); } catch { return { ok: false, reason: 'theme_json_invalid' }; }
|
|
174
|
+
const files = [];
|
|
175
|
+
for (const entry of readdirSync(dir)) {
|
|
176
|
+
try { if (!statSync(join(dir, entry)).isFile()) continue; } catch { continue; }
|
|
177
|
+
files.push({ name: entry, data: null });
|
|
178
|
+
}
|
|
179
|
+
return validateThemePack(meta, files, '');
|
|
180
|
+
} catch (error) {
|
|
181
|
+
return { ok: false, reason: 'dir_read_error:' + String((error && error.message) || error).slice(0, 80) };
|
|
182
|
+
}
|
|
183
|
+
}
|