dsh-manage-session 0.4.8 → 0.5.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.en.md +4 -0
  3. package/README.md +4 -0
  4. package/cordis.patch.yml +6 -1
  5. package/dsh.plugin.json +22 -22
  6. package/lib/client.js +3848 -202
  7. package/lib/index.d.ts +759 -29
  8. package/lib/index.js +2604 -93
  9. package/locale/de.json +6 -0
  10. package/locale/en.json +6 -0
  11. package/locale/es.json +6 -0
  12. package/locale/fr.json +6 -0
  13. package/locale/it.json +6 -0
  14. package/locale/ja.json +6 -0
  15. package/locale/ko.json +6 -0
  16. package/locale/ru.json +6 -0
  17. package/locale/zh.json +6 -0
  18. package/package.json +43 -32
  19. package/src/client/bridge-scope.ts +213 -0
  20. package/src/client/index.ts +154 -26
  21. package/src/client/locales.ts +857 -223
  22. package/src/client/panel.tsx +139 -118
  23. package/src/client/search/card.tsx +563 -0
  24. package/src/client/search/host-api.ts +208 -0
  25. package/src/client/search/locales.ts +780 -0
  26. package/src/client/search/manage.tsx +400 -0
  27. package/src/client/search/panel.tsx +815 -0
  28. package/src/client/search/platform.ts +136 -0
  29. package/src/config.ts +54 -5
  30. package/src/host/history/archive-source.ts +48 -0
  31. package/src/host/history/archive.ts +56 -1
  32. package/src/host/history/favorites.ts +71 -0
  33. package/src/host/index/archive-source.ts +90 -0
  34. package/src/host/index/engine.ts +571 -0
  35. package/src/host/index/extract.ts +272 -0
  36. package/src/host/index/peers.ts +62 -0
  37. package/src/host/index/rebuild.ts +368 -0
  38. package/src/host/index/schema.ts +136 -0
  39. package/src/host/index/snapshot.ts +135 -0
  40. package/src/host/index/sync.ts +286 -0
  41. package/src/host/registry/archive-registry.ts +317 -0
  42. package/src/host/settings-bridge.ts +237 -0
  43. package/src/index.ts +1421 -532
@@ -2,8 +2,9 @@
2
2
  * 会话管家客户端半身:样式、字典、侧边栏入口(dsh-manage-session)与设置卡。
3
3
  *
4
4
  * 侧边栏入口点击后打开「养老院 / 体检」双页签对话框;两个页签的可见性由配置开关决定,
5
- * 关掉的子域不渲染页签,也不留空壳。开关读取走 configForms(0.1.7,entry id 键)/
6
- * 旧宿主设置服务的订阅快照;配置写入由 volatile 字段自动生成的设置表单承担。
5
+ * 关掉的子域不渲染页签,也不留空壳。开关读取:configForms(0.1.7,entry id 键)
6
+ * / legacy 宿主走自家 webServer 设置桥(settingsScope.bind 是数据死路,见
7
+ * bridge-scope.ts);配置写入同链路。
7
8
  */
8
9
  import type { Context } from 'cordis'
9
10
  import { createElement, useState, useSyncExternalStore, type ReactElement } from 'react'
@@ -12,10 +13,18 @@ import {
12
13
  STEWARD_ENTRY_ID,
13
14
  STEWARD_SETTINGS_NAMESPACE,
14
15
  type StewardConfig,
16
+ type SwitchSearchConfig,
15
17
  } from '../config.ts'
16
18
  import { StewardCardScope, StewardSettingsCard, type CardTranslate } from './card.tsx'
17
- import { en, translate, zh, type LocaleKey } from './locales.ts'
19
+ import { searchBridgeScope, stewardBridgeScope, withBridgeFallback, type BridgeScope } from './bridge-scope.ts'
20
+ import { dictionaries, translate, type LocaleKey } from './locales.ts'
18
21
  import { StewardFooter, StewardPanel, TAB_HEALTH, TAB_HISTORY } from './panel.tsx'
22
+ // 搜索子域(原 dsh-search-index client 半身):面板/入口组件、设置卡、
23
+ // 打开会话的宿主服务面解析、独立字典命名空间(switch-search,键集不变)。
24
+ import { injectStyles as injectSearchStyles, SwitchFooter } from './search/panel.tsx'
25
+ import { SearchSettingsCard, type SwitchCardScope } from './search/card.tsx'
26
+ import { openSessionThrough } from './search/host-api.ts'
27
+ import { dictionaries as searchDictionaries, NS as SEARCH_NS, translate as translateSearch, type LocaleKey as SearchLocaleKey } from './search/locales.ts'
19
28
 
20
29
  export { STEWARD_ENTRY_ID, STEWARD_SETTINGS_NAMESPACE, TAB_HEALTH, TAB_HISTORY }
21
30
  export { translate } from './locales.ts'
@@ -34,13 +43,10 @@ interface StewardSlotsService {
34
43
 
35
44
  /** locale 服务面。 */
36
45
  interface StewardLocaleService {
37
- register(ns: string, dictionaries: { zh: unknown; en: unknown }): () => void
46
+ register(ns: string, dicts: Record<string, Record<string, string>>): () => void
38
47
  }
39
48
 
40
- /** 旧宿主设置服务面(0.1.7 前的回退路径,结构化镜像)。 */
41
- interface StewardSettingsScope {
42
- bind<U>(input: { namespace: string }): (StewardCardScope & { getSnapshot(): { value: U | undefined } }) | undefined
43
- }
49
+ /** 旧宿主设置服务面(已弃用:≤0.1.5 的 bind 是数据死路,台账 A2——保留类型注释存档)。 */
44
50
 
45
51
  /** configForms 服务面(0.1.7:以 profile entry id 取句柄)。 */
46
52
  interface StewardConfigForms {
@@ -48,7 +54,7 @@ interface StewardConfigForms {
48
54
  }
49
55
 
50
56
  /** 客户端插件声明的注入面。 */
51
- export const inject = ['slots']
57
+ export const inject = ['slots', 'locale']
52
58
 
53
59
  /** 侧边栏脚部入口的 props(结构子集)。 */
54
60
  export interface StewardFooterProps {
@@ -66,23 +72,37 @@ function injectStyles(): () => void {
66
72
  const style = document.createElement('style')
67
73
  style.id = id
68
74
  style.textContent = `
69
- /* 侧栏 footer 槽位公约(2026-09-26):一行多入口(第三方 dsh-context 等)会互相
70
- 挤占 —— 宿主 .footerActions 是 nowrap flex 行。这里允许容器换行,并把本插件
71
- 入口钉成独占一整行(flex-basis:100%);其余入口(含第三方)自然落到后续行,
72
- 各行内部自行布局,谁也不挤谁。类名用 [class*=] 中段匹配:宿主是 CSS Module
73
- 哈希类名(实测形如 hHd-Xa_footerActions —— <hash>_<name>,哈希在前),
75
+ /* 侧栏 footer 槽位公约(2026-09-26;2026-10-01 收紧行距并强制居中):一行多
76
+ 入口(第三方 dsh-context 等)会互相挤占 —— 宿主 .footerActions 是 nowrap
77
+ flex 行。这里允许容器换行,并把本插件入口钉成独占一整行(flex-basis:100%);
78
+ 其余入口(含第三方)自然落到后续行,各行内部自行布局,谁也不挤谁。
79
+ justify-content:center 让行内所有入口(含不占满行的)统一居中;row-gap:0
80
+ 配合入口自身 32px 高度压缩纵向占位。类名用 [class*=] 中段匹配:宿主是 CSS
81
+ Module 哈希类名(实测形如 hHd-Xa_footerActions —— <hash>_<name>,哈希在前),
74
82
  中段跨版本稳定。 */
75
- [class*="footerActions"]{flex-wrap:wrap;row-gap:2px;height:auto;min-height:0}
83
+ [class*="footerActions"]{flex-wrap:wrap;justify-content:center;row-gap:0;height:auto;min-height:0}
84
+ /* —— 第三方矫正:dsh-context「上下文洞察」入口(2026-10-01)——
85
+ .lc-ov-entry 按"独占整行"设计(width:calc(100% + 4px)、无 justify-content、
86
+ 42px 高、不对称 padding),与本槽位公约(每个入口独占一行、行内居中、32px)
87
+ 冲突,表现为文字靠左、纵向松散。这里按公约强制矫正;:not() 排除收起轨道的
88
+ 36px 圆钮形态。lc-ov-* 是 dsh-context 源码硬编码类名(非构建哈希),跨版本
89
+ 稳定(实测 0.56.1 / 0.60.0 规则一致)。两个自有插件各自携带同一矫正块:
90
+ 任一被禁用时矫正仍生效。 */
91
+ .lc-ov-entry:not(.lc-ov-entry-rail){width:auto!important;flex:0 0 100%!important;min-width:0!important;justify-content:center!important;height:32px!important;margin:0!important;padding:0 10px!important}
92
+ .lc-ov-entry-label{flex:0 1 auto!important}
76
93
  /* 宽栏:独占一整行、放弃主动收缩(flex-shrink:0),行内居中;收起轨道回落自然宽度。 */
77
94
  .dss_entryWrap{flex:0 1 auto;display:inline-flex;align-items:center;min-width:0}
78
95
  .dss_entryWrapWide{flex:0 0 100%;width:100%;justify-content:center;margin-left:0}
79
- .dss_footerEntry{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:42px;padding:0 10px 0 8px;border:none;border-radius:12px;background:transparent;cursor:pointer;color:var(--dsw-alias-label-primary);font-family:inherit;font-size:14px;line-height:22px;white-space:nowrap;overflow:hidden;transition:background-color 160ms ease-out,color 160ms ease-out}
96
+ /* 高度 32px(内容 22px 行高 + 上下各 5px):宿主默认 42px 的上下裕度在多行
97
+ 堆叠后过于松散;左右内边距对称(10px/10px),否则整行居中时按钮内容会因
98
+ 不对称 padding 向左偏 1px。 */
99
+ .dss_footerEntry{box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;gap:4px;height:32px;padding:0 10px;border:none;border-radius:12px;background:transparent;cursor:pointer;color:var(--dsw-alias-label-primary);font-family:inherit;font-size:14px;line-height:22px;white-space:nowrap;overflow:hidden;transition:background-color 160ms ease-out,color 160ms ease-out}
80
100
  .dss_footerEntry:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}
81
101
  .dss_footerEntryRail{width:28px;height:28px;padding:0;gap:0;border-radius:50%}
82
102
  .dss_footerIcon{flex:none;font-size:15px;line-height:1}
83
103
  .dss_footerLabel{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
84
104
  .dss_backdrop{position:fixed;inset:0;background:rgba(15,20,30,.42);z-index:1000}
85
- .dss_panel{position:fixed;z-index:1001;left:50%;top:8vh;transform:translateX(-50%);width:min(760px,92vw);max-height:78vh;overflow:auto;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;box-shadow:0 18px 48px rgba(0,0,0,.18);padding:10px 14px 14px}
105
+ .dss_panel{position:fixed;z-index:1001;left:50%;top:6vh;transform:translateX(-50%);width:min(860px,94vw);max-height:86vh;overflow:auto;background:var(--dsw-alias-bg-layer-1,#fff);border:1px solid var(--dsw-alias-border-l2,#e5e7eb);border-radius:14px;box-shadow:0 18px 48px rgba(0,0,0,.18);padding:10px 14px 14px}
86
106
  .dss_dialogHead{display:flex;align-items:center;gap:10px;min-height:34px}
87
107
  .dss_dialogTitle{font-weight:600;color:var(--dsw-alias-label-primary,#111827);flex:1 1 auto;min-width:0}
88
108
  .dss_tabRow{display:inline-flex;gap:4px}
@@ -172,26 +192,72 @@ export function apply(ctx: Context): void {
172
192
 
173
193
  const locale = ctx.get('locale') as StewardLocaleService | undefined
174
194
  if (locale !== undefined && typeof locale.register === 'function') {
175
- ctx.effect(() => locale.register(NS, { zh, en }), 'dsh-manage-session: dictionaries')
195
+ ctx.effect(() => locale.register(NS, dictionaries), 'dsh-manage-session: dictionaries')
196
+ // 搜索子域字典走独立命名空间 `switch-search`(键集与分包时代一致,不迁移)。
197
+ ctx.effect(() => locale.register(SEARCH_NS, searchDictionaries), 'dsh-manage-session: search dictionaries')
176
198
  }
199
+ // 搜索样式与管家样式各自幂等注入;两块类名前缀不同(dsws_ / dss_)不冲突。
200
+ ctx.effect(() => injectSearchStyles(), 'dsh-manage-session: search stylesheet')
177
201
 
178
- // 0.1.7:configForms 以 entry id 取句柄;旧宿主回退到按命名空间绑定。
202
+ // 数据面回退链(台账 A2 定案 + 0.1.7 实测补丁):首选 configForms 原生句柄
203
+ // (entry id 键),**渲染期**观测到 unavailable(出生死或 loading→死,A2 死
204
+ // 路签名)就地落桥——自家 webServer 设置桥(settings-describe/mutate,宿主
205
+ // 半 legacy settings 租约持久化;0.1.7 无 register → 只读真值投影)。两命名
206
+ // 空间并存不迁移:session-steward / switch-search。
179
207
  const configForms = ctx.get('configForms') as StewardConfigForms | undefined
180
- const legacySettings = ctx.get('settingsScope') as StewardSettingsScope | undefined
181
- const bound = configForms?.get<StewardConfig>(STEWARD_ENTRY_ID)
182
- ?? legacySettings?.bind<StewardConfig>({ namespace: STEWARD_SETTINGS_NAMESPACE })
208
+ const bound = withBridgeFallback(
209
+ configForms?.get<StewardConfig>(STEWARD_ENTRY_ID) as BridgeScope<StewardConfig> | undefined,
210
+ stewardBridgeScope(),
211
+ )
212
+ const searchBound = withBridgeFallback(
213
+ configForms?.get<StewardConfig & SwitchSearchConfig>(STEWARD_ENTRY_ID) as BridgeScope<SwitchSearchConfig> | undefined,
214
+ searchBridgeScope(),
215
+ )
183
216
 
184
217
  const slots = ctx.get('slots') as StewardSlotsService | undefined
185
218
  if (slots === undefined) return
186
219
 
220
+ // 搜索结果点击打开会话:click 时经 openSessionThrough 解析(uiWorkspace.openSession
221
+ // 0.1.7+ 优先,旧宿主回退 sessions.open)—— 应用序早于导航模块,必须惰性取。
222
+ const openSearchHit = (sessionId: string): void => {
223
+ openSessionThrough((name) => ctx.get(name), sessionId)
224
+ }
225
+
226
+ // 侧边栏入口(两枚,同一 bundle 注册):搜索(order 5,受 enabled+search 双门)与
227
+ // 管家(order 6,受 enabled+historyFiles/healthCheck 门)。UI 收敛为单一操作台
228
+ // 属 P2 批量台任务(.agents/plans/steward-merge-search-index T9)。
229
+ slots.inject('sidebar.footer.action', () => slots.register(
230
+ { name: 'sidebar.footer.action', id: 'dsh-search-index', order: 5 },
231
+ (props: StewardFooterProps) => createElement(SearchEntry, {
232
+ ...props,
233
+ open: openSearchHit,
234
+ scope: searchBound as unknown as SwitchCardScope,
235
+ }),
236
+ ), 'dsh-manage-session: sidebar footer search entry')
237
+
187
238
  // 侧边栏入口:一个按钮,打开「养老院 / 体检」双页签面板。
188
239
  slots.inject('sidebar.footer.action', () => slots.register(
189
240
  { name: 'sidebar.footer.action', id: STEWARD_ENTRY_ID, order: 6 },
190
- (props: StewardFooterProps) => createElement(StewardEntry, { ...props, scope: bound }),
241
+ (props: StewardFooterProps) => createElement(StewardEntry, { ...props, scope: bound, openSession: openSearchHit }),
191
242
  ), 'dsh-manage-session: sidebar footer entry')
192
243
 
193
244
  // 插件族共用设置 tab(dsh-thinking-levels 的顶级「起子插件设置」节声明该子
194
245
  // 席位)。thinking-levels 缺席时本 inject 静默等待,不阻塞客户端半。
246
+ // 搜索卡(order 30)与管家卡(order 40)各占一张,键集/命名空间互不迁移。
247
+ slots.inject('dsh-family.tab', () => slots.register(
248
+ {
249
+ name: 'dsh-family.tab',
250
+ id: 'dsh-search-index',
251
+ order: 30,
252
+ label: () => translateSearch((locale as { bind?: (n: string) => SearchCardTranslate } | undefined)?.bind?.(SEARCH_NS), 'card.title'),
253
+ locale: SEARCH_NS,
254
+ },
255
+ (props: { t?: SearchCardTranslate }) => createElement(SearchSettingsCard, {
256
+ scope: searchBound as unknown as SwitchCardScope,
257
+ t: props.t,
258
+ }),
259
+ ), 'dsh-manage-session: family settings tab (search)')
260
+
195
261
  slots.inject('dsh-family.tab', () => slots.register(
196
262
  {
197
263
  name: 'dsh-family.tab',
@@ -207,6 +273,25 @@ export function apply(ctx: Context): void {
207
273
  }),
208
274
  ), 'dsh-manage-session: family settings tab')
209
275
 
276
+ // Plugins 页配置卡(`plugins.bundle.config`,key = 包名,dsh-tidy-display
277
+ // 已实证该模式):0.1.7 宿主的 Plugins 页只渲染 key 与包名相等的条目。
278
+ // 合并包只有 package name `dsh-manage-session` 一个键 —— 复合卡把搜索卡与
279
+ // 管家卡同页纵排,scope 各自绑定(字段集不相交),页面 owner props 是
280
+ // `{ view, form }` 不带 `t`,回退各卡的内置 zh 译器。
281
+ slots.inject('plugins.bundle.config', () => slots.register(
282
+ { name: 'plugins.bundle.config', key: STEWARD_ENTRY_ID },
283
+ (props: { t?: CardTranslate }) => createElement('div', { style: { display: 'grid', gap: '12px' } }, [
284
+ createElement(SearchSettingsCard, {
285
+ scope: searchBound as unknown as SwitchCardScope,
286
+ t: props.t as unknown as SearchCardTranslate | undefined,
287
+ }),
288
+ createElement(StewardSettingsCard, {
289
+ scope: bound as StewardCardScope,
290
+ t: props.t,
291
+ }),
292
+ ]),
293
+ ), 'dsh-manage-session: plugins page config card')
294
+
210
295
  // 家族节宿主选举(次优先级):宿主 dsh-thinking-levels 缺席时顶上「起子插件
211
296
  // 设置」节。下载量排名 session-steward(1090) 低于 session-guard(1522) → 宽限期
212
297
  // 更长,guard 先尝试接管;guard 也缺席时本插件按同一 id `dsh-family` 接管,
@@ -239,8 +324,14 @@ export function apply(ctx: Context): void {
239
324
  const FAMILY_SECTION_ID = 'dsh-family'
240
325
  /** 家族子席位 key(与 thinking-levels 的声明严格一致)。 */
241
326
  const FAMILY_CHILD_KEY = 'dsh-family.tab'
242
- /** 接管宽限期:guard 2000ms 先试,本插件 2600ms 兜底。 */
243
- const FAMILY_HOST_GRACE_MS = 2600
327
+ /** 家族容器接管分级——唯一真源 `mine-dsh-plugins/_tools/family-rank.mjs`(离线实测
328
+ * GitHub star + npm 周下载量加权)。延迟 = BASE + RANK * STEP。
329
+ * 旧注释写“guard 2000ms 先试”与 guard 实际值不符——现在谁先谁后由 rank 表唯一决定。 */
330
+ const FAMILY_CLAIM_RANK = 3
331
+ const FAMILY_CLAIM_BASE_MS = 200
332
+ const FAMILY_CLAIM_STEP_MS = 900
333
+ /** 接管宽限期(由分级派生)。 */
334
+ const FAMILY_HOST_GRACE_MS = FAMILY_CLAIM_BASE_MS + FAMILY_CLAIM_RANK * FAMILY_CLAIM_STEP_MS
244
335
 
245
336
  /** 接管节的账本投影 hooks(与 thinking-levels 的 FamilySectionInjected 同形;locale 变更也触发重渲染)。 */
246
337
  function makeFamilyTabsHooks(slots: StewardSlotsService): {
@@ -298,6 +389,7 @@ function StewardFamilySection(props: {
298
389
  export function StewardEntry(
299
390
  props: StewardFooterProps & {
300
391
  scope?: (StewardCardScope & { getSnapshot(): { value: StewardConfig | undefined } }) | undefined
392
+ openSession?: (sessionId: string) => void
301
393
  },
302
394
  ): ReactElement {
303
395
  const [open, setOpen] = useState(false)
@@ -308,7 +400,8 @@ export function StewardEntry(
308
400
  const config: StewardConfig = { ...DEFAULT_CONFIG, ...(snapshot.value ?? {}) }
309
401
  const historyFiles = config.historyFiles !== false
310
402
  const healthCheck = config.healthCheck !== false
311
- const visible = config.enabled !== false && (historyFiles || healthCheck)
403
+ const searchEnabled = config.search !== false
404
+ const visible = config.enabled !== false && (historyFiles || healthCheck || searchEnabled)
312
405
  const wide = props.wide === true
313
406
  // 入口文案与面板标题同源:字典是唯一出处,组件不再持有兜底中文。
314
407
  const label = translate(undefined, 'panel.title')
@@ -327,11 +420,46 @@ export function StewardEntry(
327
420
  key: 'panel',
328
421
  historyFiles,
329
422
  healthCheck,
423
+ searchEnabled,
424
+ openSession: props.openSession,
330
425
  onClose: () => { setOpen(false) },
331
426
  })
332
427
  : null,
333
428
  ])
334
429
  }
335
430
 
431
+ /** 搜索设置卡的译器形状(search/locales.ts 的 LocaleKey 键集)。 */
432
+ type SearchCardTranslate = (key: SearchLocaleKey, params?: Record<string, unknown>) => string
433
+
434
+ /**
435
+ * 搜索侧栏入口(合并包装配):双门可见性 —— 插件总开关 `enabled` 与搜索子域
436
+ * 开关 `search` 任一关闭即不渲染;可见时交给 SwitchFooter(其内部再读一次
437
+ * scope 的 enabled,与设置卡「启用会话搜索」同源,开关不会空转)。
438
+ */
439
+ function SearchEntry(props: {
440
+ wide?: boolean
441
+ open: (sessionId: string) => void
442
+ scope?: unknown
443
+ }): ReactElement {
444
+ const scope = props.scope as
445
+ | { subscribe(listener: () => void): () => void; getSnapshot(): { value?: Record<string, unknown> } }
446
+ | undefined
447
+ const snapshot = useSyncExternalStore(
448
+ (listener) => scope?.subscribe(listener) ?? (() => {}),
449
+ () => scope?.getSnapshot(),
450
+ )
451
+ const value = snapshot?.value ?? {}
452
+ if (value['enabled'] === false || value['search'] === false) {
453
+ return createElement('span', { className: 'dss_entryWrap' })
454
+ }
455
+ return createElement('span', {
456
+ className: props.wide === true ? 'dss_entryWrap dss_entryWrapWide' : 'dss_entryWrap',
457
+ }, createElement(SwitchFooter, {
458
+ wide: props.wide === true,
459
+ open: props.open,
460
+ scope: props.scope as SwitchCardScope,
461
+ }))
462
+ }
463
+
336
464
  /** 导出字典查找 type 供消费者复用。 */
337
465
  export type { LocaleKey }