@jingyi0605/codingns4dsh 0.1.0 → 0.1.1

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.
Files changed (216) hide show
  1. package/README.en.md +306 -0
  2. package/README.md +190 -993
  3. package/data/build/dist/client/account-bar.d.ts +2 -2
  4. package/data/build/dist/client/account-bar.js +96 -39
  5. package/data/build/dist/client/bundle.js +13722 -3611
  6. package/data/build/dist/client/cli-catalog.js +20 -3
  7. package/data/build/dist/client/cli-slots.d.ts +5 -0
  8. package/data/build/dist/client/cli-slots.js +57 -7
  9. package/data/build/dist/client/debug/ui.d.ts +1 -1
  10. package/data/build/dist/client/debug/ui.js +62 -32
  11. package/data/build/dist/client/dsh-runtime-version.d.ts +9 -2
  12. package/data/build/dist/client/dsh-runtime-version.js +24 -2
  13. package/data/build/dist/client/features/cli-adapters.d.ts +1 -1
  14. package/data/build/dist/client/features/cli-adapters.js +19 -16
  15. package/data/build/dist/client/features/debug.js +2 -2
  16. package/data/build/dist/client/features/index.d.ts +2 -1
  17. package/data/build/dist/client/features/index.js +3 -1
  18. package/data/build/dist/client/features/lan-access-panel.d.ts +1 -1
  19. package/data/build/dist/client/features/lan-access-panel.js +8 -10
  20. package/data/build/dist/client/features/lan-access.js +1 -1
  21. package/data/build/dist/client/features/login-protection-panel.d.ts +1 -1
  22. package/data/build/dist/client/features/login-protection-panel.js +7 -9
  23. package/data/build/dist/client/features/reverse-proxy-panel.d.ts +1 -1
  24. package/data/build/dist/client/features/reverse-proxy-panel.js +99 -11
  25. package/data/build/dist/client/features/reverse-proxy.d.ts +1 -1
  26. package/data/build/dist/client/features/reverse-proxy.js +19 -18
  27. package/data/build/dist/client/features/terminal-enhancement-panel.d.ts +1 -1
  28. package/data/build/dist/client/features/terminal-enhancement-panel.js +62 -55
  29. package/data/build/dist/client/features/terminal-enhancement.d.ts +1 -1
  30. package/data/build/dist/client/features/terminal-enhancement.js +3 -3
  31. package/data/build/dist/client/features/types.d.ts +14 -3
  32. package/data/build/dist/client/features/workspace-session-enhancement-panel.d.ts +1 -1
  33. package/data/build/dist/client/features/workspace-session-enhancement-panel.js +31 -9
  34. package/data/build/dist/client/features/workspace-session-enhancement.js +72 -11
  35. package/data/build/dist/client/git-management.d.ts +16 -0
  36. package/data/build/dist/client/git-management.js +757 -0
  37. package/data/build/dist/client/index.d.ts +3 -2
  38. package/data/build/dist/client/index.js +152 -27
  39. package/data/build/dist/client/locale.js +91 -29
  40. package/data/build/dist/client/quick-phrase-layout.d.ts +49 -0
  41. package/data/build/dist/client/quick-phrase-layout.js +95 -0
  42. package/data/build/dist/client/quick-phrase-slot.d.ts +7 -0
  43. package/data/build/dist/client/quick-phrase-slot.js +370 -0
  44. package/data/build/dist/client/remote-web-context.js +1 -1
  45. package/data/build/dist/client/settings-bridge.d.ts +9 -6
  46. package/data/build/dist/client/settings-bridge.js +51 -27
  47. package/data/build/dist/client/settings-section.d.ts +3 -2
  48. package/data/build/dist/client/settings-section.js +33 -19
  49. package/data/build/dist/client/subscription-slot.d.ts +3 -0
  50. package/data/build/dist/client/subscription-slot.js +108 -16
  51. package/data/build/dist/client/terminal/model.d.ts +14 -2
  52. package/data/build/dist/client/terminal/model.js +109 -21
  53. package/data/build/dist/client/terminal/recovery.d.ts +50 -0
  54. package/data/build/dist/client/terminal/recovery.js +101 -0
  55. package/data/build/dist/client/terminal/ui.d.ts +2 -2
  56. package/data/build/dist/client/terminal/ui.js +49 -11
  57. package/data/build/dist/client/terminal/xterm-view.d.ts +2 -2
  58. package/data/build/dist/client/terminal/xterm-view.js +2 -1
  59. package/data/build/dist/client/theme.d.ts +6 -1
  60. package/data/build/dist/client/theme.js +32 -1
  61. package/data/build/dist/client/workspace-session-archive-dom.d.ts +12 -0
  62. package/data/build/dist/client/workspace-session-archive-dom.js +27 -5
  63. package/data/build/dist/client/workspace-session-visibility-dom.d.ts +30 -0
  64. package/data/build/dist/client/workspace-session-visibility-dom.js +604 -0
  65. package/data/build/dist/dsh-capabilities/client/config-forms-adapter.d.ts +50 -0
  66. package/data/build/dist/dsh-capabilities/client/config-forms-adapter.js +139 -0
  67. package/data/build/dist/dsh-capabilities/client/conversation-adapter.d.ts +7 -0
  68. package/data/build/dist/dsh-capabilities/client/conversation-adapter.js +6 -0
  69. package/data/build/dist/dsh-capabilities/client/locale-adapter.d.ts +9 -0
  70. package/data/build/dist/dsh-capabilities/client/locale-adapter.js +4 -0
  71. package/data/build/dist/dsh-capabilities/client/primitives-adapter.d.ts +10 -0
  72. package/data/build/dist/dsh-capabilities/client/primitives-adapter.js +14 -0
  73. package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.d.ts +8 -0
  74. package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.js +16 -0
  75. package/data/build/dist/dsh-capabilities/client/sidebar-adapter.d.ts +7 -0
  76. package/data/build/dist/dsh-capabilities/client/sidebar-adapter.js +5 -0
  77. package/data/build/dist/dsh-capabilities/client/theme-adapter.d.ts +6 -0
  78. package/data/build/dist/dsh-capabilities/client/theme-adapter.js +4 -0
  79. package/data/build/dist/dsh-capabilities/client/typert-adapter.d.ts +5 -0
  80. package/data/build/dist/dsh-capabilities/client/typert-adapter.js +4 -0
  81. package/data/build/dist/dsh-capabilities/host/config-forms-adapter.d.ts +19 -0
  82. package/data/build/dist/dsh-capabilities/host/config-forms-adapter.js +45 -0
  83. package/data/build/dist/dsh-capabilities/host/connection-rpc-adapter.d.ts +19 -0
  84. package/data/build/dist/dsh-capabilities/host/connection-rpc-adapter.js +13 -0
  85. package/data/build/dist/dsh-capabilities/host/settings-scope-adapter.d.ts +8 -0
  86. package/data/build/dist/dsh-capabilities/host/settings-scope-adapter.js +47 -0
  87. package/data/build/dist/dsh-capabilities/host/typert-adapter.d.ts +6 -0
  88. package/data/build/dist/dsh-capabilities/host/typert-adapter.js +4 -0
  89. package/data/build/dist/dsh-capabilities/index.d.ts +6 -0
  90. package/data/build/dist/dsh-capabilities/index.js +6 -0
  91. package/data/build/dist/dsh-capabilities/matrix.d.ts +15 -0
  92. package/data/build/dist/dsh-capabilities/matrix.js +32 -0
  93. package/data/build/dist/dsh-capabilities/registry.d.ts +22 -0
  94. package/data/build/dist/dsh-capabilities/registry.js +196 -0
  95. package/data/build/dist/dsh-capabilities/routes.d.ts +10 -0
  96. package/data/build/dist/dsh-capabilities/routes.js +34 -0
  97. package/data/build/dist/dsh-capabilities/settings-store.d.ts +29 -0
  98. package/data/build/dist/dsh-capabilities/settings-store.js +5 -0
  99. package/data/build/dist/dsh-capabilities/types.d.ts +50 -0
  100. package/data/build/dist/dsh-capabilities/types.js +23 -0
  101. package/data/build/dist/features/registry.d.ts +8 -2
  102. package/data/build/dist/features/registry.js +104 -1
  103. package/data/build/dist/host/auth-session.d.ts +8 -1
  104. package/data/build/dist/host/auth-session.js +28 -6
  105. package/data/build/dist/host/cli-adapters/claude-driver.d.ts +2 -0
  106. package/data/build/dist/host/cli-adapters/claude-driver.js +19 -4
  107. package/data/build/dist/host/cli-adapters/claude-model-options.d.ts +14 -0
  108. package/data/build/dist/host/cli-adapters/claude-model-options.js +157 -0
  109. package/data/build/dist/host/cli-adapters/codex-driver.d.ts +11 -0
  110. package/data/build/dist/host/cli-adapters/codex-driver.js +166 -14
  111. package/data/build/dist/host/cli-adapters/driver.d.ts +2 -0
  112. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +15 -0
  113. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +120 -29
  114. package/data/build/dist/host/cli-adapters/dsh-tool-history.d.ts +4 -1
  115. package/data/build/dist/host/cli-adapters/dsh-tool-history.js +25 -7
  116. package/data/build/dist/host/cli-adapters/feature.js +74 -8
  117. package/data/build/dist/host/cli-adapters/gemini-driver.js +2 -2
  118. package/data/build/dist/host/cli-adapters/grok-driver.js +2 -2
  119. package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +4 -2
  120. package/data/build/dist/host/cli-adapters/json-rpc-process.js +8 -9
  121. package/data/build/dist/host/cli-adapters/kimi-driver.js +1 -1
  122. package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts +17 -0
  123. package/data/build/dist/host/cli-adapters/legacy-session-adapter.js +88 -0
  124. package/data/build/dist/host/cli-adapters/legacy-session-settings.d.ts +24 -0
  125. package/data/build/dist/host/cli-adapters/legacy-session-settings.js +162 -0
  126. package/data/build/dist/host/cli-adapters/official-provider-subscription.d.ts +29 -0
  127. package/data/build/dist/host/cli-adapters/official-provider-subscription.js +361 -0
  128. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +2 -0
  129. package/data/build/dist/host/cli-adapters/opencode-driver.js +97 -12
  130. package/data/build/dist/host/cli-adapters/pi-driver.js +69 -23
  131. package/data/build/dist/host/cli-adapters/provider-registry.d.ts +33 -0
  132. package/data/build/dist/host/cli-adapters/provider-registry.js +83 -0
  133. package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +25 -3
  134. package/data/build/dist/host/cli-adapters/provider-subscription.js +346 -30
  135. package/data/build/dist/host/cli-adapters/registry.d.ts +6 -0
  136. package/data/build/dist/host/cli-adapters/registry.js +170 -13
  137. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +10 -0
  138. package/data/build/dist/host/cli-adapters/session-store.d.ts +7 -0
  139. package/data/build/dist/host/cli-adapters/session-store.js +39 -1
  140. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -0
  141. package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts +3 -0
  142. package/data/build/dist/host/cli-adapters/stream-normalizer.js +22 -2
  143. package/data/build/dist/host/control-api-client.d.ts +3 -3
  144. package/data/build/dist/host/control-api-client.js +2 -1
  145. package/data/build/dist/host/credential-store.js +11 -1
  146. package/data/build/dist/host/dsh-device-runtime.d.ts +4 -0
  147. package/data/build/dist/host/dsh-device-runtime.js +20 -9
  148. package/data/build/dist/host/dsh-runtime-version.d.ts +3 -2
  149. package/data/build/dist/host/dsh-runtime-version.js +71 -2
  150. package/data/build/dist/host/features/auth.js +8 -5
  151. package/data/build/dist/host/features/debug.js +1 -1
  152. package/data/build/dist/host/features/git-management.d.ts +5 -0
  153. package/data/build/dist/host/features/git-management.js +290 -0
  154. package/data/build/dist/host/features/host-status.js +1 -1
  155. package/data/build/dist/host/features/index.d.ts +1 -0
  156. package/data/build/dist/host/features/index.js +3 -0
  157. package/data/build/dist/host/features/terminal-process.js +1 -1
  158. package/data/build/dist/host/features/terminal-status.d.ts +3 -1
  159. package/data/build/dist/host/features/terminal-status.js +3 -1
  160. package/data/build/dist/host/index.d.ts +7 -3
  161. package/data/build/dist/host/index.js +79 -15
  162. package/data/build/dist/host/lan-access-dsh.js +18 -5
  163. package/data/build/dist/host/native-session-bridge.d.ts +23 -1
  164. package/data/build/dist/host/native-session-bridge.js +157 -10
  165. package/data/build/dist/host/rpc-table.d.ts +1 -1
  166. package/data/build/dist/host/rpc.js +60 -10
  167. package/data/build/dist/host/session-migration-repair.d.ts +19 -0
  168. package/data/build/dist/host/session-migration-repair.js +395 -0
  169. package/data/build/dist/host/settings.d.ts +8 -1
  170. package/data/build/dist/host/settings.js +94 -3
  171. package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +1 -0
  172. package/data/build/dist/host/terminal/backends/tmux-backend.js +4 -2
  173. package/data/build/dist/host/terminal/controller-factory.d.ts +4 -0
  174. package/data/build/dist/host/terminal/controller-factory.js +26 -7
  175. package/data/build/dist/host/terminal/runtime-manager.d.ts +2 -1
  176. package/data/build/dist/host/terminal/runtime-manager.js +3 -0
  177. package/data/build/dist/host/terminal/startup.d.ts +2 -0
  178. package/data/build/dist/host/terminal/startup.js +2 -1
  179. package/data/build/dist/host/terminal/terminal-controller.d.ts +10 -3
  180. package/data/build/dist/host/terminal/terminal-controller.js +101 -15
  181. package/data/build/dist/host/terminal/terminal-process-service.js +3 -1
  182. package/data/build/dist/host/terminal/terminal-service.d.ts +2 -0
  183. package/data/build/dist/host/terminal/terminal-service.js +10 -0
  184. package/data/build/dist/index.d.ts +6 -2
  185. package/data/build/dist/index.js +8 -2
  186. package/data/build/dist/shared/contracts/cli-adapter.d.ts +14 -0
  187. package/data/build/dist/shared/contracts/config.d.ts +31 -1
  188. package/data/build/dist/shared/contracts/config.js +25 -0
  189. package/data/build/dist/shared/contracts/dsh-device.d.ts +13 -1
  190. package/data/build/dist/shared/contracts/errors.d.ts +1 -1
  191. package/data/build/dist/shared/contracts/feature.d.ts +6 -0
  192. package/data/build/dist/shared/contracts/git.d.ts +82 -0
  193. package/data/build/dist/shared/contracts/git.js +3 -0
  194. package/data/build/dist/shared/contracts/subscription.d.ts +46 -0
  195. package/data/build/dist/shared/contracts/terminal.d.ts +4 -1
  196. package/data/build/dist/shared/contracts/version.d.ts +2 -2
  197. package/data/build/dist/shared/contracts/version.js +4 -6
  198. package/data/build/dist/shared/debug.d.ts +14 -0
  199. package/data/build/dist/shared/debug.js +53 -0
  200. package/data/build/dist/shared/index.d.ts +7 -4
  201. package/data/build/dist/shared/index.js +2 -1
  202. package/data/build/dist/transport/debug.d.ts +1 -1
  203. package/data/build/dist/transport/debug.js +3 -30
  204. package/data/build/dist/transport/dsh-session.js +3 -3
  205. package/data/build/dist/typert.host.d.ts +1 -1
  206. package/data/build/dist/typert.host.js +6 -3
  207. package/data/build/dist/typert.remote-client.d.ts +11 -0
  208. package/data/build/dist/typert.remote-client.js +7 -0
  209. package/dsh.bundle.patch +1 -1
  210. package/package.json +13 -5
  211. package/profile/README.md +4 -4
  212. package/profile/package.json +6 -4
  213. package/profile/scripts/check-dsh-install.mjs +2 -2
  214. package/profile/version.json +2 -2
  215. package/scripts/check-dsh-install.mjs +2 -2
  216. package/version.json +2 -2
@@ -0,0 +1,604 @@
1
+ import { findWorkspaceContainer, findWorkspaceHeaders, loadWorkspaceRecords, resolveWorkspaceId, } from './workspace-session-archive-dom.js';
2
+ /** 工作区隐藏注入节点的标记,便于重复扫描和停用时完整清理。 */
3
+ export const WORKSPACE_SESSION_HIDDEN_ATTRIBUTE = 'data-codingns-hidden-workspace';
4
+ export const WORKSPACE_SESSION_HIDDEN_MENU_ATTRIBUTE = 'data-codingns-hidden-workspace-menu';
5
+ export const WORKSPACE_SESSION_HIDDEN_LIST_ATTRIBUTE = 'data-codingns-hidden-workspaces';
6
+ const WORKSPACE_SESSION_HIDDEN_LIST_STATE_ATTRIBUTE = 'data-codingns-hidden-workspaces-state';
7
+ const WORKSPACE_SESSION_HIDDEN_MENU_WORKSPACE_ATTRIBUTE = 'data-codingns-hidden-workspace-id';
8
+ export const WORKSPACE_SESSION_HIDDEN_FILTER_ATTRIBUTE = 'data-codingns-hidden-workspace-filter';
9
+ export const WORKSPACE_SESSION_HIDDEN_FILTER_CHECK_ATTRIBUTE = 'data-codingns-hidden-workspace-filter-check';
10
+ const WORKSPACE_SESSION_HIDDEN_FILTER_BOUND_ATTRIBUTE = 'data-codingns-hidden-workspace-filter-bound';
11
+ const MENU_ATTRIBUTE = 'aria-haspopup';
12
+ const WORKSPACE_MENU_PATTERN = /(?:更多|菜单|选项|more|menu|option)/iu;
13
+ const WORKSPACE_FILTER_PATTERN = /(?:视图选项|筛选选项|view\s*options|filter\s*options)/iu;
14
+ /**
15
+ * 给原生工作区菜单增加隐藏动作,并通过工作区筛选菜单控制恢复列表。
16
+ *
17
+ * DSH 当前版本没有公开工作区可见性 API,因此这里仅隐藏原生 DOM,工作区
18
+ * 本身仍由 DSH Workspace Controller 管理;隐藏 ID 存在 Codingns4DSH 设置中。
19
+ */
20
+ export function startWorkspaceSessionVisibilityDom(options = {}) {
21
+ const dom = options.document ?? (typeof document === 'undefined' ? undefined : document);
22
+ const Observer = options.MutationObserver
23
+ ?? (typeof MutationObserver === 'undefined' ? undefined : MutationObserver);
24
+ const remote = options.remote;
25
+ let hiddenWorkspaceIds = new Set(normalizeIds(options.hiddenWorkspaceIds ?? []));
26
+ let workspaces = [];
27
+ let disposed = false;
28
+ let scanQueued = false;
29
+ let loading;
30
+ let activeMenuWorkspaceId;
31
+ let activeMenuKind;
32
+ let activeFilterTrigger;
33
+ let suppressFilterTrigger = false;
34
+ let menuContextPending = false;
35
+ let showHiddenWorkspaces = false;
36
+ let menuRescanTimer;
37
+ let knownNativeMenus = new Set();
38
+ let changeSequence = 0;
39
+ const menuWorkspaceIds = new WeakMap();
40
+ const persist = options.onHiddenWorkspaceIdsChange;
41
+ const updateVisibility = (workspaceId, hidden) => {
42
+ const previous = [...hiddenWorkspaceIds];
43
+ const change = ++changeSequence;
44
+ const next = new Set(previous);
45
+ if (hidden)
46
+ next.add(workspaceId);
47
+ else
48
+ next.delete(workspaceId);
49
+ const normalized = normalizeIds([...next]);
50
+ hiddenWorkspaceIds = new Set(normalized);
51
+ scheduleScan();
52
+ if (persist === undefined)
53
+ return;
54
+ Promise.resolve(persist(normalized)).catch(() => {
55
+ if (change !== changeSequence)
56
+ return;
57
+ hiddenWorkspaceIds = new Set(previous);
58
+ scheduleScan();
59
+ });
60
+ };
61
+ const refreshData = async () => {
62
+ if (disposed)
63
+ return;
64
+ if (loading !== undefined) {
65
+ await loading;
66
+ return;
67
+ }
68
+ loading = loadWorkspaceRecords(remote).then((next) => {
69
+ if (disposed)
70
+ return;
71
+ workspaces = next;
72
+ scan();
73
+ }).catch(() => undefined).finally(() => {
74
+ loading = undefined;
75
+ });
76
+ await loading;
77
+ };
78
+ const scan = () => {
79
+ if (disposed || dom === undefined)
80
+ return;
81
+ observer?.disconnect();
82
+ try {
83
+ clearHiddenWorkspaceNodes(dom);
84
+ if (!showHiddenWorkspaces)
85
+ removeVisibilityEntries(dom);
86
+ const headers = findWorkspaceHeaders(dom);
87
+ const workspaceIds = new Set(workspaces.map((workspace) => workspace.workspaceId));
88
+ for (const header of headers) {
89
+ const workspaceId = resolveWorkspaceId(header);
90
+ if (workspaceId === undefined)
91
+ continue;
92
+ workspaceIds.add(workspaceId);
93
+ bindWorkspaceMenuTriggers(header, workspaceId, () => {
94
+ activeMenuWorkspaceId = workspaceId;
95
+ activeMenuKind = 'workspace';
96
+ menuContextPending = true;
97
+ // 菜单由 DSH 在点击事件后异步挂载到 Portal;在事件队列结束后再扫描,
98
+ // 避免在宿主菜单尚未完成打开时同步改写它的 DOM。
99
+ scheduleScan();
100
+ scheduleMenuRescan();
101
+ });
102
+ if (hiddenWorkspaceIds.has(workspaceId))
103
+ markWorkspaceHidden(header, workspaceId);
104
+ }
105
+ bindWorkspaceFilterTriggers(dom, (trigger) => {
106
+ if (suppressFilterTrigger)
107
+ return;
108
+ activeFilterTrigger = trigger;
109
+ activeMenuKind = 'filter';
110
+ menuContextPending = true;
111
+ scheduleScan();
112
+ scheduleMenuRescan();
113
+ });
114
+ const menus = findWorkspaceMenus(dom);
115
+ knownNativeMenus = new Set(menus);
116
+ if (menus.length > 0)
117
+ menuContextPending = false;
118
+ if (activeMenuKind === 'filter') {
119
+ injectFilterMenuAction(dom, menus, showHiddenWorkspaces, () => {
120
+ showHiddenWorkspaces = !showHiddenWorkspaces;
121
+ scheduleScan();
122
+ }, (menu) => closeFilterMenu(activeFilterTrigger, menu, () => {
123
+ suppressFilterTrigger = true;
124
+ return () => { suppressFilterTrigger = false; };
125
+ }));
126
+ }
127
+ else {
128
+ injectWorkspaceMenuActions(menus, dom, activeMenuWorkspaceId, workspaceIds, menuWorkspaceIds, (workspaceId) => updateVisibility(workspaceId, true));
129
+ }
130
+ // 点击触发器与 Portal 菜单挂载不是同一个同步阶段。菜单尚未出现时
131
+ // 必须保留上下文,否则后续 MutationObserver 扫描无法判断这是筛选菜单。
132
+ if (menus.length === 0 && !menuContextPending) {
133
+ activeMenuWorkspaceId = undefined;
134
+ activeMenuKind = undefined;
135
+ }
136
+ if (showHiddenWorkspaces) {
137
+ injectHiddenWorkspaceFooter(dom, headers, workspaces, hiddenWorkspaceIds, (workspaceId) => updateVisibility(workspaceId, false));
138
+ }
139
+ }
140
+ finally {
141
+ if (!disposed && observer !== undefined && dom.documentElement !== null) {
142
+ observer.observe(dom.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['aria-expanded'] });
143
+ }
144
+ }
145
+ };
146
+ const scheduleScan = (mutations = []) => {
147
+ if (disposed || scanQueued)
148
+ return;
149
+ // 展开恢复列表也使用 aria-expanded;忽略插件自身的属性变化,避免每次
150
+ // 点击列表都被观察器重建成收起状态。
151
+ if (mutations.length > 0 && mutations.every((mutation) => (isVisibilityMutation(mutation.target) || isNativeMenuMutation(mutation.target, knownNativeMenus))))
152
+ return;
153
+ scanQueued = true;
154
+ queueMicrotask(() => {
155
+ scanQueued = false;
156
+ scan();
157
+ });
158
+ };
159
+ const scheduleMenuRescan = () => {
160
+ if (menuRescanTimer !== undefined)
161
+ globalThis.clearTimeout(menuRescanTimer);
162
+ menuRescanTimer = globalThis.setTimeout(() => {
163
+ menuRescanTimer = undefined;
164
+ scheduleScan();
165
+ }, 50);
166
+ };
167
+ const observer = dom === undefined || Observer === undefined
168
+ ? undefined
169
+ : new Observer(scheduleScan);
170
+ if (observer !== undefined && dom !== undefined) {
171
+ observer.observe(dom.documentElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['aria-expanded'] });
172
+ }
173
+ scan();
174
+ void refreshData();
175
+ return {
176
+ refresh() {
177
+ scheduleScan();
178
+ void refreshData();
179
+ },
180
+ setHiddenWorkspaceIds(ids) {
181
+ changeSequence += 1;
182
+ hiddenWorkspaceIds = new Set(normalizeIds(ids));
183
+ scheduleScan();
184
+ },
185
+ dispose() {
186
+ if (disposed)
187
+ return;
188
+ disposed = true;
189
+ observer?.disconnect();
190
+ if (menuRescanTimer !== undefined)
191
+ globalThis.clearTimeout(menuRescanTimer);
192
+ knownNativeMenus = new Set();
193
+ if (dom !== undefined) {
194
+ clearHiddenWorkspaceNodes(dom);
195
+ removeVisibilityEntries(dom);
196
+ removeMenuEntries(dom);
197
+ }
198
+ },
199
+ };
200
+ }
201
+ function isVisibilityMutation(target) {
202
+ return typeof Element !== 'undefined' && target instanceof Element
203
+ && (target.closest(`[${WORKSPACE_SESSION_HIDDEN_LIST_ATTRIBUTE}]`) !== null
204
+ || target.closest(`[${WORKSPACE_SESSION_HIDDEN_MENU_ATTRIBUTE}]`) !== null);
205
+ }
206
+ function isNativeMenuMutation(target, knownMenus) {
207
+ return typeof Element !== 'undefined'
208
+ && target instanceof Element
209
+ && (target.closest('[role="menu"], [role="listbox"], [data-menu-content], [data-radix-menu-content]') !== null
210
+ || [...knownMenus].some((menu) => menu.contains(target)));
211
+ }
212
+ function normalizeIds(ids) {
213
+ return [...new Set(ids.flatMap((value) => {
214
+ const normalized = typeof value === 'string' ? value.trim() : '';
215
+ return normalized === '' ? [] : [normalized];
216
+ }))];
217
+ }
218
+ function bindWorkspaceMenuTriggers(header, workspaceId, rememberWorkspace) {
219
+ const elements = [header, ...header.querySelectorAll('button,[role="button"]')];
220
+ for (const element of elements) {
221
+ if (element === header || !isWorkspaceMenuTrigger(element))
222
+ continue;
223
+ if (element.getAttribute('data-codingns-hidden-menu-bound') === workspaceId)
224
+ continue;
225
+ element.setAttribute('data-codingns-hidden-menu-bound', workspaceId);
226
+ element.addEventListener('click', rememberWorkspace);
227
+ }
228
+ }
229
+ function isWorkspaceMenuTrigger(element) {
230
+ if (element.getAttribute(MENU_ATTRIBUTE) === 'menu')
231
+ return true;
232
+ const label = `${element.textContent ?? ''} ${element.getAttribute('aria-label') ?? ''} ${element.title}`.replace(/\s+/gu, ' ');
233
+ return WORKSPACE_MENU_PATTERN.test(label);
234
+ }
235
+ function bindWorkspaceFilterTriggers(dom, rememberFilter) {
236
+ for (const element of dom.querySelectorAll('button,[role="button"]')) {
237
+ if (!isWorkspaceFilterTrigger(element))
238
+ continue;
239
+ if (element.getAttribute(WORKSPACE_SESSION_HIDDEN_FILTER_BOUND_ATTRIBUTE) === 'true')
240
+ continue;
241
+ element.setAttribute(WORKSPACE_SESSION_HIDDEN_FILTER_BOUND_ATTRIBUTE, 'true');
242
+ element.addEventListener('click', () => rememberFilter(element));
243
+ }
244
+ }
245
+ function isWorkspaceFilterTrigger(element) {
246
+ const label = `${element.textContent ?? ''} ${element.getAttribute('aria-label') ?? ''} ${element.title}`.replace(/\s+/gu, ' ');
247
+ return WORKSPACE_FILTER_PATTERN.test(label);
248
+ }
249
+ function findWorkspaceMenus(dom) {
250
+ const explicit = [...dom.querySelectorAll('[role="menu"], [role="listbox"], [data-menu-content], [data-radix-menu-content]')];
251
+ const matchingExplicit = explicit.filter(isWorkspaceFilterMenu);
252
+ if (matchingExplicit.length > 0)
253
+ return matchingExplicit;
254
+ if (explicit.length > 0)
255
+ return explicit;
256
+ // 某些 DSH 构建不会给 Portal 菜单设置 role。不能遍历页面所有 div 并对每个
257
+ // 节点读取 textContent,那会在长会话页面上反复遍历整棵消息树,退化为 O(N²)。
258
+ // 从已有菜单项向上聚合少量候选容器,复杂度只和菜单项数量及祖先深度有关。
259
+ const itemSelector = 'button,[role="menuitem"],[role="menuitemcheckbox"]';
260
+ const counts = new Map();
261
+ for (const item of dom.querySelectorAll(itemSelector)) {
262
+ let current = item.parentElement;
263
+ for (let depth = 0; depth < 8 && current !== null; depth += 1, current = current.parentElement) {
264
+ counts.set(current, (counts.get(current) ?? 0) + 1);
265
+ }
266
+ }
267
+ const candidates = [...counts.keys()].filter((element) => ((counts.get(element) ?? 0) >= 3
268
+ && /(?:分组方式|排序方式|筛选会话|group(?:ing)?|sort|filter)/iu.test(element.textContent ?? '')));
269
+ return candidates.filter((element) => !candidates.some((other) => other !== element && element.contains(other)));
270
+ }
271
+ function isWorkspaceFilterMenu(element) {
272
+ return /(?:分组方式|排序方式|筛选会话|group(?:ing)?|sort(?:ing)?|filter(?:\s+sessions?)?)/iu.test(element.textContent ?? '');
273
+ }
274
+ function injectWorkspaceMenuActions(menus, dom, activeWorkspaceId, workspaceIds, menuWorkspaceIds, onHide) {
275
+ for (const menu of menus) {
276
+ const workspaceId = activeWorkspaceId ?? resolveWorkspaceId(menu) ?? menuWorkspaceIds.get(menu);
277
+ if (workspaceId === undefined)
278
+ continue;
279
+ if (!workspaceIds.has(workspaceId))
280
+ continue;
281
+ menuWorkspaceIds.set(menu, workspaceId);
282
+ const existing = menu.querySelector(`[${WORKSPACE_SESSION_HIDDEN_MENU_ATTRIBUTE}]`);
283
+ if (existing?.getAttribute(WORKSPACE_SESSION_HIDDEN_MENU_WORKSPACE_ATTRIBUTE) === workspaceId)
284
+ continue;
285
+ existing?.remove();
286
+ const action = dom.createElement('button');
287
+ action.type = 'button';
288
+ action.setAttribute('role', 'menuitem');
289
+ action.setAttribute(WORKSPACE_SESSION_HIDDEN_MENU_ATTRIBUTE, '');
290
+ action.setAttribute(WORKSPACE_SESSION_HIDDEN_MENU_WORKSPACE_ATTRIBUTE, workspaceId);
291
+ action.setAttribute('aria-label', '隐藏工作区');
292
+ applyNativeMenuItemStyle(action, menu);
293
+ action.textContent = '隐藏工作区';
294
+ const actionIcon = createHiddenIcon(dom);
295
+ action.insertBefore(actionIcon, action.firstChild);
296
+ action.addEventListener('click', () => {
297
+ // 先让 DSH 完成原生菜单的 click 处理,再修改工作区 DOM;同步扫描会
298
+ // 与宿主 React 菜单的提交阶段竞争,表现为点击后整个页面卡住。
299
+ globalThis.setTimeout(() => {
300
+ onHide(workspaceId);
301
+ }, 0);
302
+ });
303
+ menu.appendChild(action);
304
+ }
305
+ }
306
+ function injectFilterMenuAction(dom, menus, checked, onChange, onClose) {
307
+ // 没有可读文本时,Portal 最近追加的菜单就是当前视图菜单;避免把选项
308
+ // 错注入到页面中其他仍然存在的菜单。
309
+ const menu = menus.find(isWorkspaceFilterMenu) ?? menus[menus.length - 1];
310
+ if (menu === undefined)
311
+ return;
312
+ const existing = menu.querySelector(`[${WORKSPACE_SESSION_HIDDEN_FILTER_ATTRIBUTE}]`);
313
+ if (existing !== null) {
314
+ existing.setAttribute('aria-checked', String(checked));
315
+ updateFilterMenuCheckmark(dom, existing, checked);
316
+ return;
317
+ }
318
+ const action = dom.createElement('button');
319
+ action.type = 'button';
320
+ // DSH 菜单只会把 role=menuitem 视为可选择并在点击后正确收起;
321
+ // aria-checked 仍保留筛选项的选中语义。
322
+ action.setAttribute('role', 'menuitem');
323
+ action.setAttribute('aria-checked', String(checked));
324
+ action.setAttribute(WORKSPACE_SESSION_HIDDEN_FILTER_ATTRIBUTE, '');
325
+ action.setAttribute('aria-label', '显示隐藏的工作区');
326
+ applyNativeMenuItemStyle(action, menu);
327
+ action.textContent = '显示隐藏的工作区';
328
+ const actionIcon = createHiddenIcon(dom);
329
+ action.insertBefore(actionIcon, action.firstChild);
330
+ updateFilterMenuCheckmark(dom, action, checked);
331
+ action.addEventListener('click', () => {
332
+ globalThis.setTimeout(() => {
333
+ onChange();
334
+ // Portal 菜单由 DSH React 持有,不能直接 remove(),否则 React 后续
335
+ // 卸载时会对同一节点再次 removeChild 并抛出 NotFoundError。
336
+ onClose(menu);
337
+ }, 0);
338
+ });
339
+ menu.appendChild(action);
340
+ }
341
+ function closeFilterMenu(trigger, menu, suppressTrigger) {
342
+ if (trigger === undefined || !isDomNodeConnected(menu) || typeof trigger.click !== 'function')
343
+ return;
344
+ const release = suppressTrigger();
345
+ try {
346
+ trigger.click();
347
+ }
348
+ finally {
349
+ release();
350
+ }
351
+ }
352
+ function isDomNodeConnected(node) {
353
+ return node.parentElement !== null || node.isConnected === true;
354
+ }
355
+ function updateFilterMenuCheckmark(dom, action, checked) {
356
+ const existing = action.querySelector(`[${WORKSPACE_SESSION_HIDDEN_FILTER_CHECK_ATTRIBUTE}]`);
357
+ if (!checked) {
358
+ existing?.remove();
359
+ return;
360
+ }
361
+ if (existing !== null)
362
+ return;
363
+ const check = createCheckIcon(dom);
364
+ check.setAttribute(WORKSPACE_SESSION_HIDDEN_FILTER_CHECK_ATTRIBUTE, '');
365
+ action.appendChild(check);
366
+ }
367
+ function applyNativeMenuItemStyle(action, menu) {
368
+ const reference = menu.querySelector('[role="menuitem"], [role="menuitemcheckbox"], button');
369
+ if (reference !== null && reference.className !== '') {
370
+ action.className = reference.className;
371
+ return;
372
+ }
373
+ Object.assign(action.style, menuItemStyle);
374
+ }
375
+ function injectHiddenWorkspaceFooter(dom, headers, workspaces, hiddenWorkspaceIds, onRestore) {
376
+ const recordsById = new Map(workspaces.map((workspace) => [workspace.workspaceId, workspace]));
377
+ for (const header of headers) {
378
+ const workspaceId = resolveWorkspaceId(header);
379
+ if (workspaceId !== undefined && !recordsById.has(workspaceId)) {
380
+ recordsById.set(workspaceId, { workspaceId, title: workspaceId, sessionIds: [], archivedSessionIds: [] });
381
+ }
382
+ }
383
+ // 新近隐藏的工作区排在前面,恢复入口更符合用户刚刚完成的操作顺序。
384
+ const hidden = [...hiddenWorkspaceIds].reverse().flatMap((workspaceId) => {
385
+ const workspace = recordsById.get(workspaceId);
386
+ return workspace === undefined ? [] : [workspace];
387
+ });
388
+ const host = findWorkspaceListContainer(dom, headers);
389
+ const existing = dom.querySelectorAll(`[${WORKSPACE_SESSION_HIDDEN_LIST_ATTRIBUTE}]`)[0] ?? null;
390
+ if (hidden.length === 0 || host === null) {
391
+ existing?.remove();
392
+ return;
393
+ }
394
+ const state = hidden.map((workspace) => `${workspace.workspaceId}\u0000${workspace.title}\u0000${workspace.path ?? ''}`).join('\u0001');
395
+ if (existing !== null
396
+ && existing.parentElement === host
397
+ && existing.getAttribute(WORKSPACE_SESSION_HIDDEN_LIST_STATE_ATTRIBUTE) === state)
398
+ return;
399
+ existing?.remove();
400
+ const entry = dom.createElement('div');
401
+ entry.setAttribute(WORKSPACE_SESSION_HIDDEN_LIST_ATTRIBUTE, '');
402
+ entry.setAttribute(WORKSPACE_SESSION_HIDDEN_LIST_STATE_ATTRIBUTE, state);
403
+ Object.assign(entry.style, footerStyle);
404
+ const toggle = dom.createElement('button');
405
+ toggle.type = 'button';
406
+ toggle.setAttribute('aria-expanded', 'false');
407
+ toggle.setAttribute('aria-label', `隐藏的工作区 ${hidden.length}`);
408
+ Object.assign(toggle.style, menuItemStyle);
409
+ toggle.textContent = `隐藏的工作区 ${hidden.length}`;
410
+ const toggleIcon = createHiddenIcon(dom);
411
+ toggle.insertBefore(toggleIcon, toggle.firstChild);
412
+ const list = dom.createElement('div');
413
+ list.hidden = true;
414
+ Object.assign(list.style, { display: 'none', flexDirection: 'column', gap: '2px', marginTop: '2px' });
415
+ for (const workspace of hidden) {
416
+ const restore = dom.createElement('button');
417
+ restore.type = 'button';
418
+ restore.setAttribute('role', 'menuitem');
419
+ restore.setAttribute('aria-label', `恢复工作区 ${workspace.title}`);
420
+ restore.textContent = workspace.title;
421
+ restore.title = workspace.path ?? workspace.workspaceId;
422
+ Object.assign(restore.style, menuItemStyle, { paddingLeft: '28px' });
423
+ restore.addEventListener('click', () => onRestore(workspace.workspaceId));
424
+ list.appendChild(restore);
425
+ }
426
+ toggle.addEventListener('click', () => {
427
+ const expanded = toggle.getAttribute('aria-expanded') === 'true';
428
+ toggle.setAttribute('aria-expanded', expanded ? 'false' : 'true');
429
+ list.hidden = expanded;
430
+ list.style.display = expanded ? 'none' : 'flex';
431
+ });
432
+ entry.append(toggle, list);
433
+ host.appendChild(entry);
434
+ }
435
+ function createHiddenIcon(dom) {
436
+ const createElementNS = dom.createElementNS;
437
+ if (typeof createElementNS === 'function') {
438
+ const icon = createElementNS.call(dom, 'http://www.w3.org/2000/svg', 'svg');
439
+ icon.setAttribute('width', '16');
440
+ icon.setAttribute('height', '16');
441
+ icon.setAttribute('viewBox', '0 0 24 24');
442
+ icon.setAttribute('fill', 'none');
443
+ icon.setAttribute('stroke', 'currentColor');
444
+ icon.setAttribute('stroke-width', '1.8');
445
+ icon.setAttribute('stroke-linecap', 'round');
446
+ icon.setAttribute('stroke-linejoin', 'round');
447
+ icon.setAttribute('aria-hidden', 'true');
448
+ const path = createElementNS.call(dom, 'http://www.w3.org/2000/svg', 'path');
449
+ path.setAttribute('d', 'M3 3l18 18M10.6 10.6a2 2 0 0 0 2.8 2.8M9.9 4.2A10.7 10.7 0 0 1 12 4c5 0 9 4 10 8-.4 1.5-1.2 2.8-2.2 3.9M6.1 6.1C4.5 7.2 3.3 8.8 2 12c1 4 5 8 10 8 1 0 2-.2 2.9-.5');
450
+ icon.append(path);
451
+ Object.assign(icon.style, hiddenIconStyle);
452
+ return icon;
453
+ }
454
+ const icon = dom.createElement('span');
455
+ icon.textContent = '⊘';
456
+ icon.setAttribute('aria-hidden', 'true');
457
+ Object.assign(icon.style, hiddenIconStyle);
458
+ return icon;
459
+ }
460
+ function createCheckIcon(dom) {
461
+ const createElementNS = dom.createElementNS;
462
+ if (typeof createElementNS === 'function') {
463
+ const icon = createElementNS.call(dom, 'http://www.w3.org/2000/svg', 'svg');
464
+ icon.setAttribute('width', '16');
465
+ icon.setAttribute('height', '16');
466
+ icon.setAttribute('viewBox', '0 0 16 16');
467
+ icon.setAttribute('fill', 'none');
468
+ icon.setAttribute('stroke', 'currentColor');
469
+ icon.setAttribute('stroke-width', '1.5');
470
+ icon.setAttribute('stroke-linecap', 'round');
471
+ icon.setAttribute('stroke-linejoin', 'round');
472
+ icon.setAttribute('aria-hidden', 'true');
473
+ const path = createElementNS.call(dom, 'http://www.w3.org/2000/svg', 'path');
474
+ path.setAttribute('d', 'm3 8 3 3 7-7');
475
+ icon.append(path);
476
+ Object.assign(icon.style, checkIconStyle);
477
+ return icon;
478
+ }
479
+ const icon = dom.createElement('span');
480
+ icon.textContent = '✓';
481
+ icon.setAttribute('aria-hidden', 'true');
482
+ Object.assign(icon.style, checkIconStyle);
483
+ return icon;
484
+ }
485
+ function markWorkspaceHidden(header, workspaceId) {
486
+ const container = narrowWorkspaceContainer(header, workspaceId);
487
+ if (container.hasAttribute(WORKSPACE_SESSION_HIDDEN_ATTRIBUTE))
488
+ return;
489
+ container.setAttribute(WORKSPACE_SESSION_HIDDEN_ATTRIBUTE, workspaceId);
490
+ container.dataset.codingnsHiddenDisplay = container.style.display;
491
+ container.dataset.codingnsHiddenValue = container.hidden ? 'true' : 'false';
492
+ container.hidden = true;
493
+ container.style.display = 'none';
494
+ }
495
+ function narrowWorkspaceContainer(header, workspaceId) {
496
+ const candidate = findWorkspaceContainer(header, workspaceId);
497
+ if (candidate === null)
498
+ return header;
499
+ const nestedHeaders = candidate.querySelectorAll('[role="treeitem"][aria-expanded]').length;
500
+ const isBroadContainer = candidate.tagName === 'BODY'
501
+ || candidate.tagName === 'HTML'
502
+ || candidate.getAttribute('role') === 'tree'
503
+ || nestedHeaders > 1;
504
+ return isBroadContainer ? closestWorkspaceContainer(header) : candidate;
505
+ }
506
+ function closestWorkspaceContainer(header) {
507
+ const parent = header.parentElement;
508
+ if (parent === null
509
+ || parent.tagName === 'BODY'
510
+ || parent.tagName === 'HTML'
511
+ || parent.getAttribute('role') === 'tree'
512
+ || parent.querySelectorAll('[role="treeitem"][aria-expanded]').length > 1)
513
+ return header;
514
+ return parent;
515
+ }
516
+ function clearHiddenWorkspaceNodes(dom) {
517
+ for (const node of dom.querySelectorAll(`[${WORKSPACE_SESSION_HIDDEN_ATTRIBUTE}]`)) {
518
+ const display = node.dataset.codingnsHiddenDisplay ?? '';
519
+ const hidden = node.dataset.codingnsHiddenValue === 'true';
520
+ node.hidden = hidden;
521
+ node.style.display = display;
522
+ delete node.dataset.codingnsHiddenDisplay;
523
+ delete node.dataset.codingnsHiddenValue;
524
+ node.removeAttribute(WORKSPACE_SESSION_HIDDEN_ATTRIBUTE);
525
+ }
526
+ }
527
+ function removeVisibilityEntries(dom) {
528
+ for (const node of dom.querySelectorAll(`[${WORKSPACE_SESSION_HIDDEN_LIST_ATTRIBUTE}]`))
529
+ node.remove();
530
+ }
531
+ function removeMenuEntries(dom) {
532
+ for (const node of dom.querySelectorAll(`[${WORKSPACE_SESSION_HIDDEN_MENU_ATTRIBUTE}]`))
533
+ node.remove();
534
+ for (const node of dom.querySelectorAll(`[${WORKSPACE_SESSION_HIDDEN_FILTER_ATTRIBUTE}]`))
535
+ node.remove();
536
+ }
537
+ function findWorkspaceListContainer(dom, headers) {
538
+ const first = headers[0];
539
+ if (first !== undefined) {
540
+ let current = first.parentElement;
541
+ // 只有一个工作区时,header 的直接父节点往往就是该工作区容器;
542
+ // 再向上一层才能把恢复入口放到工作区列表本身,而不是隐藏容器内。
543
+ if (headers.length === 1 && current?.getAttribute('role') !== 'tree')
544
+ current = current?.parentElement ?? null;
545
+ for (let depth = 0; depth < 10 && current !== null; depth += 1) {
546
+ if (current.getAttribute('role') === 'tree')
547
+ return current;
548
+ if (headers.every((header) => current?.contains(header)))
549
+ return current;
550
+ current = current.parentElement;
551
+ }
552
+ }
553
+ return dom.body ?? null;
554
+ }
555
+ const menuItemStyle = {
556
+ display: 'flex',
557
+ alignItems: 'center',
558
+ gap: '8px',
559
+ width: '100%',
560
+ minHeight: '36px',
561
+ boxSizing: 'border-box',
562
+ padding: '6px 10px',
563
+ border: '0',
564
+ borderRadius: '6px',
565
+ color: 'inherit',
566
+ background: 'transparent',
567
+ textAlign: 'left',
568
+ cursor: 'pointer',
569
+ fontFamily: 'inherit',
570
+ fontSize: '14px',
571
+ fontWeight: '400',
572
+ lineHeight: '20px',
573
+ };
574
+ const hiddenIconStyle = {
575
+ display: 'inline-flex',
576
+ width: '16px',
577
+ height: '16px',
578
+ flex: '0 0 16px',
579
+ alignItems: 'center',
580
+ justifyContent: 'center',
581
+ color: 'currentColor',
582
+ fontSize: '16px',
583
+ lineHeight: '16px',
584
+ };
585
+ const checkIconStyle = {
586
+ display: 'inline-flex',
587
+ width: '16px',
588
+ height: '16px',
589
+ flex: '0 0 16px',
590
+ marginLeft: 'auto',
591
+ alignItems: 'center',
592
+ justifyContent: 'center',
593
+ color: 'currentColor',
594
+ };
595
+ const footerStyle = {
596
+ display: 'flex',
597
+ flexDirection: 'column',
598
+ width: '100%',
599
+ boxSizing: 'border-box',
600
+ marginTop: '4px',
601
+ paddingTop: '4px',
602
+ borderTop: '1px solid var(--dsw-alias-border-l3, rgba(128,128,128,.18))',
603
+ };
604
+ //# sourceMappingURL=workspace-session-visibility-dom.js.map
@@ -0,0 +1,50 @@
1
+ import type { CodingNsSettings } from '../../shared/contracts/config.js';
2
+ import { type CodingNsSettingsStore } from '../settings-store.js';
3
+ /** 0.1.7 Client ConfigForm 的最小结构化边界。 */
4
+ export interface DshConfigForm<T> {
5
+ getSnapshot(): {
6
+ readonly value: T | undefined;
7
+ readonly revision?: number;
8
+ readonly writable: boolean;
9
+ readonly status?: 'loading' | 'ready' | 'unavailable';
10
+ };
11
+ subscribe(listener: () => void): () => void;
12
+ mutate(operations: readonly {
13
+ readonly op: 'set' | 'unset';
14
+ readonly path: readonly string[];
15
+ readonly value?: unknown;
16
+ }[], expectedRevision?: number): Promise<void | boolean>;
17
+ set(field: string, value: unknown): Promise<void | boolean>;
18
+ unset(field: string): Promise<void | boolean>;
19
+ }
20
+ export interface DshClientConfigForms {
21
+ get<T>(namespace: string): DshConfigForm<T> | undefined;
22
+ /** 0.1.7 ConfigForms 的共享 settings mirror;用于确认真正被 Host 提供的 entry。 */
23
+ describe?: () => {
24
+ getSnapshot: () => {
25
+ readonly view?: {
26
+ readonly namespaces: readonly {
27
+ readonly ns: string;
28
+ }[];
29
+ };
30
+ };
31
+ };
32
+ }
33
+ /** 通过 Codingns4DSH Host RPC 写入设置;省略 ConfigForm 的全局 revision 栅栏。 */
34
+ export interface DshConfigFormUnfencedWriter {
35
+ (operations: readonly {
36
+ readonly op: 'set' | 'unset';
37
+ readonly path: readonly string[];
38
+ readonly value?: unknown;
39
+ }[]): Promise<{
40
+ readonly value: CodingNsSettings;
41
+ readonly revision: number;
42
+ }>;
43
+ }
44
+ export interface DshConfigFormSettingsOptions {
45
+ /** DSH 0.1.7 后台可能持续更新同一 entry 的 Host-only 索引时使用。 */
46
+ readonly writeUnfenced?: DshConfigFormUnfencedWriter;
47
+ }
48
+ /** 0.1.7 Client ConfigForm 路由适配器。 */
49
+ export declare function createConfigFormSettingsStore(forms: DshClientConfigForms, namespace: string, options?: DshConfigFormSettingsOptions): CodingNsSettingsStore<CodingNsSettings>;
50
+ //# sourceMappingURL=config-forms-adapter.d.ts.map