dsh-global-todo 0.6.0 → 0.6.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## [0.6.1] — 2026-10-20
2
+
3
+ The follow-up pass from the design spec: the breakpoint now follows the panel, not the window,
4
+ and the form stops competing with itself.
5
+
6
+ - **Panel-measured breakpoint.** A `ResizeObserver` on the column sets `data-wide`; the detail card
7
+ becomes a right-hand card only when the column itself is at least 960px. At 1100 the detail now
8
+ expands inline under its row instead of squeezing the list into a 416px strip.
9
+ - **Inline detail stops repeating itself.** The card title is hidden in inline mode (the row title
10
+ is directly above it) and keeps only the close affordance.
11
+ - **Add form.** The header action turns into an icon-only close while the form is open, so there is
12
+ one `+ Add` on screen; the form orders 更多字段 before 添加, matching the spec.
13
+ - **Rows are 72px tall** at minimum, matching the spec's rhythm, and the detail/card radius is 10px.
14
+ - Dark theme verified against the real host: layer-2 rows, overlay inspector and ghost-active
15
+ selected states all resolve correctly.
16
+
1
17
  ## [0.6.0] — 2026-10-20
2
18
 
3
19
  A design pass over the whole panel: one list, fewer competing controls, one type scale.
Binary file
Binary file
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 = 'ui36'
53
+ const UI_REV = 'ui37'
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
 
@@ -1051,7 +1051,7 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
1051
1051
  .gt-list { display: flex; flex-direction: column; gap: 8px; }
1052
1052
  .gt-item { position: relative; display: flex; flex-direction: column; gap: 8px;
1053
1053
  background: var(--dsw-alias-bg-layer-2); border: 1px solid var(--dsw-alias-border-l1);
1054
- border-radius: 12px; padding: 12px 14px; }
1054
+ border-radius: 10px; padding: 12px 14px; min-height: 72px; box-sizing: border-box; }
1055
1055
  .gt-item:hover { border-color: var(--dsw-alias-border-l2); }
1056
1056
  .gt-item[data-status="doing"] { border-left: 2px solid var(--dsw-alias-brand-primary); }
1057
1057
  .gt-item[data-overdue="1"] { border-left: 2px solid var(--dsw-alias-state-error-primary); }
@@ -1105,6 +1105,9 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
1105
1105
  background: var(--dsw-alias-bg-layer-1); }
1106
1106
  .gt-context-details[data-open="1"] { display: flex; }
1107
1107
  .gt-detail-head { display: flex; align-items: flex-start; gap: 8px; justify-content: space-between; }
1108
+ /* Inline detail sits directly under its row: repeating the title would read as a second item. */
1109
+ .gt-root[data-wide="0"][data-wide-detail="1"] .gt-detail-title { display: none; }
1110
+ .gt-root[data-wide="0"][data-wide-detail="1"] .gt-detail-head { justify-content: flex-end; }
1108
1111
  .gt-detail-title { font-size: 14px; line-height: 22px; font-weight: 600; min-width: 0;
1109
1112
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1110
1113
  .gt-truncated { color: var(--dsw-alias-state-warn-primary); font-size: 12px; }
@@ -1139,12 +1142,12 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
1139
1142
  /* Wide screens: the open row's detail becomes a card on the right instead of pushing the list
1140
1143
  down. Deliberately CSS-only over unchanged markup: one detail implementation, and it only
1141
1144
  applies where there is room for a second column. */
1142
- @media (min-width: 1224px) {
1145
+ /* Wide layout follows the panel's measured width (data-wide), not the viewport. */
1143
1146
  /* Only the list gives way: the title row and the toolbar keep their full width, so the
1144
1147
  controls never wrap or hide behind the card. */
1145
- .gt-root[data-wide-detail="1"] .gt-groups { padding-right: 360px; }
1146
- .gt-root[data-wide-detail="1"] .gt-inner { position: relative; }
1147
- .gt-root[data-wide-detail="1"] .gt-item[data-open="1"] .gt-detail {
1148
+ .gt-root[data-wide="1"][data-wide-detail="1"] .gt-groups { padding-right: 360px; }
1149
+ .gt-root[data-wide="1"][data-wide-detail="1"] .gt-inner { position: relative; }
1150
+ .gt-root[data-wide="1"][data-wide-detail="1"] .gt-item[data-open="1"] .gt-detail {
1148
1151
  /* Anchored to the panel's own column (.gt-inner), not to the viewport: the slot does not
1149
1152
  always fill the window, and a viewport-centred offset put the card over the list. */
1150
1153
  /* The var fallback matters for the static previews (server-rendered, so effects never
@@ -1158,7 +1161,6 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
1158
1161
  overflow: auto; padding: 16px; margin: 0; border: 1px solid var(--dsw-alias-border-l2);
1159
1162
  border-radius: 12px; background: var(--dsw-alias-bg-overlay); z-index: 30;
1160
1163
  }
1161
- }
1162
1164
  .gt-section { display: flex; flex-direction: column; gap: 5px; }
1163
1165
  .gt-completion { white-space: pre-wrap; word-break: break-word; line-height: 1.5; }
1164
1166
  .gt-section-title { color: var(--dsw-alias-label-secondary); font-size: 12px; line-height: 18px; font-weight: 600; }
@@ -2166,6 +2168,10 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
2166
2168
  // reach without curl.
2167
2169
  const [settingsOpen, setSettingsOpen] = useState(false)
2168
2170
  const [settingsBox, setSettingsBox] = useState(null)
2171
+ // The detail card's breakpoint follows the panel's own width, not the viewport: the
2172
+ // slot is ~265px narrower than the window, so a viewport query opens the two-column
2173
+ // layout while the panel is still too narrow for it.
2174
+ const [panelWidth, setPanelWidth] = useState(null)
2169
2175
  const [contextOpen, setContextOpen] = useState(false)
2170
2176
  const [tokenDraft, setTokenDraft] = useState('')
2171
2177
  const [ingestDraft, setIngestDraft] = useState(false)
@@ -2406,6 +2412,20 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
2406
2412
  }
2407
2413
  }, [lang])
2408
2414
 
2415
+ useEffect(() => {
2416
+ const element = innerRef.current
2417
+ if (element === null || typeof window === 'undefined') return undefined
2418
+ const measure = () => setPanelWidth(element.getBoundingClientRect().width)
2419
+ measure()
2420
+ window.addEventListener('resize', measure)
2421
+ const observer = typeof window.ResizeObserver === 'function' ? new window.ResizeObserver(measure) : null
2422
+ if (observer !== null) observer.observe(element)
2423
+ return () => {
2424
+ window.removeEventListener('resize', measure)
2425
+ if (observer !== null) observer.disconnect()
2426
+ }
2427
+ }, [])
2428
+
2409
2429
  useEffect(() => {
2410
2430
  if (!settingsOpen || typeof window === 'undefined') {
2411
2431
  setSettingsBox(null)
@@ -3157,6 +3177,7 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
3157
3177
  {
3158
3178
  className: 'gt-root',
3159
3179
  'data-wide-detail': expandedId === '' ? '0' : '1',
3180
+ 'data-wide': panelWidth === null || panelWidth >= 960 ? '1' : '0',
3160
3181
  // The slot does not always fill the window, so the card's viewport offsets are
3161
3182
  // measured from the panel itself instead of assumed from 50vw.
3162
3183
  style:
@@ -3198,15 +3219,16 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
3198
3219
  'button',
3199
3220
  {
3200
3221
  type: 'button',
3201
- className: 'gt-btn gt-btn-primary',
3202
- 'aria-expanded': adding,
3203
- onClick: () => {
3204
- setAdding((value) => !value)
3205
- setMore(false)
3222
+ className: adding ? 'gt-btn gt-btn-quiet' : 'gt-btn gt-btn-primary',
3223
+ 'aria-expanded': adding,
3224
+ title: adding ? t('action.less') : t('action.add'),
3225
+ onClick: () => {
3226
+ setAdding((value) => !value)
3227
+ setMore(false)
3228
+ },
3206
3229
  },
3207
- },
3208
- h(Icon, { name: 'plus', size: 13 }),
3209
- t('action.add'),
3230
+ h(Icon, { name: adding ? 'close' : 'plus', size: 13 }),
3231
+ adding ? '' : t('action.add'),
3210
3232
  ),
3211
3233
  // One overflow menu instead of four competing buttons: the primary action is
3212
3234
  // adding a todo; everything else is occasional.
@@ -3351,14 +3373,14 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
3351
3373
  }),
3352
3374
  h(
3353
3375
  'button',
3354
- { type: 'button', className: 'gt-btn gt-btn-primary', disabled: busyId === '__new__' || draft.title.trim() === '', onClick: () => void submit() },
3355
- h(Icon, { name: 'plus', size: 13 }),
3356
- t('action.add'),
3376
+ { type: 'button', className: 'gt-btn gt-btn-quiet', 'aria-expanded': more, onClick: () => setMore((value) => !value) },
3377
+ more ? t('action.less') : t('action.more'),
3357
3378
  ),
3358
3379
  h(
3359
3380
  'button',
3360
- { type: 'button', className: 'gt-btn gt-btn-quiet', 'aria-expanded': more, onClick: () => setMore((value) => !value) },
3361
- more ? t('action.less') : t('action.more'),
3381
+ { type: 'button', className: 'gt-btn gt-btn-primary', disabled: busyId === '__new__' || draft.title.trim() === '', onClick: () => void submit() },
3382
+ h(Icon, { name: 'plus', size: 13 }),
3383
+ t('action.add'),
3362
3384
  ),
3363
3385
  ),
3364
3386
  more
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.0'
39
+ export const VERSION = '0.6.1'
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.0",
3
+ "version": "0.6.1",
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",