@dennisrongo/dsh-todo 0.3.0 → 0.4.0

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 (3) hide show
  1. package/README.md +5 -0
  2. package/lib/client.js +44 -36
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # @dennisrongo/dsh-todo
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@dennisrongo/dsh-todo)](https://www.npmjs.com/package/@dennisrongo/dsh-todo)
4
+
5
+ **npm:** [`@dennisrongo/dsh-todo`](https://www.npmjs.com/package/@dennisrongo/dsh-todo) ·
6
+ **source:** [dennisrongo/dsh-plugins](https://github.com/dennisrongo/dsh-plugins/tree/main/plugins/dsh-todo)
7
+
3
8
  A todo list for the [DeepSeek Harness](https://github.com/deepseek-ai) (dsh) web UI.
4
9
 
5
10
  Registers into the additive `conversation.view` slot — the conversation view
package/lib/client.js CHANGED
@@ -92,17 +92,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
92
92
  else: left unset it renders as a LIGHT popup while the option text inherits
93
93
  the shell near-white label colour, i.e. white-on-white and unreadable.
94
94
 
95
- Keyed off the same prefers-color-scheme query the shell theme service listens
96
- to (dsh-client-ui-theme holds a matchMedia for it), because the shell
97
- publishes its theme as CSS VARIABLES and never sets a color-scheme or a
98
- data-theme attribute to inherit from. Scoping to the query rather than
99
- hardcoding dark keeps a light theme light.
100
-
101
- Repeated for the modal: that panel portals to document.body, so it is not a
102
- descendant of .dshtd and inherits none of this. */
103
- @media (prefers-color-scheme: dark) {
104
- .dshtd, .dshtd-modal, .dshtd-modal-backdrop { color-scheme: dark; }
105
- }
95
+ This used to key off the prefers-color-scheme query, on the premise that the
96
+ shell "publishes its theme as CSS VARIABLES and never sets a color-scheme".
97
+ That premise was wrong: ui-layout's ThemePresenter sets
98
+ documentElement.style.colorScheme from the resolved theme on every change,
99
+ and color-scheme inherits \u2014 so native controls already follow the APP's
100
+ light/dark, including in the modal, which portals to document.body but is
101
+ still a descendant of <html>. Keying off the OS instead meant a light theme
102
+ on a dark-mode machine rendered dark popups. Nothing is needed here now.
106
103
 
107
104
  /* ---- header ---- */
108
105
  .dshtd-head {
@@ -143,13 +140,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
143
140
  padding: 8px 12px;
144
141
  }
145
142
  .dshtd-add::placeholder { color: var(--td-caption); }
146
- .dshtd-add:focus { outline: none; border-color: var(--dsw-alias-border-focus, #6b7280); }
143
+ .dshtd-add:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #6b7280); }
147
144
  .dshtd-addbtn {
148
145
  flex: none; border: 1px solid var(--td-border); border-radius: 8px;
149
146
  background: var(--td-hover); color: var(--td-primary); font: inherit; font-weight: 500;
150
147
  padding: 8px 16px; cursor: pointer;
151
148
  }
152
- .dshtd-addbtn:hover:not(:disabled) { border-color: var(--dsw-alias-border-focus, #6b7280); }
149
+ .dshtd-addbtn:hover:not(:disabled) { border-color: var(--dsw-alias-brand-primary, #6b7280); }
153
150
  .dshtd-addbtn:disabled { opacity: 0.4; cursor: default; }
154
151
 
155
152
  /* ---- list ---- */
@@ -201,7 +198,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
201
198
  }
202
199
  .dshtd-edit {
203
200
  flex: 1 1 auto; min-width: 0;
204
- border: 1px solid var(--dsw-alias-border-focus, #6b7280); border-radius: 6px;
201
+ border: 1px solid var(--dsw-alias-brand-primary, #6b7280); border-radius: 6px;
205
202
  background: transparent; color: var(--td-primary); font: inherit; padding: 3px 8px;
206
203
  }
207
204
  .dshtd-edit:focus { outline: none; }
@@ -234,8 +231,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
234
231
  /* Priority reads as a rank, so only the urgent bands get colour \u2014 colouring all
235
232
  four would make the list a rainbow and hide the two that matter. */
236
233
  .dshtd-chip.p0 { color: var(--td-danger); border-color: currentColor; font-weight: 500; }
237
- .dshtd-chip.p1 { color: var(--dsw-alias-state-warning-primary, #f59e0b); border-color: currentColor; }
238
- .dshtd-chip.rel { color: var(--dsw-alias-state-info-primary, #60a5fa); border-color: currentColor; }
234
+ .dshtd-chip.p1 { color: var(--dsw-alias-state-warn-primary, #f59e0b); border-color: currentColor; }
235
+ .dshtd-chip.rel { color: var(--dsw-alias-state-business-primary, #60a5fa); border-color: currentColor; }
239
236
 
240
237
  /* Status pill doubles as the status control, so it is a real button. */
241
238
  .dshtd-status {
@@ -244,8 +241,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
244
241
  font: inherit; font-size: 12px; line-height: 20px; color: var(--td-caption);
245
242
  cursor: pointer; appearance: none;
246
243
  }
247
- .dshtd-status:hover { color: var(--td-primary); border-color: var(--dsw-alias-border-focus, #6b7280); }
248
- .dshtd-status.s-in-progress { color: var(--dsw-alias-state-info-primary, #60a5fa); border-color: currentColor; }
244
+ .dshtd-status:hover { color: var(--td-primary); border-color: var(--dsw-alias-brand-primary, #6b7280); }
245
+ .dshtd-status.s-in-progress { color: var(--dsw-alias-state-business-primary, #60a5fa); border-color: currentColor; }
249
246
  .dshtd-status.s-blocked { color: var(--td-danger); border-color: currentColor; }
250
247
  .dshtd-status.s-done { color: var(--td-accent); border-color: currentColor; }
251
248
 
@@ -273,7 +270,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
273
270
  border: 1px solid var(--td-border); border-radius: 6px; background: transparent;
274
271
  color: var(--td-secondary); font: inherit; font-size: 14px; line-height: 22px; padding: 6px 8px;
275
272
  }
276
- .dshtd-desc:focus { outline: none; border-color: var(--dsw-alias-border-focus, #6b7280); }
273
+ .dshtd-desc:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #6b7280); }
277
274
  .dshtd-fields { display: flex; flex-wrap: wrap; gap: 8px; }
278
275
  .dshtd-field { display: flex; align-items: center; gap: 6px; font-size: 12px; line-height: 18px; color: var(--td-caption); }
279
276
  .dshtd-input {
@@ -281,24 +278,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
281
278
  color: var(--td-primary); font: inherit; font-size: 12px; line-height: 18px;
282
279
  padding: 3px 8px; min-width: 0; width: 130px;
283
280
  }
284
- .dshtd-input:focus { outline: none; border-color: var(--dsw-alias-border-focus, #6b7280); }
281
+ .dshtd-input:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #6b7280); }
285
282
  .dshtd-select {
286
283
  border: 1px solid var(--td-border); border-radius: 6px; background: transparent;
287
284
  color: var(--td-primary); font: inherit; font-size: 12px; line-height: 18px; padding: 3px 6px;
288
285
  }
289
286
 
290
287
  /* Option rows get an explicit pair on the platforms that honour it (Windows and
291
- Linux Chromium paint them; macOS ignores it and defers to color-scheme).
288
+ Linux Chromium paint them; macOS ignores it and defers to color-scheme,
289
+ which ui-layout already sets on <html> from the resolved theme).
292
290
 
293
- Scoped to the dark query on purpose: applied unconditionally these same
294
- declarations paint a DARK popup under a light theme, which is the original
295
- bug with the polarity reversed. Verified in both media states. */
296
- @media (prefers-color-scheme: dark) {
297
- .dshtd-select option,
298
- .dshtd-status option {
299
- background: var(--dsw-alias-bg-layer-1, #1b1d21);
300
- color: var(--dsw-alias-label-primary, #f9fafb);
301
- }
291
+ Gated on the APP's palette, not the OS query it used to use: applied
292
+ unconditionally these declarations paint a dark popup under a light theme,
293
+ and keyed to the OS query they did exactly that whenever the theme and the
294
+ OS disagreed. body[data-ds-dark-theme] is what ThemePresenter
295
+ actually toggles, so this now follows the theme the user picked. */
296
+ body[data-ds-dark-theme] .dshtd-select option,
297
+ body[data-ds-dark-theme] .dshtd-status option {
298
+ background: var(--dsw-alias-bg-layer-1, #1b1d21);
299
+ color: var(--dsw-alias-label-primary, #f9fafb);
302
300
  }
303
301
 
304
302
  /* ---- task modal ----
@@ -345,14 +343,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
345
343
  padding: 4px 8px;
346
344
  }
347
345
  .dshtd-modal-title:hover { border-color: var(--td-border); }
348
- .dshtd-modal-title:focus { outline: none; border-color: var(--dsw-alias-border-focus, #6b7280); }
346
+ .dshtd-modal-title:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #6b7280); }
349
347
  .dshtd-modal-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: 14px 16px; display: flex; flex-direction: column; gap: 14px; }
350
348
  .dshtd-modal-desc {
351
349
  width: 100%; min-height: 160px; resize: vertical;
352
350
  border: 1px solid var(--td-border); border-radius: 8px; background: transparent;
353
351
  color: var(--td-secondary); font: inherit; font-size: 14px; line-height: 22px; padding: 8px 10px;
354
352
  }
355
- .dshtd-modal-desc:focus { outline: none; border-color: var(--dsw-alias-border-focus, #6b7280); }
353
+ .dshtd-modal-desc:focus { outline: none; border-color: var(--dsw-alias-brand-primary, #6b7280); }
356
354
  .dshtd-modal-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
357
355
  .dshtd-modal-label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; line-height: 18px; color: var(--td-caption); }
358
356
  .dshtd-modal-foot {
@@ -393,16 +391,26 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
393
391
  padding: 6px 14px; cursor: pointer;
394
392
  }
395
393
  .dshtd-btn:hover { background: var(--td-hover); }
396
- .dshtd-btn:focus-visible { outline: 2px solid var(--dsw-alias-border-focus, #6b7280); outline-offset: 1px; }
394
+ .dshtd-btn:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #6b7280); outline-offset: 1px; }
397
395
  /* The destructive action is the one that looks destructive; Cancel stays quiet
398
396
  so the safe choice is never the visually louder one. */
399
397
  .dshtd-btn.danger { border-color: var(--td-danger); color: var(--td-danger); }
400
- .dshtd-btn.danger:hover { background: var(--td-danger); color: var(--dsw-alias-label-on-accent, #fff); }
398
+ .dshtd-btn.danger:hover { background: var(--td-danger); color: var(--dsw-alias-label-primary-foreground, #fff); }
399
+ /* The shell's own filled primary, token for token, so a confirm action here
400
+ reads as the same control as one in Settings rather than a bespoke link.
401
+ Both tokens follow the active theme AND the accent axis. */
402
+ .dshtd-btn.primary {
403
+ background: var(--dsw-alias-button-primary-fill, #f9fafb);
404
+ border-color: transparent;
405
+ color: var(--dsw-alias-label-primary-foreground, #0f1115);
406
+ font-weight: 500;
407
+ }
408
+ .dshtd-btn.primary:hover { background: var(--dsw-alias-button-primary-hover, #e1e5ee); }
401
409
 
402
410
  /* Overdue is the one state worth colouring in the list: it is the thing a
403
411
  standup escalates. Due-today is warned, not alarmed. */
404
412
  .dshtd-chip.due-over { color: var(--td-danger); border-color: currentColor; font-weight: 500; }
405
- .dshtd-chip.due-today { color: var(--dsw-alias-state-warning-primary, #f59e0b); border-color: currentColor; }
413
+ .dshtd-chip.due-today { color: var(--dsw-alias-state-warn-primary, #f59e0b); border-color: currentColor; }
406
414
 
407
415
  /* ---- toolbar (group-by) ---- */
408
416
  .dshtd-tools { flex: none; display: flex; align-items: center; gap: 6px; padding: 0 20px 10px; }
@@ -431,7 +439,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
431
439
  @media (prefers-reduced-motion: reduce) {
432
440
  .dshtd-progress > i, .dshtd-rowbtns { transition: none; }
433
441
  }
434
- `,gp=!1;function Mg(){if(gp)return;gp=!0;let e=document.createElement("style");e.dataset.plugin="@dennisrongo/dsh-todo",e.textContent=Vg,document.head.appendChild(e)}function le({path:e}){return(0,m.jsx)("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",stroke:"currentColor",strokeWidth:"1.25",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:(0,m.jsx)("path",{d:e})})}var Q={check:"M3.5 8.5l3 3 6-6",restore:"M3 8a5 5 0 1 0 1.6-3.68M3 2.5v3h3",close:"M4 4l8 8M12 4l-8 8",up:"M8 12.5v-9M4.5 7L8 3.5 11.5 7",down:"M8 3.5v9M4.5 9L8 12.5 11.5 9",archive:"M2.5 5.5h11M3.5 5.5v7h9v-7M6.5 8.5h3",expand:"M6 4l4 4-4 4",collapse:"M4 6l4 4 4-4"};function Up({pending:e,onClose:r}){let i=E.default.useRef(null),o=E.default.useRef(null);E.default.useEffect(()=>{let a=document.activeElement;return o.current?.focus(),()=>a?.focus?.()},[]);let t=()=>{e.onConfirm(),r()};return(0,su.createPortal)((0,m.jsx)("div",{className:"dshtd-modal-backdrop",onClick:r,onKeyDown:a=>{if(a.stopPropagation(),a.key==="Escape"){a.preventDefault(),r();return}if(a.key!=="Tab")return;let c=i.current?.querySelectorAll("button");if(!c||c.length===0)return;let u=c[0],s=c[c.length-1];a.shiftKey&&document.activeElement===u?(a.preventDefault(),s.focus()):!a.shiftKey&&document.activeElement===s&&(a.preventDefault(),u.focus())},children:(0,m.jsxs)("div",{className:"dshtd-modal dshtd-confirm",role:"alertdialog","aria-modal":"true","aria-label":e.title,tabIndex:-1,ref:i,onClick:a=>a.stopPropagation(),children:[(0,m.jsxs)("div",{className:"dshtd-confirm-body",children:[(0,m.jsx)("span",{className:"dshtd-confirm-title",children:e.title}),(0,m.jsx)("span",{className:"dshtd-confirm-text",children:e.message}),e.subject?(0,m.jsx)("span",{className:"dshtd-confirm-subject",children:e.subject}):null]}),(0,m.jsxs)("div",{className:"dshtd-confirm-foot",children:[(0,m.jsx)("button",{className:"dshtd-btn",ref:o,onClick:r,children:"Cancel"}),(0,m.jsx)("button",{className:"dshtd-btn danger",onClick:t,children:e.confirmLabel})]})]})}),document.body)}function Op({item:e,store:r,onClose:i,knownReleases:o,knownSprints:t}){let[n,a]=E.default.useState(e.title),[c,u]=E.default.useState(e.description??""),s=E.default.useRef(null),l=E.default.useCallback(()=>{let g=vt(n),I=Vn(c);r.update(k=>{let P=k;return g&&g!==e.title&&(P=ee(P,e.id,{title:g})),I!==(e.description??"")&&(P=ee(P,e.id,{description:I})),P})},[n,c,e,r]),f=E.default.useCallback(()=>{l(),i()},[l,i]);E.default.useEffect(()=>{let g=document.activeElement;return s.current?.focus(),()=>g?.focus?.()},[]);let b=g=>{if(g.stopPropagation(),g.key==="Escape"){g.preventDefault(),f();return}if(g.key!=="Tab")return;let I=s.current?.querySelectorAll('button, input, textarea, select, [tabindex]:not([tabindex="-1"])');if(!I||I.length===0)return;let k=I[0],P=I[I.length-1];g.shiftKey&&document.activeElement===k?(g.preventDefault(),P.focus()):!g.shiftKey&&document.activeElement===P&&(g.preventDefault(),k.focus())},v=Kn(e);return(0,su.createPortal)((0,m.jsx)("div",{className:"dshtd-modal-backdrop",onClick:f,onKeyDown:b,children:(0,m.jsxs)("div",{className:"dshtd-modal",role:"dialog","aria-modal":"true","aria-label":`Task: ${e.title}`,tabIndex:-1,ref:s,onClick:g=>g.stopPropagation(),children:[(0,m.jsxs)("div",{className:"dshtd-modal-head",children:[(0,m.jsx)("input",{className:"dshtd-modal-title",value:n,"aria-label":"Task title",onChange:g=>a(g.target.value),onBlur:()=>{let g=vt(n);!g||g===e.title||r.update(I=>ee(I,e.id,{title:g}))}}),(0,m.jsx)("button",{className:"dshtd-icon",title:"Close","aria-label":"Close task details",onClick:f,children:(0,m.jsx)(le,{path:Q.close})})]}),(0,m.jsxs)("div",{className:"dshtd-modal-body",children:[(0,m.jsx)("textarea",{className:"dshtd-modal-desc",value:c,placeholder:"Description \u2014 acceptance criteria, repro steps, links\u2026","aria-label":"Task description",onChange:g=>u(g.target.value),onBlur:()=>{let g=Vn(c);g!==(e.description??"")&&r.update(I=>ee(I,e.id,{description:g}))}}),(0,m.jsxs)("div",{className:"dshtd-modal-grid",children:[(0,m.jsxs)("label",{className:"dshtd-modal-label",children:["Status",(0,m.jsx)("select",{className:"dshtd-select",value:e.status,onChange:g=>r.update(I=>Wn(I,e.id,g.target.value)),children:Ze.map(g=>(0,m.jsx)("option",{value:g,children:Bn[g]},g))})]}),(0,m.jsxs)("label",{className:"dshtd-modal-label",children:["Priority",(0,m.jsx)("select",{className:"dshtd-select",value:e.priority,onChange:g=>r.update(I=>uu(I,e.id,g.target.value)),children:gt.map(g=>(0,m.jsx)("option",{value:g,children:Qt[g]},g))})]}),(0,m.jsxs)("label",{className:"dshtd-modal-label",children:["Release",(0,m.jsx)("input",{className:"dshtd-input",list:"dshtd-releases",defaultValue:e.release??"",placeholder:"v1.2.0",onBlur:g=>r.update(I=>ee(I,e.id,{release:g.target.value}))})]}),(0,m.jsxs)("label",{className:"dshtd-modal-label",children:["Sprint",(0,m.jsx)("input",{className:"dshtd-input",list:"dshtd-sprints",defaultValue:e.sprint??"",placeholder:"Sprint 24",onBlur:g=>r.update(I=>ee(I,e.id,{sprint:g.target.value}))})]}),(0,m.jsxs)("label",{className:"dshtd-modal-label",children:["Due date",(0,m.jsx)("input",{className:"dshtd-input",type:"date",defaultValue:e.dueDate??"",onChange:g=>r.update(I=>ee(I,e.id,{dueDate:g.target.value}))})]})]}),(0,m.jsx)("datalist",{id:"dshtd-releases",children:o.map(g=>(0,m.jsx)("option",{value:g},g))}),(0,m.jsx)("datalist",{id:"dshtd-sprints",children:t.map(g=>(0,m.jsx)("option",{value:g},g))})]}),(0,m.jsxs)("div",{className:"dshtd-modal-foot",children:[(0,m.jsxs)("span",{children:["Created ",Ht(e.createdAt)," ago",e.completedAt?` \xB7 completed ${Ht(e.completedAt)} ago`:"",v?" \xB7 overdue":""]}),(0,m.jsx)("button",{className:"dshtd-link",onClick:f,children:"Done"})]})]})}),document.body)}function Kg({item:e,store:r}){return(0,m.jsx)("select",{className:`dshtd-status s-${e.status}`,value:e.status,"aria-label":`Status of "${e.title}"`,title:"Change status",onClick:i=>i.stopPropagation(),onChange:i=>r.update(o=>Wn(o,e.id,i.target.value)),children:Ze.map(i=>(0,m.jsx)("option",{value:i,children:Bn[i]},i))})}function Gg({item:e,index:r,total:i,store:o,knownReleases:t,knownSprints:n,onOpen:a,onDelete:c}){let[u,s]=E.default.useState(!1),[l,f]=E.default.useState(e.title),[b,v]=E.default.useState(!1),g=q(e),I=ie(e),k=()=>{let P=vt(l);s(!1),!(!P||P===e.title)&&o.update(Ee=>ee(Ee,e.id,{title:P}))};return g?(0,m.jsxs)("li",{className:"dshtd-row archived",children:[(0,m.jsx)("span",{className:"dshtd-badge","aria-hidden":"true",children:(0,m.jsx)(le,{path:Q.check})}),(0,m.jsx)("span",{className:"dshtd-text",children:e.title}),e.release?(0,m.jsx)("span",{className:"dshtd-chip rel",children:e.release}):null,(0,m.jsx)("span",{className:"dshtd-age",title:"When this was archived",children:Ht(e.archivedAt??0)}),(0,m.jsxs)("span",{className:"dshtd-rowbtns",children:[(0,m.jsx)("button",{className:"dshtd-icon",title:"Restore to the active list","aria-label":`Restore "${e.title}"`,onClick:()=>o.update(P=>_p(P,e.id)),children:(0,m.jsx)(le,{path:Q.restore})}),(0,m.jsx)("button",{className:"dshtd-icon danger",title:"Delete permanently","aria-label":`Permanently delete "${e.title}"`,onClick:c,children:(0,m.jsx)(le,{path:Q.close})})]})]}):(0,m.jsxs)("li",{className:"dshtd-item",children:[(0,m.jsxs)("div",{className:`dshtd-row${I?" done":""}`,children:[(0,m.jsx)("input",{type:"checkbox",className:"dshtd-check",checked:I,"aria-label":I?`Mark "${e.title}" as not done`:`Mark "${e.title}" as done`,onChange:()=>o.update(P=>xp(P,e.id))}),u?(0,m.jsx)("input",{className:"dshtd-edit",value:l,autoFocus:!0,"aria-label":"Edit task title",onChange:P=>f(P.target.value),onBlur:k,onKeyDown:P=>{P.stopPropagation(),P.key==="Enter"?(P.preventDefault(),k()):P.key==="Escape"&&(P.preventDefault(),f(e.title),s(!1))}}):(0,m.jsx)("span",{className:"dshtd-text",title:"Click to open \xB7 double-click to rename inline",onClick:a,onDoubleClick:P=>{P.stopPropagation(),f(e.title),s(!0)},children:e.title}),u?null:(0,m.jsxs)("span",{className:"dshtd-chips",children:[e.priority!==Fn?(0,m.jsx)("span",{className:`dshtd-chip ${e.priority}`,title:Qt[e.priority],children:e.priority.toUpperCase()}):null,e.release?(0,m.jsx)("span",{className:"dshtd-chip rel",title:`Release ${e.release}`,children:e.release}):null,e.sprint?(0,m.jsx)("span",{className:"dshtd-chip",title:`Sprint ${e.sprint}`,children:e.sprint}):null,e.dueDate?(0,m.jsx)("span",{className:`dshtd-chip${Kn(e)?" due-over":Pp(e)?" due-today":""}`,title:`Due ${e.dueDate}${Kn(e)?" \u2014 overdue":""}`,children:jp(e.dueDate)}):null,(0,m.jsx)(Kg,{item:e,store:o})]}),(0,m.jsxs)("span",{className:"dshtd-rowbtns",children:[(0,m.jsx)("button",{className:"dshtd-icon",title:b?"Hide details":"Show details","aria-label":`${b?"Hide":"Show"} details for "${e.title}"`,"aria-expanded":b,onClick:()=>v(P=>!P),children:(0,m.jsx)(le,{path:b?Q.collapse:Q.expand})}),(0,m.jsx)("button",{className:"dshtd-icon",title:"Move up","aria-label":`Move "${e.title}" up`,disabled:r===0,onClick:()=>o.update(P=>au(P,e.id,-1)),children:(0,m.jsx)(le,{path:Q.up})}),(0,m.jsx)("button",{className:"dshtd-icon",title:"Move down","aria-label":`Move "${e.title}" down`,disabled:r===i-1,onClick:()=>o.update(P=>au(P,e.id,1)),children:(0,m.jsx)(le,{path:Q.down})}),I?(0,m.jsx)("button",{className:"dshtd-icon",title:"Archive","aria-label":`Archive "${e.title}"`,onClick:()=>o.update(P=>yp(P,e.id)),children:(0,m.jsx)(le,{path:Q.archive})}):null,(0,m.jsx)("button",{className:"dshtd-icon danger",title:"Delete","aria-label":`Delete "${e.title}"`,onClick:c,children:(0,m.jsx)(le,{path:Q.close})})]})]}),b?(0,m.jsx)(Bg,{item:e,store:o,knownReleases:t,knownSprints:n}):null]})}function Bg({item:e,store:r,knownReleases:i,knownSprints:o}){let[t,n]=E.default.useState(e.description??""),[a,c]=E.default.useState(e.release??""),[u,s]=E.default.useState(e.sprint??"");return(0,m.jsxs)("div",{className:"dshtd-detail",children:[(0,m.jsx)("textarea",{className:"dshtd-desc",value:t,placeholder:"Description \u2014 acceptance criteria, repro steps, links\u2026","aria-label":`Description of "${e.title}"`,onChange:l=>n(l.target.value),onKeyDown:l=>l.stopPropagation(),onBlur:()=>{let l=Vn(t);l!==(e.description??"")&&r.update(f=>ee(f,e.id,{description:l}))}}),(0,m.jsxs)("div",{className:"dshtd-fields",children:[(0,m.jsxs)("label",{className:"dshtd-field",children:["Release",(0,m.jsx)("input",{className:"dshtd-input",list:"dshtd-releases",value:a,placeholder:"v1.2.0",onChange:l=>c(l.target.value),onKeyDown:l=>l.stopPropagation(),onBlur:()=>{(ze(a)??"")!==(e.release??"")&&r.update(l=>ee(l,e.id,{release:a}))}})]}),(0,m.jsxs)("label",{className:"dshtd-field",children:["Sprint",(0,m.jsx)("input",{className:"dshtd-input",list:"dshtd-sprints",value:u,placeholder:"Sprint 24",onChange:l=>s(l.target.value),onKeyDown:l=>l.stopPropagation(),onBlur:()=>{(ze(u)??"")!==(e.sprint??"")&&r.update(l=>ee(l,e.id,{sprint:u}))}})]}),(0,m.jsxs)("label",{className:"dshtd-field",children:["Priority",(0,m.jsx)("select",{className:"dshtd-select",value:e.priority,onChange:l=>r.update(f=>uu(f,e.id,l.target.value)),children:gt.map(l=>(0,m.jsx)("option",{value:l,children:Qt[l]},l))})]}),(0,m.jsxs)("span",{className:"dshtd-field",children:["Created ",Ht(e.createdAt)," ago"]})]}),(0,m.jsx)("datalist",{id:"dshtd-releases",children:i.map(l=>(0,m.jsx)("option",{value:l},l))}),(0,m.jsx)("datalist",{id:"dshtd-sprints",children:o.map(l=>(0,m.jsx)("option",{value:l},l))})]})}var Wg=[{id:"all",label:"All"},{id:"open",label:"Open"},{id:"in-progress",label:"In Progress"},{id:"blocked",label:"Blocked"},{id:"backlog",label:"Backlog"},{id:"done",label:"Done"},{id:"archived",label:"Archive"}],Xg=[{id:"none",label:"None"},{id:"status",label:"Status"},{id:"release",label:"Release"},{id:"sprint",label:"Sprint"},{id:"priority",label:"Priority"}];function qg(e,r,i){return r==="all"?i.total:r==="open"?i.open:r==="archived"?i.archived:ht(e).filter(o=>o.status===r).length}function Np({store:e}){let[r,i]=E.default.useState("all"),[o,t]=E.default.useState("none"),[n,a]=E.default.useState(""),[c,u]=E.default.useState({}),[s,l]=E.default.useState(null),[f,b]=E.default.useState(null);E.default.useEffect(()=>{Mg()},[]),E.default.useEffect(()=>{e&&e.ensure()},[e]);let v=E.default.useSyncExternalStore(e?e.subscribe:Hg,e?e.getSnapshot:vp,e?e.getSnapshot:vp);if(!e)return(0,m.jsx)("div",{className:"dshtd",children:(0,m.jsxs)("div",{className:"dshtd-empty",children:[(0,m.jsx)("b",{children:"No workspace yet"}),"Todos are stored per workspace. Open or create one to start a list."]})});let g=v.items,I=()=>{let w=vt(n);w&&(e.update(te=>[...te,bp(w)]),a(""),r!=="all"&&r!=="open"&&r!=="todo"&&i("all"))},k=$p(g),P=ht(g),Ee=Ip(g,r),Y=wp(Ee,o),Z=cu(g,"release"),R=cu(g,"sprint"),$t=s===null?void 0:g.find(w=>w.id===s),er=w=>{let te=q(w);b({title:te?"Delete permanently":"Delete task",message:te?"This removes the task from the archive for good. This cannot be undone.":"This deletes the task outright. Archiving keeps it recoverable instead.",subject:w.title,confirmLabel:"Delete",onConfirm:()=>e.update(qn=>qn.filter(Ae=>Ae.id!==w.id))})};return(0,m.jsxs)("div",{className:"dshtd",children:[(0,m.jsxs)("div",{className:"dshtd-head",children:[(0,m.jsx)("span",{className:"dshtd-title",children:"Todo"}),(0,m.jsx)("span",{className:"dshtd-progress",role:"progressbar","aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":k.percent,"aria-label":"Todo completion",children:(0,m.jsx)("i",{style:{width:`${k.percent}%`}})}),(0,m.jsxs)("span",{className:"dshtd-score",children:[k.done,"/",k.total," done",k.inProgress>0?` \xB7 ${k.inProgress} in progress`:"",k.blocked>0?` \xB7 ${k.blocked} blocked`:""]})]}),(0,m.jsx)("div",{className:"dshtd-filters",role:"group","aria-label":"Filter todos",children:Wg.map(w=>{let te=qg(g,w.id,k);return te===0&&w.id!=="all"&&w.id!=="open"?null:(0,m.jsxs)("button",{className:"dshtd-filter","aria-pressed":r===w.id,onClick:()=>i(w.id),children:[w.label,w.id!=="all"&&te>0?` (${te})`:null]},w.id)})}),(0,m.jsxs)("div",{className:"dshtd-tools",children:[(0,m.jsx)("label",{htmlFor:"dshtd-groupby",children:"Group by"}),(0,m.jsx)("select",{id:"dshtd-groupby",className:"dshtd-select",value:o,onChange:w=>t(w.target.value),children:Xg.map(w=>(0,m.jsx)("option",{value:w.id,children:w.label},w.id))})]}),(0,m.jsxs)("div",{className:"dshtd-addrow",children:[(0,m.jsx)("input",{className:"dshtd-add",value:n,placeholder:"What needs doing?","aria-label":"Add a todo",disabled:v.status!=="ready",onChange:w=>a(w.target.value),onKeyDown:w=>{w.stopPropagation(),w.key==="Enter"?(w.preventDefault(),I()):w.key==="Escape"&&(w.preventDefault(),a(""))}}),(0,m.jsx)("button",{className:"dshtd-addbtn",disabled:!vt(n)||v.status!=="ready",onClick:I,children:"Add"})]}),(0,m.jsx)("div",{className:"dshtd-scroll",children:v.status==="loading"?(0,m.jsx)("div",{className:"dshtd-empty",children:"Loading\u2026"}):v.status==="error"?(0,m.jsxs)("div",{className:"dshtd-empty",children:[(0,m.jsx)("b",{children:"Couldn't load todos"}),v.error]}):Ee.length===0?(0,m.jsx)("div",{className:"dshtd-empty",children:r==="archived"?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)("b",{children:"Nothing archived yet"}),"Check items off, then use \u201CArchive completed\u201D to file them away. Archived items stay here until you delete them."]}):k.total===0?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)("b",{children:"No tasks yet"}),"Add your first one above \u2014 this list belongs to the current workspace."]}):r==="open"?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)("b",{children:"All done \u{1F389}"}),"Nothing open right now."]}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)("b",{children:"Nothing here"}),"No tasks match this filter."]})}):Y.map(w=>{let te=w.items.filter(ie).length,qn=w.items.length===0?0:Math.round(te/w.items.length*100),Ae=c[w.key]===!0;return(0,m.jsxs)("section",{className:"dshtd-group",children:[o!=="none"?(0,m.jsxs)("button",{className:"dshtd-ghead","aria-expanded":!Ae,onClick:()=>u(Ie=>({...Ie,[w.key]:!Ae})),children:[(0,m.jsx)(le,{path:Ae?Q.expand:Q.collapse}),w.label,(0,m.jsxs)("span",{className:"dshtd-gcount",children:[te,"/",w.items.length]}),(0,m.jsx)("span",{className:"dshtd-gbar","aria-hidden":"true",children:(0,m.jsx)("i",{style:{width:`${qn}%`}})})]}):null,Ae?null:(0,m.jsx)("ul",{className:"dshtd-list",children:w.items.map(Ie=>(0,m.jsx)(Gg,{item:Ie,index:P.indexOf(Ie),total:P.length,store:e,knownReleases:Z,knownSprints:R,onOpen:()=>l(Ie.id),onDelete:()=>er(Ie)},Ie.id))})]},w.key)})}),(0,m.jsxs)("div",{className:"dshtd-foot",children:[(0,m.jsxs)("span",{children:[k.open," open \xB7 ",k.done," done",k.archived>0?` \xB7 ${k.archived} archived`:null,v.saving?(0,m.jsx)("span",{className:"dshtd-state",children:" \xB7 saving\u2026"}):null,v.error&&v.status==="ready"?(0,m.jsxs)("span",{className:"dshtd-state err",children:[" \xB7 ",v.error]}):null]}),r==="archived"?k.archived>0?(0,m.jsx)("button",{className:"dshtd-link danger",title:"Permanently delete every archived item",onClick:()=>b({title:"Delete archived tasks",message:`Permanently delete ${k.archived} archived ${k.archived===1?"task":"tasks"}? This cannot be undone.`,confirmLabel:"Delete all",onConfirm:()=>e.update(zp)}),children:"Delete archived"}):null:k.done>0?(0,m.jsx)("button",{className:"dshtd-link",title:"Move completed items to the archive \u2014 they stay recoverable",onClick:()=>e.update(kp),children:"Archive completed"}):null]}),$t?(0,m.jsx)(Op,{item:$t,store:e,onClose:()=>l(null),knownReleases:Z,knownSprints:R}):null,f?(0,m.jsx)(Up,{pending:f,onClose:()=>b(null)}):null]})}var Yg={items:[],status:"ready",error:null,saving:!1},Hg=()=>()=>{},vp=()=>Yg;function Qg(e){let r=new Map,i=e;e.effect(()=>{let o=!1,t;return i.remote.$mount(tu).then(n=>{if(o)return void n();t=n}).catch(n=>{console.error("dsh-todo: failed to mount host remote",n)}),()=>{o=!0,r.clear(),t?.()}},"dsh-todo: mount host remote"),e.effect(()=>{let o=i.inject(["remote.dshTodo","workspaces","slots"],t=>{let n=t;n.slots.inject("conversation.view",()=>n.slots.register({name:"conversation.view",id:"todo",order:20,label:()=>"Todo",inject:a=>{let c=n.workspaces.list.getSnapshot().items,u=Tp(c,a);if(u===void 0)return{store:null};let s=r.get(u);return s===void 0&&(s=new Gn(n.remote.dshTodo,u),r.set(u,s)),{store:s}}},Np))});return()=>{o.dispose()}},"dsh-todo: conversation.view registration")}
442
+ `,gp=!1;function Mg(){if(gp)return;gp=!0;let e=document.createElement("style");e.dataset.plugin="@dennisrongo/dsh-todo",e.textContent=Vg,document.head.appendChild(e)}function le({path:e}){return(0,m.jsx)("svg",{width:"16",height:"16",viewBox:"0 0 16 16",fill:"none",stroke:"currentColor",strokeWidth:"1.25",strokeLinecap:"round",strokeLinejoin:"round","aria-hidden":"true",children:(0,m.jsx)("path",{d:e})})}var Q={check:"M3.5 8.5l3 3 6-6",restore:"M3 8a5 5 0 1 0 1.6-3.68M3 2.5v3h3",close:"M4 4l8 8M12 4l-8 8",up:"M8 12.5v-9M4.5 7L8 3.5 11.5 7",down:"M8 3.5v9M4.5 9L8 12.5 11.5 9",archive:"M2.5 5.5h11M3.5 5.5v7h9v-7M6.5 8.5h3",expand:"M6 4l4 4-4 4",collapse:"M4 6l4 4 4-4"};function Up({pending:e,onClose:r}){let i=E.default.useRef(null),o=E.default.useRef(null);E.default.useEffect(()=>{let a=document.activeElement;return o.current?.focus(),()=>a?.focus?.()},[]);let t=()=>{e.onConfirm(),r()};return(0,su.createPortal)((0,m.jsx)("div",{className:"dshtd-modal-backdrop",onClick:r,onKeyDown:a=>{if(a.stopPropagation(),a.key==="Escape"){a.preventDefault(),r();return}if(a.key!=="Tab")return;let c=i.current?.querySelectorAll("button");if(!c||c.length===0)return;let u=c[0],s=c[c.length-1];a.shiftKey&&document.activeElement===u?(a.preventDefault(),s.focus()):!a.shiftKey&&document.activeElement===s&&(a.preventDefault(),u.focus())},children:(0,m.jsxs)("div",{className:"dshtd-modal dshtd-confirm",role:"alertdialog","aria-modal":"true","aria-label":e.title,tabIndex:-1,ref:i,onClick:a=>a.stopPropagation(),children:[(0,m.jsxs)("div",{className:"dshtd-confirm-body",children:[(0,m.jsx)("span",{className:"dshtd-confirm-title",children:e.title}),(0,m.jsx)("span",{className:"dshtd-confirm-text",children:e.message}),e.subject?(0,m.jsx)("span",{className:"dshtd-confirm-subject",children:e.subject}):null]}),(0,m.jsxs)("div",{className:"dshtd-confirm-foot",children:[(0,m.jsx)("button",{className:"dshtd-btn",ref:o,onClick:r,children:"Cancel"}),(0,m.jsx)("button",{className:"dshtd-btn danger",onClick:t,children:e.confirmLabel})]})]})}),document.body)}function Op({item:e,store:r,onClose:i,knownReleases:o,knownSprints:t}){let[n,a]=E.default.useState(e.title),[c,u]=E.default.useState(e.description??""),s=E.default.useRef(null),l=E.default.useCallback(()=>{let g=vt(n),I=Vn(c);r.update(k=>{let P=k;return g&&g!==e.title&&(P=ee(P,e.id,{title:g})),I!==(e.description??"")&&(P=ee(P,e.id,{description:I})),P})},[n,c,e,r]),f=E.default.useCallback(()=>{l(),i()},[l,i]);E.default.useEffect(()=>{let g=document.activeElement;return s.current?.focus(),()=>g?.focus?.()},[]);let b=g=>{if(g.stopPropagation(),g.key==="Escape"){g.preventDefault(),f();return}if(g.key!=="Tab")return;let I=s.current?.querySelectorAll('button, input, textarea, select, [tabindex]:not([tabindex="-1"])');if(!I||I.length===0)return;let k=I[0],P=I[I.length-1];g.shiftKey&&document.activeElement===k?(g.preventDefault(),P.focus()):!g.shiftKey&&document.activeElement===P&&(g.preventDefault(),k.focus())},v=Kn(e);return(0,su.createPortal)((0,m.jsx)("div",{className:"dshtd-modal-backdrop",onClick:f,onKeyDown:b,children:(0,m.jsxs)("div",{className:"dshtd-modal",role:"dialog","aria-modal":"true","aria-label":`Task: ${e.title}`,tabIndex:-1,ref:s,onClick:g=>g.stopPropagation(),children:[(0,m.jsxs)("div",{className:"dshtd-modal-head",children:[(0,m.jsx)("input",{className:"dshtd-modal-title",value:n,"aria-label":"Task title",onChange:g=>a(g.target.value),onBlur:()=>{let g=vt(n);!g||g===e.title||r.update(I=>ee(I,e.id,{title:g}))}}),(0,m.jsx)("button",{className:"dshtd-icon",title:"Close","aria-label":"Close task details",onClick:f,children:(0,m.jsx)(le,{path:Q.close})})]}),(0,m.jsxs)("div",{className:"dshtd-modal-body",children:[(0,m.jsx)("textarea",{className:"dshtd-modal-desc",value:c,placeholder:"Description \u2014 acceptance criteria, repro steps, links\u2026","aria-label":"Task description",onChange:g=>u(g.target.value),onBlur:()=>{let g=Vn(c);g!==(e.description??"")&&r.update(I=>ee(I,e.id,{description:g}))}}),(0,m.jsxs)("div",{className:"dshtd-modal-grid",children:[(0,m.jsxs)("label",{className:"dshtd-modal-label",children:["Status",(0,m.jsx)("select",{className:"dshtd-select",value:e.status,onChange:g=>r.update(I=>Wn(I,e.id,g.target.value)),children:Ze.map(g=>(0,m.jsx)("option",{value:g,children:Bn[g]},g))})]}),(0,m.jsxs)("label",{className:"dshtd-modal-label",children:["Priority",(0,m.jsx)("select",{className:"dshtd-select",value:e.priority,onChange:g=>r.update(I=>uu(I,e.id,g.target.value)),children:gt.map(g=>(0,m.jsx)("option",{value:g,children:Qt[g]},g))})]}),(0,m.jsxs)("label",{className:"dshtd-modal-label",children:["Release",(0,m.jsx)("input",{className:"dshtd-input",list:"dshtd-releases",defaultValue:e.release??"",placeholder:"v1.2.0",onBlur:g=>r.update(I=>ee(I,e.id,{release:g.target.value}))})]}),(0,m.jsxs)("label",{className:"dshtd-modal-label",children:["Sprint",(0,m.jsx)("input",{className:"dshtd-input",list:"dshtd-sprints",defaultValue:e.sprint??"",placeholder:"Sprint 24",onBlur:g=>r.update(I=>ee(I,e.id,{sprint:g.target.value}))})]}),(0,m.jsxs)("label",{className:"dshtd-modal-label",children:["Due date",(0,m.jsx)("input",{className:"dshtd-input",type:"date",defaultValue:e.dueDate??"",onChange:g=>r.update(I=>ee(I,e.id,{dueDate:g.target.value}))})]})]}),(0,m.jsx)("datalist",{id:"dshtd-releases",children:o.map(g=>(0,m.jsx)("option",{value:g},g))}),(0,m.jsx)("datalist",{id:"dshtd-sprints",children:t.map(g=>(0,m.jsx)("option",{value:g},g))})]}),(0,m.jsxs)("div",{className:"dshtd-modal-foot",children:[(0,m.jsxs)("span",{children:["Created ",Ht(e.createdAt)," ago",e.completedAt?` \xB7 completed ${Ht(e.completedAt)} ago`:"",v?" \xB7 overdue":""]}),(0,m.jsx)("button",{className:"dshtd-btn primary",onClick:f,children:"Done"})]})]})}),document.body)}function Kg({item:e,store:r}){return(0,m.jsx)("select",{className:`dshtd-status s-${e.status}`,value:e.status,"aria-label":`Status of "${e.title}"`,title:"Change status",onClick:i=>i.stopPropagation(),onChange:i=>r.update(o=>Wn(o,e.id,i.target.value)),children:Ze.map(i=>(0,m.jsx)("option",{value:i,children:Bn[i]},i))})}function Gg({item:e,index:r,total:i,store:o,knownReleases:t,knownSprints:n,onOpen:a,onDelete:c}){let[u,s]=E.default.useState(!1),[l,f]=E.default.useState(e.title),[b,v]=E.default.useState(!1),g=q(e),I=ie(e),k=()=>{let P=vt(l);s(!1),!(!P||P===e.title)&&o.update(Ee=>ee(Ee,e.id,{title:P}))};return g?(0,m.jsxs)("li",{className:"dshtd-row archived",children:[(0,m.jsx)("span",{className:"dshtd-badge","aria-hidden":"true",children:(0,m.jsx)(le,{path:Q.check})}),(0,m.jsx)("span",{className:"dshtd-text",children:e.title}),e.release?(0,m.jsx)("span",{className:"dshtd-chip rel",children:e.release}):null,(0,m.jsx)("span",{className:"dshtd-age",title:"When this was archived",children:Ht(e.archivedAt??0)}),(0,m.jsxs)("span",{className:"dshtd-rowbtns",children:[(0,m.jsx)("button",{className:"dshtd-icon",title:"Restore to the active list","aria-label":`Restore "${e.title}"`,onClick:()=>o.update(P=>_p(P,e.id)),children:(0,m.jsx)(le,{path:Q.restore})}),(0,m.jsx)("button",{className:"dshtd-icon danger",title:"Delete permanently","aria-label":`Permanently delete "${e.title}"`,onClick:c,children:(0,m.jsx)(le,{path:Q.close})})]})]}):(0,m.jsxs)("li",{className:"dshtd-item",children:[(0,m.jsxs)("div",{className:`dshtd-row${I?" done":""}`,children:[(0,m.jsx)("input",{type:"checkbox",className:"dshtd-check",checked:I,"aria-label":I?`Mark "${e.title}" as not done`:`Mark "${e.title}" as done`,onChange:()=>o.update(P=>xp(P,e.id))}),u?(0,m.jsx)("input",{className:"dshtd-edit",value:l,autoFocus:!0,"aria-label":"Edit task title",onChange:P=>f(P.target.value),onBlur:k,onKeyDown:P=>{P.stopPropagation(),P.key==="Enter"?(P.preventDefault(),k()):P.key==="Escape"&&(P.preventDefault(),f(e.title),s(!1))}}):(0,m.jsx)("span",{className:"dshtd-text",title:"Click to open \xB7 double-click to rename inline",onClick:a,onDoubleClick:P=>{P.stopPropagation(),f(e.title),s(!0)},children:e.title}),u?null:(0,m.jsxs)("span",{className:"dshtd-chips",children:[e.priority!==Fn?(0,m.jsx)("span",{className:`dshtd-chip ${e.priority}`,title:Qt[e.priority],children:e.priority.toUpperCase()}):null,e.release?(0,m.jsx)("span",{className:"dshtd-chip rel",title:`Release ${e.release}`,children:e.release}):null,e.sprint?(0,m.jsx)("span",{className:"dshtd-chip",title:`Sprint ${e.sprint}`,children:e.sprint}):null,e.dueDate?(0,m.jsx)("span",{className:`dshtd-chip${Kn(e)?" due-over":Pp(e)?" due-today":""}`,title:`Due ${e.dueDate}${Kn(e)?" \u2014 overdue":""}`,children:jp(e.dueDate)}):null,(0,m.jsx)(Kg,{item:e,store:o})]}),(0,m.jsxs)("span",{className:"dshtd-rowbtns",children:[(0,m.jsx)("button",{className:"dshtd-icon",title:b?"Hide details":"Show details","aria-label":`${b?"Hide":"Show"} details for "${e.title}"`,"aria-expanded":b,onClick:()=>v(P=>!P),children:(0,m.jsx)(le,{path:b?Q.collapse:Q.expand})}),(0,m.jsx)("button",{className:"dshtd-icon",title:"Move up","aria-label":`Move "${e.title}" up`,disabled:r===0,onClick:()=>o.update(P=>au(P,e.id,-1)),children:(0,m.jsx)(le,{path:Q.up})}),(0,m.jsx)("button",{className:"dshtd-icon",title:"Move down","aria-label":`Move "${e.title}" down`,disabled:r===i-1,onClick:()=>o.update(P=>au(P,e.id,1)),children:(0,m.jsx)(le,{path:Q.down})}),I?(0,m.jsx)("button",{className:"dshtd-icon",title:"Archive","aria-label":`Archive "${e.title}"`,onClick:()=>o.update(P=>yp(P,e.id)),children:(0,m.jsx)(le,{path:Q.archive})}):null,(0,m.jsx)("button",{className:"dshtd-icon danger",title:"Delete","aria-label":`Delete "${e.title}"`,onClick:c,children:(0,m.jsx)(le,{path:Q.close})})]})]}),b?(0,m.jsx)(Bg,{item:e,store:o,knownReleases:t,knownSprints:n}):null]})}function Bg({item:e,store:r,knownReleases:i,knownSprints:o}){let[t,n]=E.default.useState(e.description??""),[a,c]=E.default.useState(e.release??""),[u,s]=E.default.useState(e.sprint??"");return(0,m.jsxs)("div",{className:"dshtd-detail",children:[(0,m.jsx)("textarea",{className:"dshtd-desc",value:t,placeholder:"Description \u2014 acceptance criteria, repro steps, links\u2026","aria-label":`Description of "${e.title}"`,onChange:l=>n(l.target.value),onKeyDown:l=>l.stopPropagation(),onBlur:()=>{let l=Vn(t);l!==(e.description??"")&&r.update(f=>ee(f,e.id,{description:l}))}}),(0,m.jsxs)("div",{className:"dshtd-fields",children:[(0,m.jsxs)("label",{className:"dshtd-field",children:["Release",(0,m.jsx)("input",{className:"dshtd-input",list:"dshtd-releases",value:a,placeholder:"v1.2.0",onChange:l=>c(l.target.value),onKeyDown:l=>l.stopPropagation(),onBlur:()=>{(ze(a)??"")!==(e.release??"")&&r.update(l=>ee(l,e.id,{release:a}))}})]}),(0,m.jsxs)("label",{className:"dshtd-field",children:["Sprint",(0,m.jsx)("input",{className:"dshtd-input",list:"dshtd-sprints",value:u,placeholder:"Sprint 24",onChange:l=>s(l.target.value),onKeyDown:l=>l.stopPropagation(),onBlur:()=>{(ze(u)??"")!==(e.sprint??"")&&r.update(l=>ee(l,e.id,{sprint:u}))}})]}),(0,m.jsxs)("label",{className:"dshtd-field",children:["Priority",(0,m.jsx)("select",{className:"dshtd-select",value:e.priority,onChange:l=>r.update(f=>uu(f,e.id,l.target.value)),children:gt.map(l=>(0,m.jsx)("option",{value:l,children:Qt[l]},l))})]}),(0,m.jsxs)("span",{className:"dshtd-field",children:["Created ",Ht(e.createdAt)," ago"]})]}),(0,m.jsx)("datalist",{id:"dshtd-releases",children:i.map(l=>(0,m.jsx)("option",{value:l},l))}),(0,m.jsx)("datalist",{id:"dshtd-sprints",children:o.map(l=>(0,m.jsx)("option",{value:l},l))})]})}var Wg=[{id:"all",label:"All"},{id:"open",label:"Open"},{id:"in-progress",label:"In Progress"},{id:"blocked",label:"Blocked"},{id:"backlog",label:"Backlog"},{id:"done",label:"Done"},{id:"archived",label:"Archive"}],Xg=[{id:"none",label:"None"},{id:"status",label:"Status"},{id:"release",label:"Release"},{id:"sprint",label:"Sprint"},{id:"priority",label:"Priority"}];function qg(e,r,i){return r==="all"?i.total:r==="open"?i.open:r==="archived"?i.archived:ht(e).filter(o=>o.status===r).length}function Np({store:e}){let[r,i]=E.default.useState("all"),[o,t]=E.default.useState("none"),[n,a]=E.default.useState(""),[c,u]=E.default.useState({}),[s,l]=E.default.useState(null),[f,b]=E.default.useState(null);E.default.useEffect(()=>{Mg()},[]),E.default.useEffect(()=>{e&&e.ensure()},[e]);let v=E.default.useSyncExternalStore(e?e.subscribe:Hg,e?e.getSnapshot:vp,e?e.getSnapshot:vp);if(!e)return(0,m.jsx)("div",{className:"dshtd",children:(0,m.jsxs)("div",{className:"dshtd-empty",children:[(0,m.jsx)("b",{children:"No workspace yet"}),"Todos are stored per workspace. Open or create one to start a list."]})});let g=v.items,I=()=>{let w=vt(n);w&&(e.update(te=>[...te,bp(w)]),a(""),r!=="all"&&r!=="open"&&r!=="todo"&&i("all"))},k=$p(g),P=ht(g),Ee=Ip(g,r),Y=wp(Ee,o),Z=cu(g,"release"),R=cu(g,"sprint"),$t=s===null?void 0:g.find(w=>w.id===s),er=w=>{let te=q(w);b({title:te?"Delete permanently":"Delete task",message:te?"This removes the task from the archive for good. This cannot be undone.":"This deletes the task outright. Archiving keeps it recoverable instead.",subject:w.title,confirmLabel:"Delete",onConfirm:()=>e.update(qn=>qn.filter(Ae=>Ae.id!==w.id))})};return(0,m.jsxs)("div",{className:"dshtd",children:[(0,m.jsxs)("div",{className:"dshtd-head",children:[(0,m.jsx)("span",{className:"dshtd-title",children:"Todo"}),(0,m.jsx)("span",{className:"dshtd-progress",role:"progressbar","aria-valuemin":0,"aria-valuemax":100,"aria-valuenow":k.percent,"aria-label":"Todo completion",children:(0,m.jsx)("i",{style:{width:`${k.percent}%`}})}),(0,m.jsxs)("span",{className:"dshtd-score",children:[k.done,"/",k.total," done",k.inProgress>0?` \xB7 ${k.inProgress} in progress`:"",k.blocked>0?` \xB7 ${k.blocked} blocked`:""]})]}),(0,m.jsx)("div",{className:"dshtd-filters",role:"group","aria-label":"Filter todos",children:Wg.map(w=>{let te=qg(g,w.id,k);return te===0&&w.id!=="all"&&w.id!=="open"?null:(0,m.jsxs)("button",{className:"dshtd-filter","aria-pressed":r===w.id,onClick:()=>i(w.id),children:[w.label,w.id!=="all"&&te>0?` (${te})`:null]},w.id)})}),(0,m.jsxs)("div",{className:"dshtd-tools",children:[(0,m.jsx)("label",{htmlFor:"dshtd-groupby",children:"Group by"}),(0,m.jsx)("select",{id:"dshtd-groupby",className:"dshtd-select",value:o,onChange:w=>t(w.target.value),children:Xg.map(w=>(0,m.jsx)("option",{value:w.id,children:w.label},w.id))})]}),(0,m.jsxs)("div",{className:"dshtd-addrow",children:[(0,m.jsx)("input",{className:"dshtd-add",value:n,placeholder:"What needs doing?","aria-label":"Add a todo",disabled:v.status!=="ready",onChange:w=>a(w.target.value),onKeyDown:w=>{w.stopPropagation(),w.key==="Enter"?(w.preventDefault(),I()):w.key==="Escape"&&(w.preventDefault(),a(""))}}),(0,m.jsx)("button",{className:"dshtd-addbtn",disabled:!vt(n)||v.status!=="ready",onClick:I,children:"Add"})]}),(0,m.jsx)("div",{className:"dshtd-scroll",children:v.status==="loading"?(0,m.jsx)("div",{className:"dshtd-empty",children:"Loading\u2026"}):v.status==="error"?(0,m.jsxs)("div",{className:"dshtd-empty",children:[(0,m.jsx)("b",{children:"Couldn't load todos"}),v.error]}):Ee.length===0?(0,m.jsx)("div",{className:"dshtd-empty",children:r==="archived"?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)("b",{children:"Nothing archived yet"}),"Check items off, then use \u201CArchive completed\u201D to file them away. Archived items stay here until you delete them."]}):k.total===0?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)("b",{children:"No tasks yet"}),"Add your first one above \u2014 this list belongs to the current workspace."]}):r==="open"?(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)("b",{children:"All done \u{1F389}"}),"Nothing open right now."]}):(0,m.jsxs)(m.Fragment,{children:[(0,m.jsx)("b",{children:"Nothing here"}),"No tasks match this filter."]})}):Y.map(w=>{let te=w.items.filter(ie).length,qn=w.items.length===0?0:Math.round(te/w.items.length*100),Ae=c[w.key]===!0;return(0,m.jsxs)("section",{className:"dshtd-group",children:[o!=="none"?(0,m.jsxs)("button",{className:"dshtd-ghead","aria-expanded":!Ae,onClick:()=>u(Ie=>({...Ie,[w.key]:!Ae})),children:[(0,m.jsx)(le,{path:Ae?Q.expand:Q.collapse}),w.label,(0,m.jsxs)("span",{className:"dshtd-gcount",children:[te,"/",w.items.length]}),(0,m.jsx)("span",{className:"dshtd-gbar","aria-hidden":"true",children:(0,m.jsx)("i",{style:{width:`${qn}%`}})})]}):null,Ae?null:(0,m.jsx)("ul",{className:"dshtd-list",children:w.items.map(Ie=>(0,m.jsx)(Gg,{item:Ie,index:P.indexOf(Ie),total:P.length,store:e,knownReleases:Z,knownSprints:R,onOpen:()=>l(Ie.id),onDelete:()=>er(Ie)},Ie.id))})]},w.key)})}),(0,m.jsxs)("div",{className:"dshtd-foot",children:[(0,m.jsxs)("span",{children:[k.open," open \xB7 ",k.done," done",k.archived>0?` \xB7 ${k.archived} archived`:null,v.saving?(0,m.jsx)("span",{className:"dshtd-state",children:" \xB7 saving\u2026"}):null,v.error&&v.status==="ready"?(0,m.jsxs)("span",{className:"dshtd-state err",children:[" \xB7 ",v.error]}):null]}),r==="archived"?k.archived>0?(0,m.jsx)("button",{className:"dshtd-link danger",title:"Permanently delete every archived item",onClick:()=>b({title:"Delete archived tasks",message:`Permanently delete ${k.archived} archived ${k.archived===1?"task":"tasks"}? This cannot be undone.`,confirmLabel:"Delete all",onConfirm:()=>e.update(zp)}),children:"Delete archived"}):null:k.done>0?(0,m.jsx)("button",{className:"dshtd-link",title:"Move completed items to the archive \u2014 they stay recoverable",onClick:()=>e.update(kp),children:"Archive completed"}):null]}),$t?(0,m.jsx)(Op,{item:$t,store:e,onClose:()=>l(null),knownReleases:Z,knownSprints:R}):null,f?(0,m.jsx)(Up,{pending:f,onClose:()=>b(null)}):null]})}var Yg={items:[],status:"ready",error:null,saving:!1},Hg=()=>()=>{},vp=()=>Yg;function Qg(e){let r=new Map,i=e;e.effect(()=>{let o=!1,t;return i.remote.$mount(tu).then(n=>{if(o)return void n();t=n}).catch(n=>{console.error("dsh-todo: failed to mount host remote",n)}),()=>{o=!0,r.clear(),t?.()}},"dsh-todo: mount host remote"),e.effect(()=>{let o=i.inject(["remote.dshTodo","workspaces","slots"],t=>{let n=t;n.slots.inject("conversation.view",()=>n.slots.register({name:"conversation.view",id:"todo",order:20,label:()=>"Todo",inject:a=>{let c=n.workspaces.list.getSnapshot().items,u=Tp(c,a);if(u===void 0)return{store:null};let s=r.get(u);return s===void 0&&(s=new Gn(n.remote.dshTodo,u),r.set(u,s)),{store:s}}},Np))});return()=>{o.dispose()}},"dsh-todo: conversation.view registration")}
435
443
 
436
444
  return module.exports;
437
445
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dennisrongo/dsh-todo",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Todo list for DeepSeek Harness (dsh) — a per-workspace task list persisted on disk by the host",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -34,7 +34,7 @@
34
34
  "scripts": {
35
35
  "build": "node build/build.mjs",
36
36
  "typecheck": "tsc --noEmit",
37
- "test": "node test/smoke.mjs && node test/cli.test.mjs",
37
+ "test": "node build/build.mjs && node test/smoke.mjs && node test/cli.test.mjs",
38
38
  "test:icons": "node test/icon-probe.mjs",
39
39
  "test:modal": "node test/modal-probe.mjs",
40
40
  "test:cli": "node test/cli.test.mjs"