dsh-global-todo 0.6.3 → 0.6.4

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
@@ -1,3 +1,20 @@
1
+ ## [0.6.4] — 2026-10-20
2
+
3
+ The settings panel, rebuilt as an inspector instead of a stack of labelled rows.
4
+
5
+ - **Header and footer, finally.** A title row with a close button, three labelled groups divided
6
+ by hairlines, and a save row pinned at the bottom — the token field no longer pushes the save
7
+ buttons into the middle of the form with a field stranded beneath them.
8
+ - **Every field says what it does.** Help text sits under each label (`settings.*Hint`) rather than
9
+ being implied by the label; the label-and-control grid keeps number inputs aligned at a fixed
10
+ 76px right-aligned box.
11
+ - **Ingest fields dim when ingest is off**, and `New token` becomes a quiet text link on the token
12
+ label row (`Rotate token`) instead of a third equal-weight button.
13
+ - **About section** shows the data file and version on labelled rows; the card sizes to its content
14
+ and scrolls internally when the window is short.
15
+ - `npm run screenshots` now also photographs a settings variant, so the README image is
16
+ reproducible instead of hand-made.
17
+
1
18
  ## [0.6.3] — 2026-10-20
2
19
 
3
20
  States that were still speaking to nobody.
package/README.md CHANGED
@@ -33,6 +33,8 @@ one record:
33
33
 
34
34
  ![One row opened: inline editing, attachments with 打开 / 复制路径, activity](docs/screenshots/panel-detail.png)
35
35
 
36
+ ![Settings: grouped fields, hints under each label, one save row](docs/screenshots/panel-settings.png)
37
+
36
38
  These are produced by `npm run screenshots` (which chains `npm run preview`): the real client
37
39
  half is rendered to static HTML with no Host and no dependencies, then photographed at 1280×720
38
40
  into `docs/screenshots/`. Playwright is not a dependency — the tool borrows the copy a DSH
@@ -339,7 +341,7 @@ npm run preview # render the panel to preview/panel.html (+ detail and sel
339
341
  npm run screenshots # re-photograph those previews into docs/screenshots/, 1280×720
340
342
  ```
341
343
 
342
- The three PNGs under `docs/screenshots/` ship with the package and are embedded in both
344
+ The PNGs under `docs/screenshots/` ship with the package and are embedded in both
343
345
  READMEs, so re-run `npm run screenshots` whenever the panel UI changes.
344
346
 
345
347
  ```text
package/README.zh.md CHANGED
@@ -30,6 +30,8 @@ DSH 自带的 `todo_write` 属于**某一个 agent 会话**:整表替换、三
30
30
 
31
31
  ![展开一条:就地编辑、附件带「打开 / 复制路径」、动态轨迹](docs/screenshots/panel-detail.png)
32
32
 
33
+ ![设置:分组字段、说明写在标签下方、底部一行保存](docs/screenshots/panel-settings.png)
34
+
33
35
  图都由 `npm run screenshots`(内部串起 `npm run preview`)生成:先把真实的客户端半区渲染成静态
34
36
  HTML(不依赖宿主、零依赖),再以 1280×720 拍进 `docs/screenshots/`。Playwright 不是依赖——工具
35
37
  借的是 DSH profile 里已经装好的那份,或 `DSH_PLAYWRIGHT` 指向的那份。
@@ -230,7 +232,7 @@ npm run preview # 把面板渲染成 preview/panel.html(另附详情态
230
232
  npm run screenshots # 把这些预览重新拍成 docs/screenshots/,1280×720
231
233
  ```
232
234
 
233
- `docs/screenshots/` 下那三张 PNG 会随包发布、并嵌在两份 README 里,所以面板 UI 一改就要重跑
235
+ `docs/screenshots/` 下那几张 PNG 会随包发布、并嵌在两份 README 里,所以面板 UI 一改就要重跑
234
236
  `npm run screenshots`。
235
237
 
236
238
  ```text
Binary file
Binary file
Binary file
package/lib/client.js CHANGED
@@ -50,7 +50,7 @@ window.__ModuleLoader__.load({
50
50
  * different mechanisms (restart vs. hot reload), so "which one am I looking
51
51
  * at" needs an answer that the bundle itself carries.
52
52
  */
53
- const UI_REV = 'ui39'
53
+ const UI_REV = 'ui40'
54
54
  /** Last language resolved for the shell; module-level so a fetch failure can be worded too. */
55
55
  let activeLang = 'zh'
56
56
 
@@ -100,12 +100,18 @@ window.__ModuleLoader__.load({
100
100
  'settings.ingestEnable': '启用外部写入',
101
101
  'settings.token': '访问 token',
102
102
  'settings.tokenSet': '已设置,留空保持不变',
103
- 'settings.generate': '生成新 token',
104
- 'settings.confirmBelow': '低于此置信度进收件箱',
103
+ 'settings.generate': '轮换 token',
104
+ 'settings.confirmBelow': '置信度阈值',
105
105
  'settings.save': '保存',
106
106
  'settings.cancel': '取消',
107
107
  'settings.saved': '设置已保存',
108
108
  'settings.about': '关于',
109
+ 'settings.remindersHint': '到期时在面板顶部提示,并按设置提前。',
110
+ 'settings.ingestHint': '允许其他工具带 token 写入,先进收件箱。',
111
+ 'settings.tokenHint': '留空表示不改动;轮换后旧 token 立刻失效。',
112
+ 'settings.confirmBelowHint': '置信度低于这个值的条目进收件箱,不进清单。',
113
+ 'settings.storeFile': '数据文件',
114
+ 'settings.version': '版本',
109
115
  'inbox.explain': '以下由外部写入或 Agent 提交,确认后才进入清单。',
110
116
  'notice.exported': '已导出 {n} 条待办',
111
117
  'notice.imported': '已导入 {n} 条(合并,现有共 {total} 条)',
@@ -347,12 +353,18 @@ window.__ModuleLoader__.load({
347
353
  'settings.ingestEnable': 'Accept external writes',
348
354
  'settings.token': 'Access token',
349
355
  'settings.tokenSet': 'Set — leave blank to keep',
350
- 'settings.generate': 'New token',
351
- 'settings.confirmBelow': 'Below this confidence → inbox',
356
+ 'settings.generate': 'Rotate token',
357
+ 'settings.confirmBelow': 'Confidence threshold',
352
358
  'settings.save': 'Save',
353
359
  'settings.cancel': 'Cancel',
354
360
  'settings.saved': 'Settings saved',
355
361
  'settings.about': 'About',
362
+ 'settings.remindersHint': 'Flag a deadline at the top of the panel when it comes due.',
363
+ 'settings.ingestHint': 'Let other tools POST todos with a token; they start in the inbox.',
364
+ 'settings.tokenHint': 'Leave blank to keep the current one; rotating invalidates the old token.',
365
+ 'settings.confirmBelowHint': 'Items below this confidence go to the inbox instead of the list.',
366
+ 'settings.storeFile': 'Data file',
367
+ 'settings.version': 'Version',
356
368
  'inbox.explain': 'Written by external sources or the agent. Confirm it before it joins the list.',
357
369
  'notice.exported': 'Exported {n} todos',
358
370
  'notice.imported': 'Imported {n} (merged; {total} in the list now)',
@@ -1102,9 +1114,35 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
1102
1114
  border-radius: 6px; cursor: pointer; max-width: 300px; overflow: hidden; text-overflow: ellipsis;
1103
1115
  white-space: nowrap; }
1104
1116
  .gt-reminder-item:hover { background: var(--dsw-alias-button-ghost-active-fill); }
1105
- .gt-settings { display: flex; flex-direction: column; gap: 12px; padding: 16px; width: 340px;
1106
- box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l2); border-radius: 12px;
1107
- background: var(--dsw-alias-bg-overlay); font-size: 13px; line-height: 20px; }
1117
+ .gt-settings { display: flex; flex-direction: column; width: 360px; max-width: 100%;
1118
+ box-sizing: border-box; overflow: hidden; border: 1px solid var(--dsw-alias-border-l2);
1119
+ border-radius: 12px; background: var(--dsw-alias-bg-overlay); font-size: 13px; line-height: 20px; }
1120
+ .gt-settings-head { display: flex; flex: 0 0 auto; align-items: center; justify-content: space-between;
1121
+ gap: 8px; padding: 10px 10px 10px 16px; border-bottom: 1px solid var(--dsw-alias-border-l1); }
1122
+ .gt-settings-title { font-size: 14px; font-weight: 600; }
1123
+ .gt-settings-body { display: flex; flex: 1 1 auto; flex-direction: column; gap: 16px;
1124
+ padding: 14px 16px; overflow: auto; min-height: 0; }
1125
+ .gt-settings-group { display: flex; flex-direction: column; gap: 8px; }
1126
+ .gt-settings-group + .gt-settings-group { border-top: 1px solid var(--dsw-alias-border-l1); padding-top: 14px; }
1127
+ .gt-settings-group-title { font-size: 12px; line-height: 18px; font-weight: 600;
1128
+ color: var(--dsw-alias-label-secondary); }
1129
+ .gt-check-row { display: flex; align-items: flex-start; gap: 8px; cursor: pointer; }
1130
+ .gt-check-row input { margin: 3px 0 0; flex: 0 0 auto; }
1131
+ .gt-check-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
1132
+ .gt-check-label { color: var(--dsw-alias-label-primary); }
1133
+ .gt-hint { font-size: 12px; line-height: 18px; color: var(--dsw-alias-label-secondary); }
1134
+ .gt-field { display: flex; flex-direction: column; gap: 6px; }
1135
+ .gt-field[data-muted="1"], .gt-field-row[data-muted="1"] { opacity: .55; }
1136
+ .gt-field-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
1137
+ .gt-field-label { font-size: 13px; color: var(--dsw-alias-label-primary); }
1138
+ .gt-field-row { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 8px; }
1139
+ .gt-input-num { width: 76px; text-align: right; font-variant-numeric: tabular-nums; }
1140
+ .gt-link-btn { border: none; background: transparent; padding: 0; font: inherit; font-size: 12px;
1141
+ color: var(--dsw-alias-link); cursor: pointer; }
1142
+ .gt-link-btn:disabled { color: var(--dsw-alias-label-secondary); cursor: default; }
1143
+ .gt-about-row { display: grid; grid-template-columns: 88px minmax(0, 1fr); align-items: baseline; gap: 8px; }
1144
+ .gt-settings-foot { display: flex; flex: 0 0 auto; justify-content: flex-end; gap: 8px;
1145
+ padding: 12px 16px; border-top: 1px solid var(--dsw-alias-border-l1); }
1108
1146
  .gt-group-select { display: none; min-height: 28px; max-width: 150px; padding: 0 8px; font: inherit;
1109
1147
  font-size: 12.5px; color: var(--dsw-alias-label-primary); background: var(--dsw-alias-bg-layer-1);
1110
1148
  border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; }
@@ -1114,8 +1152,7 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
1114
1152
  line-height: 20px; }
1115
1153
  .gt-settings-path { font-size: 11.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1116
1154
  .gt-settings .gt-input { flex: 0 0 auto; width: 100%; min-height: 32px; }
1117
- .gt-settings .gt-check-line .gt-input { width: 64px; }
1118
- .gt-settings-head { font-weight: 650; font-size: 14px; }
1155
+ .gt-settings .gt-input-num { width: 76px; }
1119
1156
  .gt-check-line { display: flex; align-items: center; gap: 8px; justify-content: flex-start;
1120
1157
  color: var(--dsw-alias-label-primary); }
1121
1158
  .gt-settings-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
@@ -2169,6 +2206,9 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
2169
2206
  // The preview harness cannot re-render after a click, so it may ask to start in
2170
2207
  // selection mode; a person toggles it with the `选择` control.
2171
2208
  const [selectMode, setSelectMode] = useState(props.initialSelectMode === true)
2209
+ // Only the static preview asks for this: the panel opens settings by click, but the
2210
+ // SSR harness cannot click, so the screenshot variant has to start there.
2211
+ const initialSettingsOpen = props.initialSettingsOpen === true
2172
2212
  // The picker's filters mirror the shell's own visibility rule, but the shape of the
2173
2213
  // real snapshot has never been observable from here — a preview fixture is written by
2174
2214
  // the same hand as the filter, so it can only confirm itself. Report the shape once per
@@ -2187,7 +2227,7 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
2187
2227
  const [importAsk, setImportAsk] = useState(null)
2188
2228
  // One settings popover: reminders and the external door both need a place a person can
2189
2229
  // reach without curl.
2190
- const [settingsOpen, setSettingsOpen] = useState(false)
2230
+ const [settingsOpen, setSettingsOpen] = useState(initialSettingsOpen)
2191
2231
  const [settingsBox, setSettingsBox] = useState(null)
2192
2232
  // The detail card's breakpoint follows the panel's own width, not the viewport: the
2193
2233
  // slot is ~265px narrower than the window, so a viewport query opens the two-column
@@ -2436,13 +2476,15 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
2436
2476
  useEffect(() => {
2437
2477
  const element = innerRef.current
2438
2478
  if (element === null || typeof window === 'undefined') return undefined
2479
+ // The static preview supplies a `window` stub without event plumbing.
2480
+ const listen = typeof window.addEventListener === 'function'
2439
2481
  const measure = () => setPanelWidth(element.getBoundingClientRect().width)
2440
2482
  measure()
2441
- window.addEventListener('resize', measure)
2483
+ if (listen === true) window.addEventListener('resize', measure)
2442
2484
  const observer = typeof window.ResizeObserver === 'function' ? new window.ResizeObserver(measure) : null
2443
2485
  if (observer !== null) observer.observe(element)
2444
2486
  return () => {
2445
- window.removeEventListener('resize', measure)
2487
+ if (listen === true) window.removeEventListener('resize', measure)
2446
2488
  if (observer !== null) observer.disconnect()
2447
2489
  }
2448
2490
  }, [])
@@ -2457,14 +2499,19 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
2457
2499
  if (element === null) return
2458
2500
  const rect = element.getBoundingClientRect()
2459
2501
  setSettingsBox({
2460
- top: Math.round(rect.top + 210),
2502
+ // Just under the title block: high enough that the whole card fits, low enough
2503
+ // that it never covers the panel title or the header actions.
2504
+ top: Math.round(rect.top + 56),
2461
2505
  right: Math.round(window.innerWidth - rect.right + 20),
2462
2506
  bottom: Math.round(window.innerHeight - rect.bottom + 20),
2463
2507
  })
2464
2508
  }
2509
+ const listen = typeof window.addEventListener === 'function'
2465
2510
  measure()
2466
- window.addEventListener('resize', measure)
2467
- return () => window.removeEventListener('resize', measure)
2511
+ if (listen === true) window.addEventListener('resize', measure)
2512
+ return () => {
2513
+ if (listen === true) window.removeEventListener('resize', measure)
2514
+ }
2468
2515
  }, [settingsOpen, lang])
2469
2516
 
2470
2517
  useEffect(() => {
@@ -3286,77 +3333,141 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
3286
3333
  position: 'fixed',
3287
3334
  top: `${settingsBox.top}px`,
3288
3335
  right: `${settingsBox.right}px`,
3289
- bottom: `${settingsBox.bottom}px`,
3290
- maxHeight: 'none',
3291
- overflow: 'auto',
3336
+ // Height follows the content, capped by the room below: stretching to
3337
+ // `bottom` left a tall empty card with the footer stranded at the floor.
3338
+ maxHeight: `calc(100dvh - ${settingsBox.top + 20}px)`,
3292
3339
  zIndex: 40,
3293
3340
  },
3294
3341
  },
3295
- h('div', { className: 'gt-settings-head' }, t('settings.title')),
3296
- h('div', { className: 'gt-section-title' }, t('settings.reminders')),
3297
- h(
3298
- 'label',
3299
- { className: 'gt-check-line' },
3300
- h('input', { type: 'checkbox', checked: remindersDraft, onChange: (event) => setRemindersDraft(event.target.checked) }),
3301
- t('settings.remindersEnable'),
3302
- ),
3303
3342
  h(
3304
- 'label',
3305
- { className: 'gt-check-line' },
3306
- t('settings.lead'),
3307
- h('input', {
3308
- type: 'number',
3309
- min: 0,
3310
- max: 1440,
3311
- className: 'gt-input gt-input-xs',
3312
- value: leadDraft,
3313
- onChange: (event) => setLeadDraft(event.target.value),
3314
- }),
3343
+ 'div',
3344
+ { className: 'gt-settings-head' },
3345
+ h('span', { className: 'gt-settings-title' }, t('settings.title')),
3346
+ h(
3347
+ 'button',
3348
+ {
3349
+ type: 'button',
3350
+ className: 'gt-iconbtn',
3351
+ title: t('settings.cancel'),
3352
+ 'aria-label': t('settings.cancel'),
3353
+ onClick: () => setSettingsOpen(false),
3354
+ },
3355
+ h(Icon, { name: 'close', size: 13 }),
3356
+ ),
3315
3357
  ),
3316
- h('div', { className: 'gt-section-title' }, t('settings.ingest')),
3317
3358
  h(
3318
- 'label',
3319
- { className: 'gt-check-line' },
3320
- h('input', { type: 'checkbox', checked: ingestDraft, onChange: (event) => setIngestDraft(event.target.checked) }),
3321
- t('settings.ingestEnable'),
3359
+ 'div',
3360
+ { className: 'gt-settings-body' },
3361
+ h(
3362
+ 'div',
3363
+ { className: 'gt-settings-group' },
3364
+ h('div', { className: 'gt-settings-group-title' }, t('settings.reminders')),
3365
+ h(
3366
+ 'label',
3367
+ { className: 'gt-check-row' },
3368
+ h('input', { type: 'checkbox', checked: remindersDraft, onChange: (event) => setRemindersDraft(event.target.checked) }),
3369
+ h(
3370
+ 'span',
3371
+ { className: 'gt-check-main' },
3372
+ h('span', { className: 'gt-check-label' }, t('settings.remindersEnable')),
3373
+ h('span', { className: 'gt-hint' }, t('settings.remindersHint')),
3374
+ ),
3375
+ ),
3376
+ h(
3377
+ 'div',
3378
+ { className: 'gt-field-row' },
3379
+ h('label', { className: 'gt-field-label', htmlFor: 'gt-lead' }, t('settings.lead')),
3380
+ h('input', {
3381
+ id: 'gt-lead',
3382
+ type: 'number',
3383
+ min: 0,
3384
+ max: 1440,
3385
+ className: 'gt-input gt-input-num',
3386
+ disabled: remindersDraft !== true,
3387
+ value: leadDraft,
3388
+ onChange: (event) => setLeadDraft(event.target.value),
3389
+ }),
3390
+ ),
3391
+ ),
3392
+ h(
3393
+ 'div',
3394
+ { className: 'gt-settings-group' },
3395
+ h('div', { className: 'gt-settings-group-title' }, t('settings.ingest')),
3396
+ h(
3397
+ 'label',
3398
+ { className: 'gt-check-row' },
3399
+ h('input', { type: 'checkbox', checked: ingestDraft, onChange: (event) => setIngestDraft(event.target.checked) }),
3400
+ h(
3401
+ 'span',
3402
+ { className: 'gt-check-main' },
3403
+ h('span', { className: 'gt-check-label' }, t('settings.ingestEnable')),
3404
+ h('span', { className: 'gt-hint' }, t('settings.ingestHint')),
3405
+ ),
3406
+ ),
3407
+ h(
3408
+ 'div',
3409
+ { className: 'gt-field', 'data-muted': ingestDraft === true ? '0' : '1' },
3410
+ h(
3411
+ 'div',
3412
+ { className: 'gt-field-head' },
3413
+ h('label', { className: 'gt-field-label', htmlFor: 'gt-token' }, t('settings.token')),
3414
+ h('button', { type: 'button', className: 'gt-link-btn', disabled: ingestDraft !== true, onClick: generateToken }, t('settings.generate')),
3415
+ ),
3416
+ h('input', {
3417
+ id: 'gt-token',
3418
+ className: 'gt-input',
3419
+ type: 'password',
3420
+ disabled: ingestDraft !== true,
3421
+ value: tokenDraft,
3422
+ placeholder:
3423
+ state.health?.settings?.ingest?.tokenSet === true ? t('settings.tokenSet') : t('settings.token'),
3424
+ autoComplete: 'new-password',
3425
+ onChange: (event) => setTokenDraft(event.target.value),
3426
+ }),
3427
+ h('span', { className: 'gt-hint' }, t('settings.tokenHint')),
3428
+ ),
3429
+ h(
3430
+ 'div',
3431
+ { className: 'gt-field-row', 'data-muted': ingestDraft === true ? '0' : '1' },
3432
+ h('label', { className: 'gt-field-label', htmlFor: 'gt-below' }, t('settings.confirmBelow')),
3433
+ h('input', {
3434
+ id: 'gt-below',
3435
+ type: 'number',
3436
+ min: 0,
3437
+ max: 1,
3438
+ step: 0.05,
3439
+ className: 'gt-input gt-input-num',
3440
+ disabled: ingestDraft !== true,
3441
+ value: belowDraft,
3442
+ onChange: (event) => setBelowDraft(event.target.value),
3443
+ }),
3444
+ ),
3445
+ h('span', { className: 'gt-hint' }, t('settings.confirmBelowHint')),
3446
+ ),
3447
+ h(
3448
+ 'div',
3449
+ { className: 'gt-settings-group' },
3450
+ h('div', { className: 'gt-settings-group-title' }, t('settings.about')),
3451
+ h(
3452
+ 'div',
3453
+ { className: 'gt-about-row' },
3454
+ h('span', { className: 'gt-field-label' }, t('settings.storeFile')),
3455
+ h('span', { className: 'gt-mono gt-settings-path', title: state.health?.store?.file ?? '' }, state.health?.store?.file ?? ''),
3456
+ ),
3457
+ h(
3458
+ 'div',
3459
+ { className: 'gt-about-row' },
3460
+ h('span', { className: 'gt-field-label' }, t('settings.version')),
3461
+ h('span', { className: 'gt-mono gt-hint' }, `v${state.health?.version ?? ''} · ${UI_REV}`),
3462
+ ),
3463
+ ),
3322
3464
  ),
3323
- h('input', {
3324
- className: 'gt-input',
3325
- type: 'password',
3326
- value: tokenDraft,
3327
- placeholder:
3328
- state.health?.settings?.ingest?.tokenSet === true ? t('settings.tokenSet') : t('settings.token'),
3329
- autoComplete: 'new-password',
3330
- onChange: (event) => setTokenDraft(event.target.value),
3331
- }),
3332
3465
  h(
3333
3466
  'div',
3334
- { className: 'gt-settings-row' },
3335
- h('button', { type: 'button', className: 'gt-btn gt-btn-quiet', onClick: generateToken }, t('settings.generate')),
3467
+ { className: 'gt-settings-foot' },
3336
3468
  h('button', { type: 'button', className: 'gt-btn gt-btn-quiet', onClick: () => setSettingsOpen(false) }, t('settings.cancel')),
3337
3469
  h('button', { type: 'button', className: 'gt-btn gt-btn-primary', onClick: () => void saveSettings() }, t('settings.save')),
3338
3470
  ),
3339
- h(
3340
- 'label',
3341
- { className: 'gt-check-line' },
3342
- t('settings.confirmBelow'),
3343
- h('input', {
3344
- type: 'number',
3345
- min: 0,
3346
- max: 1,
3347
- step: 0.05,
3348
- className: 'gt-input gt-input-xs',
3349
- value: belowDraft,
3350
- onChange: (event) => setBelowDraft(event.target.value),
3351
- }),
3352
- ),
3353
- h('div', { className: 'gt-section-title' }, t('settings.about')),
3354
- h(
3355
- 'div',
3356
- { className: 'gt-muted gt-mono gt-settings-path', title: state.health?.store?.file ?? '' },
3357
- state.health?.store?.file ?? '',
3358
- ),
3359
- h('div', { className: 'gt-muted' }, `v${state.health?.version ?? ''} · ${UI_REV}`),
3360
3471
  )
3361
3472
  : null,
3362
3473
 
package/lib/index.js CHANGED
@@ -36,7 +36,7 @@ import { createStore, resolveStorePath } from './store.js'
36
36
  import { createTools } from './tools.js'
37
37
 
38
38
  /** Must equal `package.json`'s version; `npm run check` enforces that. */
39
- export const VERSION = '0.6.3'
39
+ export const VERSION = '0.6.4'
40
40
 
41
41
  /** Plugin id. Also the Loader row id in `cordis.patch.yml`. */
42
42
  export const name = 'dsh-global-todo'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-global-todo",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "icon": "./docs/icon.svg",
5
5
  "description": "Global, cross-session todos for DeepSeek Harness: one list owned by the human and the agent together, with creation time, attached documents and links, and one-click hand-off to an agent session. 全局待办:跨会话一份清单,记录加入时间,可挂文档与地址,可一键交给 Agent 继续干。",
6
6
  "type": "module",