dsh-composer-ux 0.29.0 → 0.29.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,52 @@
2
2
 
3
3
  本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [0.29.2] — 2026-10-29(展开时把「账号与其他 ▴」缩进到账号行右端)
6
+
7
+ 用户 2026-10-29(截图圈住「方文」右侧的空白):「这个展开时,把这个账号和其他和收缩按钮放到这个位置」。
8
+
9
+ **改了什么**
10
+
11
+ - **展开态**:那行「账号与其他 ▴」不再单独占一整行,缩进到账号那一块(`settingsArea`)的**右端**,
12
+ 绝对定位、不占布局高度 —— 也就是你框的那个空白位置。
13
+ - **收起态**:仍是底部容器前面那一整条「账号与其他 ▾」(这时账号行被一起藏了,没有别的落脚点)。
14
+
15
+ **实现**:同一个按钮在两种状态下换父节点(收起 → 容器前面的兄弟节点;展开 → 账号块内的绝对定位),
16
+ 只动我们自己那一个按钮的 parent/属性;账号块只加一个 `position: relative`(做包含块),
17
+ 宿主与别的插件的节点照旧一个不碰。
18
+
19
+ **顺手修了测试桩的一个失真**:桩的 `appendChild/insertBefore` 之前不会把节点从旧父节点摘下来
20
+ (真 DOM 会),于是"移动按钮"这件事在测试里会变成两个按钮。桩已按真实语义修正。
21
+
22
+ **测试**:`test/sidebar-footer.mjs` 新增第 6 节(9 条);变异新增 TE(展开时还在老位置占一行)。
23
+
24
+ ## [0.29.1] — 2026-10-29(「账号与其他」+ 收起状态不再记忆:打开就收起)
25
+
26
+ 用户 2026-10-29 两条:
27
+
28
+ 1. 「账号与用量改为账号与其他」;
29
+ 2. 「收起与否改为默认收起」—— 追问后选定**改行为**:每次打开 DSH 都强制收起、**不记住展开**,
30
+ 两处(顶部「全局面板」+ 底部区)一起。
31
+
32
+ **行为变了(这条最容易察觉)**
33
+
34
+ - 以前:点开折叠条 → 状态写进 `composer-ux-nav-fold.json` → 下次打开还是展开的。
35
+ - 现在:点开只在**本次会话**有效;重开 DSH 一律回到收起。
36
+ 文件里只留**形态**(`mode`:收成一条 / 只留图标 / 不折叠);老文件里的 `collapsed: false`
37
+ 会被**忽略**,不会把"上次点开过"带过来。
38
+
39
+ **界面上的连带改动**
40
+
41
+ - 底部细条文字:`账号与用量` → **`账号与其他`**(连带 title「展开/收起账号与其他」)。
42
+ - 设置页「侧边栏」组:两个"现在收起"开关删掉(一个不落盘的开关是骗人的),
43
+ 换成一行状态说明「打开时:总是收起(不记住展开):点折叠条可临时展开」+ 当前是收起/临时展开。
44
+
45
+ **为什么拆了个新文件**:把纯状态(`NavFoldState` / `sanitizeNavFold` / `persistableNavFold`)从
46
+ `nav-fold-store.ts` 挪到 `nav-fold-state.ts` —— store 里带 React hook,测试要 bundle 它就得拉 react;
47
+ 拆开之后"强制收起"和"只有形态落盘"这两条能像其它纯函数一样逐条钉住。
48
+
49
+ **测试**:`test/sidebar-nav.mjs` 新增第 7 节(6 条);变异新增 TC / TD(又记住展开 / 收起状态被写盘)。
50
+
5
51
  ## [0.29.0] — 2026-10-29(侧边栏**底部区**也能收:整块收成一行「账号与用量」)
6
52
 
7
53
  用户 2026-10-29(截图圈住左下角「今日消耗金额」卡片 + 下面那行账号):
package/README.md CHANGED
@@ -135,7 +135,10 @@ dsh plugin --profile web add <你克隆或解压出来的目录>
135
135
  ### 侧边栏底部区(0.29.0)
136
136
 
137
137
  左下角那一块(账号行 + 插件塞在这里的卡片,比如「今日消耗金额」)也能收:**整块收成一行
138
- 「账号与用量 ▾」**,点一下展开。设置 → 输入体验 →「设置面板」卡 →「侧边栏」组 →「底部区」(默认收起)。
138
+ 「账号与其他 ▾」**,点一下展开。设置 → 输入体验 →「设置面板」卡 →「侧边栏」组可以看到当前状态。
139
+
140
+ **两处都是"打开就收起、不记住展开"**(0.29.1):点折叠条只在本次会话有效,重开 DSH 一律回到收起;
141
+ 文件里只记"形态"这一项偏好。
139
142
 
140
143
  做法是**整块藏/显**:只在最外层容器挂一个属性,**不碰里面任何一个节点** —— 所以别的插件往这个插槽里
141
144
  放什么都不会被我们改坏(用量卡片就是 `@kenz1117/dsh-ui-usage-billing` 放在 `sidebar.footer.action` 里的)。
@@ -681,7 +684,7 @@ node build.mjs # 产出 lib/index.js + lib/client.js
681
684
  # 里同版本的 npm 包(两者逐字节相同)——CI 上走的就是退路
682
685
  npm test # 30 个套件;当前 2621 passed, 0 failed(2026-10-28 实测;CI 三平台同样全绿)
683
686
  # 走 scripts/run-tests.mjs:顺带把"多少套件/多少条"记进 test/.last-run.json
684
- npm run test:mutations # 手动跑:变异测试,证明那套护栏真的在咬人(203 条,须单独跑)
687
+ npm run test:mutations # 手动跑:变异测试,证明那套护栏真的在咬人(206 条,须单独跑)
685
688
  # 同样记录结果,供下面的文档门禁核对
686
689
  npm run gates # 发版门禁三条一起跑:tag 指向 / 包内容 / 文档数字
687
690
  npm run check:tag # ① tag 名里的版本 == 该 tag 所指提交里的 package.json 版本
package/dsh-plugin.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "manifestVersion": "0.15",
4
4
  "id": "dsh-composer-ux.input-experience",
5
5
  "name": "dsh-composer-ux",
6
- "version": "0.29.0",
6
+ "version": "0.29.2",
7
7
  "license": "MIT",
8
8
  "facets": {
9
9
  "host": {
package/lib/client.js CHANGED
@@ -2715,12 +2715,24 @@ var import_react3 = require("react");
2715
2715
 
2716
2716
  // src/client/nav-fold-store.ts
2717
2717
  var import_react2 = require("react");
2718
+
2719
+ // src/client/nav-fold-state.ts
2718
2720
  var NAV_FOLD_MODES = [
2719
2721
  { id: "off", label: "\u4E0D\u6298\u53E0" },
2720
2722
  { id: "bar", label: "\u6536\u6210\u4E00\u6761" },
2721
2723
  { id: "icons", label: "\u53EA\u7559\u56FE\u6807" }
2722
2724
  ];
2723
2725
  var DEFAULT_NAV_FOLD = { mode: "bar", collapsed: true, footerCollapsed: true };
2726
+ function sanitizeNavFold(value) {
2727
+ const raw = value ?? {};
2728
+ const mode = raw.mode === "off" || raw.mode === "icons" || raw.mode === "bar" ? raw.mode : DEFAULT_NAV_FOLD.mode;
2729
+ return { mode, collapsed: true, footerCollapsed: true };
2730
+ }
2731
+ function persistableNavFold(patch) {
2732
+ return patch.mode === void 0 ? {} : { mode: patch.mode };
2733
+ }
2734
+
2735
+ // src/client/nav-fold-store.ts
2724
2736
  var state2 = DEFAULT_NAV_FOLD;
2725
2737
  var listeners = /* @__PURE__ */ new Set();
2726
2738
  function subscribeNavFold(listener) {
@@ -2735,15 +2747,6 @@ function setLocal(next) {
2735
2747
  state2 = merged;
2736
2748
  for (const listener of listeners) listener();
2737
2749
  }
2738
- function sanitizeNavFold(value) {
2739
- const raw = value ?? {};
2740
- const mode = raw.mode === "off" || raw.mode === "icons" || raw.mode === "bar" ? raw.mode : DEFAULT_NAV_FOLD.mode;
2741
- return {
2742
- mode,
2743
- collapsed: typeof raw.collapsed === "boolean" ? raw.collapsed : DEFAULT_NAV_FOLD.collapsed,
2744
- footerCollapsed: typeof raw.footerCollapsed === "boolean" ? raw.footerCollapsed : DEFAULT_NAV_FOLD.footerCollapsed
2745
- };
2746
- }
2747
2750
  async function loadNavFold() {
2748
2751
  try {
2749
2752
  const response = await fetch(NAV_FOLD_API_PATH, { headers: { accept: "application/json" } });
@@ -2755,11 +2758,13 @@ async function loadNavFold() {
2755
2758
  }
2756
2759
  async function saveNavFold(patch) {
2757
2760
  setLocal(patch);
2761
+ const body = persistableNavFold(patch);
2762
+ if (body.mode === void 0) return "";
2758
2763
  try {
2759
2764
  const response = await fetch(NAV_FOLD_API_PATH, {
2760
2765
  method: "POST",
2761
2766
  headers: { "content-type": "application/json" },
2762
- body: JSON.stringify(patch)
2767
+ body: JSON.stringify(body)
2763
2768
  });
2764
2769
  if (!response.ok) return `\u4FDD\u5B58\u6298\u53E0\u72B6\u6001\u5931\u8D25\uFF1A\u5BBF\u4E3B\u8FD4\u56DE HTTP ${response.status}`;
2765
2770
  const payload = await response.json();
@@ -2940,7 +2945,8 @@ function applyNavFold(doc, state3, onToggle) {
2940
2945
  var FOOTER_FOLD_ATTR = "data-ux-footer-fold";
2941
2946
  var FOOTER_HEAD_ATTR = "data-ux-footer-fold-head";
2942
2947
  var FOOTER_FOLD_STYLE_ATTR = "data-ux-footer-fold-style";
2943
- var FOOTER_HEAD_TEXT = "\u8D26\u53F7\u4E0E\u7528\u91CF";
2948
+ var FOOTER_HEAD_INLINE_ATTR = "data-ux-footer-inline";
2949
+ var FOOTER_HEAD_TEXT = "\u8D26\u53F7\u4E0E\u5176\u4ED6";
2944
2950
  var ACCOUNT_LABELS = ["\u8D26\u53F7\u83DC\u5355", "Account menu", "\u8D26\u6237\u83DC\u5355"];
2945
2951
  var FOOTER_FOLD_CSS = `
2946
2952
  [${FOOTER_FOLD_ATTR}='folded'] { display: none !important; }
@@ -2961,6 +2967,17 @@ var FOOTER_FOLD_CSS = `
2961
2967
  cursor: pointer;
2962
2968
  }
2963
2969
  [${FOOTER_HEAD_ATTR}]:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-secondary); }
2970
+ /* \u5C55\u5F00\u6001\uFF1A\u8D34\u5728\u8D26\u53F7\u90A3\u4E00\u884C\u7684\u53F3\u7AEF\uFF0C\u4E0D\u5360\u5E03\u5C40\u9AD8\u5EA6\uFF08\u7EDD\u5BF9\u5B9A\u4F4D\u5728\u8D26\u53F7\u5757\u91CC\uFF09\u3002 */
2971
+ [${FOOTER_HEAD_ATTR}][${FOOTER_HEAD_INLINE_ATTR}] {
2972
+ position: absolute;
2973
+ right: 6px;
2974
+ top: 50%;
2975
+ transform: translateY(-50%);
2976
+ width: auto;
2977
+ max-width: 55%;
2978
+ padding: 2px 6px;
2979
+ z-index: 1;
2980
+ }
2964
2981
  [${FOOTER_HEAD_ATTR}]:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: -2px; }
2965
2982
  [${FOOTER_HEAD_ATTR}] > span[data-ux-footer-fold-chevron] { margin-left: auto; opacity: .7; }
2966
2983
  `;
@@ -2996,11 +3013,23 @@ function ensureHeader2(doc, footer, state3, onToggle) {
2996
3013
  head = doc.createElement("button");
2997
3014
  head.type = "button";
2998
3015
  head.setAttribute(FOOTER_HEAD_ATTR, "");
2999
- footer.parentElement?.insertBefore(head, footer);
3016
+ }
3017
+ const accountArea = footer.lastElementChild;
3018
+ const inline = !state3.collapsed && accountArea !== null;
3019
+ const parent = inline ? accountArea : footer.parentElement;
3020
+ if (parent !== null && head.parentElement !== parent) {
3021
+ if (inline) parent.appendChild(head);
3022
+ else parent.insertBefore(head, footer);
3023
+ }
3024
+ if (inline && accountArea !== null) {
3025
+ if (accountArea.style.position !== "relative") accountArea.style.position = "relative";
3026
+ if (!head.hasAttribute(FOOTER_HEAD_INLINE_ATTR)) head.setAttribute(FOOTER_HEAD_INLINE_ATTR, "");
3027
+ } else if (head.hasAttribute(FOOTER_HEAD_INLINE_ATTR)) {
3028
+ head.removeAttribute(FOOTER_HEAD_INLINE_ATTR);
3000
3029
  }
3001
3030
  const open = !state3.collapsed;
3002
3031
  head.setAttribute("aria-expanded", open ? "true" : "false");
3003
- head.setAttribute("title", open ? "\u6536\u8D77\u8D26\u53F7\u4E0E\u7528\u91CF" : "\u5C55\u5F00\u8D26\u53F7\u4E0E\u7528\u91CF");
3032
+ head.setAttribute("title", open ? "\u6536\u8D77\u8D26\u53F7\u4E0E\u5176\u4ED6" : "\u5C55\u5F00\u8D26\u53F7\u4E0E\u5176\u4ED6");
3004
3033
  head.textContent = "";
3005
3034
  const label = doc.createElement("span");
3006
3035
  label.textContent = FOOTER_HEAD_TEXT;
@@ -6993,38 +7022,18 @@ function SettingsSection({ useLive, useBook, useBookStatus, useWriteNotice, acti
6993
7022
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
6994
7023
  Row,
6995
7024
  {
6996
- title: "\u73B0\u5728\u6536\u8D77",
6997
- desc: navFold.mode === "off" ? "\u5F62\u6001\u662F\u300C\u4E0D\u6298\u53E0\u300D\uFF0C\u8FD9\u4E2A\u5F00\u5173\u4E0D\u8D77\u4F5C\u7528" : "\u8BB0\u4F4F\u8FD9\u4E2A\u72B6\u6001\uFF1A\u4E0B\u6B21\u6253\u5F00 DSH \u6CBF\u7528",
6998
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
6999
- Switch,
7000
- {
7001
- small: true,
7002
- label: "\u73B0\u5728\u6536\u8D77\u5168\u5C40\u9762\u677F",
7003
- checked: navFold.collapsed,
7004
- disabled: navFold.mode === "off",
7005
- onChange: (next) => {
7006
- void saveNavFold({ collapsed: next });
7007
- }
7008
- }
7009
- )
7025
+ title: "\u6253\u5F00\u65F6",
7026
+ desc: navFold.mode === "off" ? "\u5F62\u6001\u662F\u300C\u4E0D\u6298\u53E0\u300D\uFF0C\u8FD9\u91CC\u4E0D\u8D77\u4F5C\u7528" : "\u603B\u662F\u6536\u8D77\uFF08\u4E0D\u8BB0\u4F4F\u5C55\u5F00\uFF09\uFF1A\u70B9\u6298\u53E0\u6761\u53EF\u4E34\u65F6\u5C55\u5F00\uFF0C\u91CD\u5F00 DSH \u53C8\u56DE\u5230\u6536\u8D77",
7027
+ first: false,
7028
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontSize: 12, opacity: 0.75 }, children: navFold.collapsed ? "\u6536\u8D77\u4E2D" : "\u5DF2\u4E34\u65F6\u5C55\u5F00\uFF08\u4EC5\u672C\u6B21\uFF09" })
7010
7029
  }
7011
7030
  ),
7012
7031
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7013
7032
  Row,
7014
7033
  {
7015
7034
  title: "\u5E95\u90E8\u533A",
7016
- desc: "\u8D26\u53F7\u884C\u4E0E\u63D2\u4EF6\u585E\u5728\u8FD9\u91CC\u7684\u5361\u7247\uFF08\u6BD4\u5982\u4ECA\u65E5\u6D88\u8017\u91D1\u989D\uFF09",
7017
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
7018
- Switch,
7019
- {
7020
- small: true,
7021
- label: "\u73B0\u5728\u6536\u8D77\u4FA7\u8FB9\u680F\u5E95\u90E8\u533A",
7022
- checked: navFold.footerCollapsed,
7023
- onChange: (next) => {
7024
- void saveNavFold({ footerCollapsed: next });
7025
- }
7026
- }
7027
- )
7035
+ desc: "\u8D26\u53F7\u884C\u4E0E\u63D2\u4EF6\u585E\u5728\u8FD9\u91CC\u7684\u5361\u7247\uFF08\u6BD4\u5982\u4ECA\u65E5\u6D88\u8017\u91D1\u989D\uFF09\uFF1B\u6253\u5F00\u65F6\u603B\u662F\u6536\u8D77\uFF0C\u70B9\u300C\u8D26\u53F7\u4E0E\u5176\u4ED6\u300D\u53EF\u4E34\u65F6\u5C55\u5F00",
7036
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { style: { fontSize: 12, opacity: 0.75 }, children: navFold.footerCollapsed ? "\u6536\u8D77\u4E2D" : "\u5DF2\u4E34\u65F6\u5C55\u5F00\uFF08\u4EC5\u672C\u6B21\uFF09" })
7028
7037
  }
7029
7038
  )
7030
7039
  ]