dsh-pighub 0.9.67 → 0.9.69

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 CHANGED
@@ -127,6 +127,13 @@ window.__ModuleLoader__.load({
127
127
  'overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--dsw-alias-text-secondary,#9aa4b2)}',
128
128
  '.pighub-gallery-cell.is-locked .pighub-gallery-name{letter-spacing:.12em;color:var(--dsw-alias-text-tertiary,#6b7280)}',
129
129
  '.pighub-gallery-stars{font-size:10px;line-height:1;color:#e8b64c;letter-spacing:.06em}',
130
+ // 主题二级菜单:收起时高度 0,展开用 max-height + opacity 过渡(用户口径 2026-11-14:要平滑动画)
131
+ '.pighub-themelist{max-height:0;opacity:0;overflow:hidden;transition:max-height .2s ease,opacity .2s ease,margin .2s ease;margin:0}',
132
+ '.pighub-themelist.is-open{max-height:240px;opacity:1;margin:4px 0}',
133
+ '.pighub-themerow{display:flex;align-items:center;justify-content:space-between;gap:6px;margin:3px 0}',
134
+ '.pighub-themedel{font-size:11px;padding:2px 8px;border-radius:999px;cursor:pointer;color:#f0b7b7;',
135
+ 'background:transparent;border:1px solid color-mix(in oklab,#7a3b3b 70%,transparent)}',
136
+ '.pighub-themedel.is-disabled{color:var(--dsw-alias-text-tertiary,#6b7280);border-color:transparent;cursor:default}',
130
137
  // 图鉴用的次级按钮(不复用 pighub-iconbtn:那是设置齿轮的专属样式,测试也按它选)
131
138
  // 图鉴按钮排在设置之后(测试按第一个图标按钮选设置),靠 order 摆到左边
132
139
  '.pighub-settings .pighub-ghostbtn{order:-1}',
@@ -621,6 +628,19 @@ window.__ModuleLoader__.load({
621
628
  catch (error) { set({ error: String((error && error.message) || error) }); }
622
629
  }
623
630
 
631
+ /* 主题切换/删除(用户口径 2026-11-14)。
632
+ * 必须定义在这里:设置浮层的 JSX 里直接调 request 会 ReferenceError
633
+ * (「点不动」的根因就是这个——探针实测 onClick 抛错 request is not defined)。 */
634
+ async function setTheme(name) {
635
+ try { apply(await request({ action: 'theme', name })); }
636
+ catch (error) { set({ error: String((error && error.message) || error) }); }
637
+ }
638
+
639
+ async function removeTheme(name) {
640
+ try { apply(await request({ action: 'theme-delete', name })); }
641
+ catch (error) { set({ error: String((error && error.message) || error) }); }
642
+ }
643
+
624
644
  async function refreshCatalog() {
625
645
  try {
626
646
  set({ phase: state.data ? state.phase : 'loading' });
@@ -634,6 +654,7 @@ window.__ModuleLoader__.load({
634
654
 
635
655
  return {
636
656
  get, set, subscribe, toast, load, syncRev, draw, sort, setPet, place, setMotion, savePosition, updatePrefs, refreshCatalog,
657
+ setTheme, removeTheme,
637
658
  // 拖动期间给 body 挂全局禁选:拖到面板外面时,选区不会落到下面的聊天区。
638
659
  beginDrag: (drag) => { setBodyDragging(true); set({ drag }); },
639
660
  moveDrag: (x, y, drag) => set({ drag: Object.assign({}, drag, { x, y }) }),
@@ -737,7 +758,10 @@ window.__ModuleLoader__.load({
737
758
  const name = theme && theme.name ? String(theme.name) : 'classic';
738
759
  const fence = (theme && theme.fence) || {};
739
760
  const file = (which === 'front' ? fence.front : fence.back) || fence.image || '';
740
- if (!file) return '/pighub/pen/pen-fence.png';
761
+ /* classic 的图**不在** assets/themes/classic 里,而是在包内 assets/img,由 /pighub/pen 服务。
762
+ * 早先这里统一拼 /pighub/theme/classic/pen-fence.png → 404 → 默认主题根本没有栏杆
763
+ * (用户 2026-11-14 报「默认的栏杆也没有」)。 */
764
+ if (!file || name === 'classic') return '/pighub/pen/' + encodeURIComponent(file || 'pen-fence.png');
741
765
  return '/pighub/theme/' + encodeURIComponent(name) + '/' + encodeURIComponent(String(file));
742
766
  }
743
767
 
@@ -1718,15 +1742,14 @@ window.__ModuleLoader__.load({
1718
1742
  onClick: () => setThemeMenuOpen((open) => !open),
1719
1743
  }, themeMenuOpen ? '收起' : '更换…'),
1720
1744
  ),
1721
- themeMenuOpen
1722
- ? h('div', { className: 'pighub-themelist' },
1745
+ h('div', { className: 'pighub-themelist' + (themeMenuOpen ? ' is-open' : '') },
1723
1746
  ((data && data.themes) || [{ name: 'classic', title: 'classic(默认)' }]).map((item) =>
1724
1747
  h('div', { key: 'theme-row-' + item.name, className: 'pighub-themerow' },
1725
1748
  h('button', {
1726
1749
  type: 'button',
1727
1750
  className: 'pighub-ghostbtn',
1728
1751
  'aria-pressed': activeTheme.name === item.name ? 'true' : 'false',
1729
- onClick: () => { request({ action: 'theme', name: item.name }).then(apply).catch(() => {}); },
1752
+ onClick: () => STORE.setTheme(item.name),
1730
1753
  }, (activeTheme.name === item.name ? '✓ ' : '') + (item.title || item.name)),
1731
1754
  item.name === 'classic'
1732
1755
  ? h('span', { className: 'pighub-themedel is-disabled' }, '内置')
@@ -1734,12 +1757,11 @@ window.__ModuleLoader__.load({
1734
1757
  type: 'button',
1735
1758
  className: 'pighub-themedel',
1736
1759
  title: '删除这个主题(当前主题会回落 classic)',
1737
- onClick: () => { request({ action: 'theme-delete', name: item.name }).then(apply).catch(() => {}); },
1760
+ onClick: () => STORE.removeTheme(item.name),
1738
1761
  }, '删除'),
1739
1762
  ),
1740
1763
  ),
1741
- )
1742
- : null,
1764
+ ),
1743
1765
  /* 「更换主题包」:用隐藏的 <input type=file accept=.zip> 唤起系统文件选择器(Electron 里就是原生对话框),
1744
1766
  * 选中的 zip 原样 POST 到 /pighub/theme-import;宿主校验并解压后**自动切到新主题**,这里只负责刷新快照。 */
1745
1767
  h('div', { className: 'row' },
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.67';
47
+ const VERSION = '0.9.69';
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-pighub",
3
- "version": "0.9.67",
3
+ "version": "0.9.69",
4
4
  "description": "猪猪库 PigHub 的 DeepSeek Harness 插件:侧边栏猪圈面板、每日一只猪猪、重复获得升星、可拖出的置顶桌宠,数据落在 ~/.PigHub。不内嵌任何上游图片。",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",