dsh-global-todo 0.4.4 → 0.4.6

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
@@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.6] — 2026-10-19
11
+
12
+ The card existed; where it landed depended on a viewport assumption that the real shell does not
13
+ honour.
14
+
15
+ - **The detail card is measured from the panel, not from `50vw`.** In the GUI the plugin slot does
16
+ not fill the window, so the viewport-derived right edge put the card on top of the list; the card
17
+ now measures `.gt-inner` on open, on window resize and on a slot resize (sidebar collapse), and
18
+ positions itself from that rectangle. The CSS fallback (used by the static previews, which are
19
+ server-rendered and run no effects) assumes the centred 980px column.
20
+ ## [0.4.5] — 2026-10-19
21
+
22
+ Polishing where the side detail sits, now that it exists.
23
+
24
+ - **The card is bracketed between the toolbar and the footer**, so it no longer covers the dir
25
+ controls or the store line; the toolbar keeps its full width and only the list gives way; the
26
+ card scrolls as one column instead of pushing the footer around. Same CSS-only mechanism, no
27
+ markup change.
10
28
  ## [0.4.4] — 2026-10-19
11
29
 
12
30
  Opening a todo on a wide screen used to push the list down and leave half the window unused.
package/docs/DESIGN.md CHANGED
@@ -61,8 +61,11 @@ $DSH_HOME/global-todo/todos.json ← { version: 1, todos: [ … ] }
61
61
 
62
62
  Expanding a row inline is right on a narrow window and wrong on a wide one: the list jumps, the
63
63
  row you were reading slides away, and the rest of the screen sits empty. Above 1100px the open
64
- row's detail is lifted out of the flow into a card at the panel's right edge, and the list gets
65
- right padding equal to that card. This is a CSS-only override over unchanged markup
64
+ row's detail is lifted out of the flow into a card bracketed between the toolbar and the footer
65
+ at the panel's right edge; only the list gives way (the toolbar keeps full width), and the list gets
66
+ padding equal to that card. The card's offsets are measured from the panel's own column on mount
67
+ and on resize — the slot does not always fill the window, so a viewport-derived offset put the card
68
+ over the list inside the real GUI. This is a CSS-only override over unchanged markup
66
69
  (`.gt-root[data-wide-detail]` plus one media query) precisely so there is still exactly one
67
70
  detail implementation — a second render path for wide screens would drift from the inline one at
68
71
  the first field change.
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 = 'ui27'
53
+ const UI_REV = 'ui29'
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
 
@@ -1012,11 +1012,18 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
1012
1012
  down. Deliberately CSS-only over unchanged markup: one detail implementation, and it only
1013
1013
  applies where there is room for a second column. */
1014
1014
  @media (min-width: 1100px) {
1015
- .gt-root[data-wide-detail="1"] .gt-inner { padding-right: 408px; }
1015
+ /* Only the list gives way: the title row and the toolbar keep their full width, so the
1016
+ controls never wrap or hide behind the card. */
1017
+ .gt-root[data-wide-detail="1"] .gt-groups { padding-right: 380px; }
1018
+ .gt-root[data-wide-detail="1"] .gt-inner { position: relative; }
1016
1019
  .gt-root[data-wide-detail="1"] .gt-item[data-open="1"] .gt-detail {
1017
- /* Follow the panel's own right edge (it is centred and capped at 980px) instead of the
1018
- viewport's, so the card stays attached to the list on ultra-wide screens. */
1019
- position: fixed; top: 96px; right: max(24px, calc(50vw - 466px)); width: 360px; max-height: calc(100vh - 140px);
1020
+ /* Anchored to the panel's own column (.gt-inner), not to the viewport: the slot does not
1021
+ always fill the window, and a viewport-centred offset put the card over the list. */
1022
+ /* The var fallback matters for the static previews (server-rendered, so effects never
1023
+ run): they assume the panel is the centred 980px column. */
1024
+ position: fixed; top: var(--gt-detail-top, 208px);
1025
+ right: var(--gt-detail-right, max(24px, calc((100vw - 980px) / 2 + 24px)));
1026
+ bottom: var(--gt-detail-bottom, 64px); width: 360px;
1020
1027
  overflow: auto; padding: 16px; margin: 0; border: 1px solid var(--dsw-alias-border-l2);
1021
1028
  border-radius: 12px; background: var(--dsw-alias-bg-overlay); z-index: 30;
1022
1029
  }
@@ -2143,6 +2150,10 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
2143
2150
  // The store counts file changes made outside this process (another machine, a sync
2144
2151
  // client). A new count means the panel's view was stale a moment ago; say so once.
2145
2152
  const externalRef = useRef(null)
2153
+ // The wide-screen detail card floats over the panel, so its offsets are measured from
2154
+ // the panel's own column rather than derived from the viewport width.
2155
+ const innerRef = useRef(null)
2156
+ const [detailAnchor, setDetailAnchor] = useState(null)
2146
2157
  const titleRef = useRef(null)
2147
2158
  const searchRef = useRef(null)
2148
2159
  const undoTimer = useRef(null)
@@ -2196,6 +2207,35 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
2196
2207
  }
2197
2208
  }, [lang])
2198
2209
 
2210
+ useEffect(() => {
2211
+ if (expandedId === '' || typeof window === 'undefined') {
2212
+ setDetailAnchor(null)
2213
+ return undefined
2214
+ }
2215
+ const measure = () => {
2216
+ const element = innerRef.current
2217
+ if (element === null) return
2218
+ const rect = element.getBoundingClientRect()
2219
+ setDetailAnchor({
2220
+ top: Math.round(rect.top + 170),
2221
+ right: Math.round(window.innerWidth - rect.right + 20),
2222
+ bottom: Math.round(window.innerHeight - rect.bottom + 20),
2223
+ })
2224
+ }
2225
+ measure()
2226
+ window.addEventListener('resize', measure)
2227
+ // The slot can change width without a window resize (sidebar collapse), which would
2228
+ // leave the card at stale coordinates.
2229
+ // `window.` on purpose: the static scan rejects a bare global it cannot see defined.
2230
+ const Observer = typeof window.ResizeObserver === 'function' ? window.ResizeObserver : null
2231
+ const observer = Observer === null ? null : new Observer(measure)
2232
+ if (observer !== null && innerRef.current !== null) observer.observe(innerRef.current)
2233
+ return () => {
2234
+ window.removeEventListener('resize', measure)
2235
+ if (observer !== null) observer.disconnect()
2236
+ }
2237
+ }, [expandedId, lang])
2238
+
2199
2239
  useEffect(() => {
2200
2240
  mounted.current = true
2201
2241
  void reload()
@@ -2773,10 +2813,23 @@ button.gt-chip[data-on="1"] { color: var(--dsw-alias-label-primary); border-colo
2773
2813
 
2774
2814
  return h(
2775
2815
  'div',
2776
- { className: 'gt-root', 'data-wide-detail': expandedId === '' ? '0' : '1' },
2816
+ {
2817
+ className: 'gt-root',
2818
+ 'data-wide-detail': expandedId === '' ? '0' : '1',
2819
+ // The slot does not always fill the window, so the card's viewport offsets are
2820
+ // measured from the panel itself instead of assumed from 50vw.
2821
+ style:
2822
+ detailAnchor === null
2823
+ ? undefined
2824
+ : {
2825
+ '--gt-detail-top': `${detailAnchor.top}px`,
2826
+ '--gt-detail-right': `${detailAnchor.right}px`,
2827
+ '--gt-detail-bottom': `${detailAnchor.bottom}px`,
2828
+ },
2829
+ },
2777
2830
  h(
2778
2831
  'div',
2779
- { className: 'gt-inner' },
2832
+ { className: 'gt-inner', ref: innerRef },
2780
2833
  h(
2781
2834
  'div',
2782
2835
  { className: 'gt-head' },
package/lib/index.js CHANGED
@@ -35,7 +35,7 @@ import { createStore, resolveStorePath } from './store.js'
35
35
  import { createTools } from './tools.js'
36
36
 
37
37
  /** Must equal `package.json`'s version; `npm run check` enforces that. */
38
- export const VERSION = '0.4.4'
38
+ export const VERSION = '0.4.6'
39
39
 
40
40
  /** Plugin id. Also the Loader row id in `cordis.patch.yml`. */
41
41
  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.4.4",
3
+ "version": "0.4.6",
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",