@unbrained/pm-web 2026.8.7 → 2026.8.14

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 (173) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/app.d.ts +16 -0
  3. package/dist/app.js +16 -0
  4. package/dist/app.js.map +1 -1
  5. package/dist/auth.d.ts +5 -0
  6. package/dist/auth.js.map +1 -1
  7. package/dist/board.d.ts +53 -0
  8. package/dist/board.js +48 -0
  9. package/dist/board.js.map +1 -1
  10. package/dist/crypto.d.ts +23 -0
  11. package/dist/crypto.js +32 -0
  12. package/dist/crypto.js.map +1 -1
  13. package/dist/db.d.ts +19 -0
  14. package/dist/db.js +19 -0
  15. package/dist/db.js.map +1 -1
  16. package/dist/ical.d.ts +72 -0
  17. package/dist/ical.js +73 -0
  18. package/dist/ical.js.map +1 -1
  19. package/dist/index.d.ts +11 -0
  20. package/dist/index.js +52 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/middleware/auth.d.ts +8 -0
  23. package/dist/middleware/auth.js.map +1 -1
  24. package/dist/oidc.d.ts +87 -0
  25. package/dist/oidc.js +113 -0
  26. package/dist/oidc.js.map +1 -1
  27. package/dist/routes/admin.js +15 -0
  28. package/dist/routes/admin.js.map +1 -1
  29. package/dist/routes/github.js +13 -0
  30. package/dist/routes/github.js.map +1 -1
  31. package/dist/routes/oidc.d.ts +10 -0
  32. package/dist/routes/oidc.js +51 -0
  33. package/dist/routes/oidc.js.map +1 -1
  34. package/dist/routes/pm.js +132 -1
  35. package/dist/routes/pm.js.map +1 -1
  36. package/dist/routes/sharing.js +8 -0
  37. package/dist/routes/sharing.js.map +1 -1
  38. package/dist/services/mutation-event-watcher.js +13 -0
  39. package/dist/services/mutation-event-watcher.js.map +1 -1
  40. package/dist/services/pm-runner.d.ts +82 -0
  41. package/dist/services/pm-runner.js +121 -0
  42. package/dist/services/pm-runner.js.map +1 -1
  43. package/dist/services/project-watcher.d.ts +22 -0
  44. package/dist/services/project-watcher.js +51 -0
  45. package/dist/services/project-watcher.js.map +1 -1
  46. package/dist/services/realtime-bus.d.ts +46 -0
  47. package/dist/services/realtime-bus.js +59 -0
  48. package/dist/services/realtime-bus.js.map +1 -1
  49. package/dist/services/sse.d.ts +134 -0
  50. package/dist/services/sse.js +145 -0
  51. package/dist/services/sse.js.map +1 -1
  52. package/manifest.json +1 -1
  53. package/package.json +5 -3
  54. package/public/src/app.js +44 -1
  55. package/public/src/app.js.map +1 -1
  56. package/public/src/app.ts +44 -1
  57. package/public/src/components/modals.js +11 -0
  58. package/public/src/components/modals.js.map +1 -1
  59. package/public/src/components/modals.ts +11 -0
  60. package/public/src/components/toast.js +2 -0
  61. package/public/src/components/toast.js.map +1 -1
  62. package/public/src/components/toast.ts +2 -0
  63. package/public/src/constants.js +21 -4
  64. package/public/src/constants.js.map +1 -1
  65. package/public/src/constants.ts +21 -4
  66. package/public/src/filters.js +10 -7
  67. package/public/src/filters.js.map +1 -1
  68. package/public/src/filters.ts +10 -7
  69. package/public/src/i18n.js +11 -1
  70. package/public/src/i18n.js.map +1 -1
  71. package/public/src/i18n.ts +13 -1
  72. package/public/src/state.js +3 -0
  73. package/public/src/state.js.map +1 -1
  74. package/public/src/state.ts +3 -0
  75. package/public/src/sw.ts +71 -7
  76. package/public/src/theme.js +13 -1
  77. package/public/src/theme.js.map +1 -1
  78. package/public/src/theme.ts +15 -1
  79. package/public/src/types.ts +2 -0
  80. package/public/src/utils.js +26 -1
  81. package/public/src/utils.js.map +1 -1
  82. package/public/src/utils.ts +26 -1
  83. package/public/src/views/activity.js +1 -0
  84. package/public/src/views/activity.js.map +1 -1
  85. package/public/src/views/activity.ts +1 -0
  86. package/public/src/views/admin.js +134 -0
  87. package/public/src/views/admin.js.map +1 -1
  88. package/public/src/views/admin.ts +134 -0
  89. package/public/src/views/auth.js +31 -0
  90. package/public/src/views/auth.js.map +1 -1
  91. package/public/src/views/auth.ts +31 -0
  92. package/public/src/views/calendar.js +3 -0
  93. package/public/src/views/calendar.js.map +1 -1
  94. package/public/src/views/calendar.ts +3 -0
  95. package/public/src/views/comments-audit.js +1 -0
  96. package/public/src/views/comments-audit.js.map +1 -1
  97. package/public/src/views/comments-audit.ts +1 -0
  98. package/public/src/views/config.js +67 -0
  99. package/public/src/views/config.js.map +1 -1
  100. package/public/src/views/config.ts +67 -0
  101. package/public/src/views/context.js +2 -0
  102. package/public/src/views/context.js.map +1 -1
  103. package/public/src/views/context.ts +2 -0
  104. package/public/src/views/create.js +3 -0
  105. package/public/src/views/create.js.map +1 -1
  106. package/public/src/views/create.ts +3 -0
  107. package/public/src/views/dedupe.js +1 -0
  108. package/public/src/views/dedupe.js.map +1 -1
  109. package/public/src/views/dedupe.ts +1 -0
  110. package/public/src/views/export.js +6 -0
  111. package/public/src/views/export.js.map +1 -1
  112. package/public/src/views/export.ts +6 -0
  113. package/public/src/views/github.js +58 -0
  114. package/public/src/views/github.js.map +1 -1
  115. package/public/src/views/github.ts +58 -0
  116. package/public/src/views/graph-canvas.js +18 -0
  117. package/public/src/views/graph-canvas.js.map +1 -1
  118. package/public/src/views/graph-canvas.ts +20 -0
  119. package/public/src/views/graph.js +41 -0
  120. package/public/src/views/graph.js.map +1 -1
  121. package/public/src/views/graph.ts +42 -1
  122. package/public/src/views/groups.js +45 -0
  123. package/public/src/views/groups.js.map +1 -1
  124. package/public/src/views/groups.ts +45 -0
  125. package/public/src/views/guide.js +3 -0
  126. package/public/src/views/guide.js.map +1 -1
  127. package/public/src/views/guide.ts +3 -0
  128. package/public/src/views/health.js +3 -0
  129. package/public/src/views/health.js.map +1 -1
  130. package/public/src/views/health.ts +3 -0
  131. package/public/src/views/items.js +196 -6
  132. package/public/src/views/items.js.map +1 -1
  133. package/public/src/views/items.ts +196 -6
  134. package/public/src/views/normalize.js +2 -0
  135. package/public/src/views/normalize.js.map +1 -1
  136. package/public/src/views/normalize.ts +2 -0
  137. package/public/src/views/packages.js +41 -0
  138. package/public/src/views/packages.js.map +1 -1
  139. package/public/src/views/packages.ts +41 -0
  140. package/public/src/views/plan-execution.js +6 -0
  141. package/public/src/views/plan-execution.js.map +1 -1
  142. package/public/src/views/plan-execution.ts +8 -0
  143. package/public/src/views/plan.js +145 -0
  144. package/public/src/views/plan.js.map +1 -1
  145. package/public/src/views/plan.ts +146 -1
  146. package/public/src/views/projects.js +60 -0
  147. package/public/src/views/projects.js.map +1 -1
  148. package/public/src/views/projects.ts +60 -0
  149. package/public/src/views/router.js +43 -0
  150. package/public/src/views/router.js.map +1 -1
  151. package/public/src/views/router.ts +43 -0
  152. package/public/src/views/search.js +5 -0
  153. package/public/src/views/search.js.map +1 -1
  154. package/public/src/views/search.ts +5 -0
  155. package/public/src/views/settings.js +36 -0
  156. package/public/src/views/settings.js.map +1 -1
  157. package/public/src/views/settings.ts +36 -0
  158. package/public/src/views/shared.js +1 -0
  159. package/public/src/views/shared.js.map +1 -1
  160. package/public/src/views/shared.ts +1 -0
  161. package/public/src/views/sharing.js +32 -0
  162. package/public/src/views/sharing.js.map +1 -1
  163. package/public/src/views/sharing.ts +32 -0
  164. package/public/src/views/stats.js +1 -0
  165. package/public/src/views/stats.js.map +1 -1
  166. package/public/src/views/stats.ts +1 -0
  167. package/public/src/views/templates.js +4 -0
  168. package/public/src/views/templates.js.map +1 -1
  169. package/public/src/views/templates.ts +4 -0
  170. package/public/src/views/validate.js +1 -0
  171. package/public/src/views/validate.js.map +1 -1
  172. package/public/src/views/validate.ts +1 -0
  173. package/public/sw.js +60 -5
@@ -1 +1 @@
1
- {"version":3,"file":"health.js","sourceRoot":"","sources":["health.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"health.js","sourceRoot":"","sources":["health.ts"],"names":[],"mappings":"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"}
@@ -20,6 +20,7 @@ function isHistoryDriftIssue(issue: HealthIssue): boolean {
20
20
  return msg.includes('history') && (msg.includes('drift') || msg.includes('repair') || msg.includes('mismatch'));
21
21
  }
22
22
 
23
+ /** Renders the markup for one health issue, appending Repair and Dry Run buttons when the issue is history drift and a project item id is detected in the message. */
23
24
  function renderIssueRow(issue: HealthIssue, projectId: string): string {
24
25
  const msg = escHtml(issue.message || issue.description || String(issue));
25
26
  if (isHistoryDriftIssue(issue)) {
@@ -34,6 +35,7 @@ function renderIssueRow(issue: HealthIssue, projectId: string): string {
34
35
  return `<div class="health-issue-item">⚠ ${msg}</div>`;
35
36
  }
36
37
 
38
+ /** Runs item history repair (or a dry-run preview) via the project's history-repair endpoint and toasts a summary of entries scanned, rehashed, and patched, including verification failures. */
37
39
  export async function repairItemHistory(projectId: string, itemId: string, dryRun: boolean): Promise<void> {
38
40
  try {
39
41
  const data = await api<HistoryRepairResponse>('POST', `/projects/${projectId}/pm/items/${encodeURIComponent(itemId)}/history-repair`, { dryRun });
@@ -63,6 +65,7 @@ export async function repairItemHistory(projectId: string, itemId: string, dryRu
63
65
  }
64
66
  }
65
67
 
68
+ /** Renders the project health view: a numeric health score card colored by threshold and a list of issues (each rendered with repair actions when applicable), plus an optional summary. */
66
69
  export async function renderHealthView(): Promise<void> {
67
70
  const el = document.getElementById('content-health');
68
71
  if (!el) return;
@@ -16,9 +16,11 @@ import { EMPTY_FILTERS, filtersToQueryString, filtersFromSearchParams, hasActive
16
16
  // alive across the Preview → Apply call pair and is GC-friendly.
17
17
  const bulkUpdatePayloads = new WeakMap();
18
18
  const bulkClosePayloads = new WeakMap();
19
- // Mirror the active item filters into the URL query string (on the /items
20
- // path) so a filtered view is shareable and bookmarkable. Replaces the current
21
- // history entry to avoid polluting back/forward with every keystroke.
19
+ /**
20
+ * Mirrors the current item filters into the page URL query string when the
21
+ * items view is active, using history.replaceState so the filtered view is
22
+ * shareable without adding back/forward entries. Does nothing on other views.
23
+ */
22
24
  function syncFiltersToUrl() {
23
25
  if (state.currentView !== 'items')
24
26
  return;
@@ -29,8 +31,12 @@ function syncFiltersToUrl() {
29
31
  }
30
32
  catch { /* ignore */ }
31
33
  }
32
- // Read filters from the current URL's query string into state. Called when the
33
- // Items view is opened so a shared link applies its filters.
34
+ /**
35
+ * Reads item filters from the current URL query string into state. Only
36
+ * replaces the in-memory filters when the URL actually carries filter
37
+ * parameters, so navigating to /items without a query string preserves the
38
+ * existing filters.
39
+ */
34
40
  export function loadFiltersFromUrl() {
35
41
  const params = new URLSearchParams(window.location.search);
36
42
  // Only override when the URL actually carries filter params, so navigating
@@ -47,6 +53,12 @@ const DEP_REL_OPTIONS = [
47
53
  { value: 'child', label: 'Child' },
48
54
  { value: 'related', label: 'Related' },
49
55
  ];
56
+ /**
57
+ * Normalizes a raw dependency relationship label to a canonical value by
58
+ * trimming, lowercasing, and converting dashes to underscores, then applying a
59
+ * known-alias map (e.g. blockedby → blocked_by). Returns the alias when one
60
+ * exists, otherwise the normalized input unchanged.
61
+ */
50
62
  function normalizeDepRelation(raw) {
51
63
  const aliases = {
52
64
  blockedby: 'blocked_by',
@@ -67,12 +79,22 @@ function normalizeDepRelation(raw) {
67
79
  const normalized = (raw || '').trim().toLowerCase().replace(/-/g, '_');
68
80
  return aliases[normalized] ?? normalized;
69
81
  }
82
+ /**
83
+ * Builds the HTML <option> elements for a dependency relationship dropdown from
84
+ * the fixed DEP_REL_OPTIONS list, marking the option that matches the
85
+ * normalized selected value as selected.
86
+ */
70
87
  function renderDependencyOptions(selected) {
71
88
  const current = normalizeDepRelation(selected);
72
89
  return DEP_REL_OPTIONS
73
90
  .map((option) => `<option value="${option.value}"${option.value === current ? ' selected' : ''}>${option.label}</option>`)
74
91
  .join('');
75
92
  }
93
+ /**
94
+ * Returns a human-readable display label for a dependency relationship by
95
+ * looking up its normalized value in a fixed label map, falling back to the
96
+ * original relationship string when no label is defined.
97
+ */
76
98
  function depLabel(rel) {
77
99
  const labels = {
78
100
  blocked_by: 'Blocked by',
@@ -92,6 +114,11 @@ function depRelation(dep) {
92
114
  // ═══════════════════════════════════════════════════════════════
93
115
  // BULK UPDATE
94
116
  // ═══════════════════════════════════════════════════════════════
117
+ /**
118
+ * Opens the Bulk Update Items modal, which lets the user filter items and
119
+ * choose fields to change across many items at once. Shows an info toast and
120
+ * returns early when no project is currently selected.
121
+ */
95
122
  export function showBulkUpdateModal() {
96
123
  if (!state.currentProject) {
97
124
  toast('Select a project first', 'info');
@@ -166,6 +193,12 @@ export function showBulkUpdateModal() {
166
193
  </div>`, '');
167
194
  showModal('bulk-update-modal');
168
195
  }
196
+ /**
197
+ * Runs a dry-run bulk update and renders a preview of the matched items and
198
+ * their field changes inside the modal. Requires at least one field to update,
199
+ * and stashes the actual (non-dry-run) payload on the Apply button so
200
+ * applyBulkUpdate can run without re-reading the form.
201
+ */
169
202
  export async function previewBulkUpdate() {
170
203
  const pid = state.currentProject?.id;
171
204
  if (!pid)
@@ -255,6 +288,12 @@ export async function previewBulkUpdate() {
255
288
  previewEl.innerHTML = `<div style="color:var(--status-open);font-size:13px">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div>`;
256
289
  }
257
290
  }
291
+ /**
292
+ * Applies the bulk update payload stashed on the Apply button by
293
+ * previewBulkUpdate, posting to the update-many endpoint. Reports the updated
294
+ * and failed counts via toast, closes the bulk update modal, and refreshes the
295
+ * items list and project badge.
296
+ */
258
297
  export async function applyBulkUpdate() {
259
298
  const pid = state.currentProject?.id;
260
299
  if (!pid)
@@ -295,6 +334,11 @@ export async function applyBulkUpdate() {
295
334
  // ═══════════════════════════════════════════════════════════════
296
335
  // BULK CLOSE
297
336
  // ═══════════════════════════════════════════════════════════════
337
+ /**
338
+ * Opens the Bulk Close Items modal, which lets the user filter items and close
339
+ * or cancel many of them at once with a required reason. Shows an info toast
340
+ * and returns early when no project is currently selected.
341
+ */
298
342
  export function showBulkCloseModal() {
299
343
  if (!state.currentProject) {
300
344
  toast('Select a project first', 'info');
@@ -352,6 +396,12 @@ export function showBulkCloseModal() {
352
396
  </div>`, '');
353
397
  showModal('bulk-close-modal');
354
398
  }
399
+ /**
400
+ * Previews the items that would be closed or canceled by running an
401
+ * update-many dry-run, and renders the matched count and first few items
402
+ * inside the modal. Requires a close reason, and stashes the close payload on
403
+ * the Apply button for applyBulkClose to use.
404
+ */
355
405
  export async function previewBulkClose() {
356
406
  const pid = state.currentProject?.id;
357
407
  if (!pid)
@@ -421,6 +471,13 @@ export async function previewBulkClose() {
421
471
  previewEl.innerHTML = `<div style="color:var(--status-blocked);font-size:13px">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div>`;
422
472
  }
423
473
  }
474
+ /**
475
+ * Applies the bulk close payload stashed on the Apply button by
476
+ * previewBulkClose, posting to the close-many endpoint which closes or cancels
477
+ * each matched item with the chosen reason. Reports the closed and failed
478
+ * counts via toast, closes the modal, and refreshes the items list and project
479
+ * badge.
480
+ */
424
481
  export async function applyBulkClose() {
425
482
  const pid = state.currentProject?.id;
426
483
  if (!pid)
@@ -472,6 +529,12 @@ export async function applyBulkClose() {
472
529
  }
473
530
  }
474
531
  }
532
+ /**
533
+ * Renders the Items view into the page: a header with refresh and bulk
534
+ * actions, the status filter tabs, the multi-field filter bar, and a loading
535
+ * list placeholder, then fetches and renders the items. Shows an empty state
536
+ * and returns when no project is selected.
537
+ */
475
538
  export async function renderItemsView() {
476
539
  const el = document.getElementById('content-items');
477
540
  if (!el)
@@ -521,6 +584,12 @@ export async function renderItemsView() {
521
584
  <div id="items-list"><div class="loading-state"><div class="loading-spinner"></div></div></div>`;
522
585
  await fetchAndRenderItems();
523
586
  }
587
+ /**
588
+ * Fetches items for the current project using the active filters (capped at
589
+ * 200, with tags filtered client-side because the list endpoint has no tag
590
+ * flag), stores them in state, updates the count subtitle, and renders the
591
+ * list. Shows an error state in the list element if the request fails.
592
+ */
524
593
  export async function fetchAndRenderItems() {
525
594
  const pid = state.currentProject?.id;
526
595
  if (!pid)
@@ -561,6 +630,10 @@ export async function fetchAndRenderItems() {
561
630
  listEl.innerHTML = `<div class="empty-state"><div class="empty-state-text">Failed to load items: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
562
631
  }
563
632
  }
633
+ /**
634
+ * Renders the items list element from state.items, showing an empty-state
635
+ * placeholder when there are no items or the mapped item rows otherwise.
636
+ */
564
637
  function renderItemsList() {
565
638
  const el = document.getElementById('items-list');
566
639
  if (!el)
@@ -575,6 +648,11 @@ function renderItemsList() {
575
648
  }
576
649
  el.innerHTML = `<div class="item-list">${state.items.map(item => renderItemRow(item)).join('')}</div>`;
577
650
  }
651
+ /**
652
+ * Returns the HTML markup for a single items-list row: a clickable card
653
+ * showing the item's type icon, id, title, tags, priority dot, and status
654
+ * badge that opens the item detail modal when clicked.
655
+ */
578
656
  export function renderItemRow(item) {
579
657
  const tags = (item.tags || []).map(t => `<span class="tag">${escHtml(t)}</span>`).join('');
580
658
  return `<div class="item-row" onclick="window.__app.openItemDetail('${escHtml(item.id)}')">
@@ -588,6 +666,11 @@ export function renderItemRow(item) {
588
666
  </div>
589
667
  </div>`;
590
668
  }
669
+ /**
670
+ * Reads the current values from every items filter-bar control into
671
+ * state.itemFilters, mirrors them to the URL, and re-fetches and renders the
672
+ * list.
673
+ */
591
674
  export function applyItemFilters() {
592
675
  const fs = document.getElementById('filter-status');
593
676
  const ft = document.getElementById('filter-type');
@@ -606,6 +689,10 @@ export function applyItemFilters() {
606
689
  syncFiltersToUrl();
607
690
  fetchAndRenderItems();
608
691
  }
692
+ /**
693
+ * Resets the item filters to empty, clears every filter-bar control in the
694
+ * DOM, mirrors the cleared filters to the URL, and re-renders the items list.
695
+ */
609
696
  export function clearFilters() {
610
697
  state.itemFilters = { ...EMPTY_FILTERS };
611
698
  const ids = ['filter-status', 'filter-type', 'filter-priority', 'filter-sprint', 'filter-release', 'filter-assignee', 'filter-tag'];
@@ -617,7 +704,11 @@ export function clearFilters() {
617
704
  syncFiltersToUrl();
618
705
  fetchAndRenderItems();
619
706
  }
620
- // Copy a shareable URL for the current filtered Items view to the clipboard.
707
+ /**
708
+ * Builds a shareable URL for the current filtered items view and copies it to
709
+ * the clipboard, toasting success or, if the clipboard write fails, toasting
710
+ * the URL itself for manual copy.
711
+ */
621
712
  export async function copyFilterLink() {
622
713
  const qs = filtersToQueryString(state.itemFilters);
623
714
  const url = `${window.location.origin}/items${qs ? `?${qs}` : ''}`;
@@ -629,6 +720,10 @@ export async function copyFilterLink() {
629
720
  toast(url, 'info');
630
721
  }
631
722
  }
723
+ /**
724
+ * Sets the status filter to the provided value, mirrors it to the URL, and
725
+ * re-renders the entire items view.
726
+ */
632
727
  export function setStatusFilter(status) {
633
728
  state.itemFilters.status = status;
634
729
  syncFiltersToUrl();
@@ -637,6 +732,12 @@ export function setStatusFilter(status) {
637
732
  // ═══════════════════════════════════════════════════════════════
638
733
  // ITEM DETAIL MODAL
639
734
  // ═══════════════════════════════════════════════════════════════
735
+ /**
736
+ * Opens the item detail modal and renders it. Fetches the item together with
737
+ * its comments, history, dependencies, learnings, notes, tests, and linked
738
+ * files in parallel (each detail call falls back to empty on failure) and
739
+ * builds the full tabbed detail view.
740
+ */
640
741
  export async function openItemDetail(itemId) {
641
742
  const pid = state.currentProject?.id;
642
743
  if (!pid)
@@ -969,6 +1070,11 @@ export async function openItemDetail(itemId) {
969
1070
  bodyEl.innerHTML = `<div class="empty-state"><div class="empty-state-text">Failed to load: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
970
1071
  }
971
1072
  }
1073
+ /**
1074
+ * Formats a timestamp as a human-readable relative age string (just now,
1075
+ * Nm/Nh/Nd ago) for recent times, or a localized date for anything older than
1076
+ * a week. Returns an empty string when the timestamp is missing.
1077
+ */
972
1078
  function relTime(ts) {
973
1079
  if (!ts)
974
1080
  return '';
@@ -990,6 +1096,12 @@ function fmtDate(ts) {
990
1096
  return '';
991
1097
  return new Date(ts).toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
992
1098
  }
1099
+ /**
1100
+ * Switches the item detail modal to the selected tab: toggles the active class
1101
+ * on the tabs, shows the target tab panel while hiding the others, tears down
1102
+ * the local graph when leaving the graph tab, and initializes the graph along
1103
+ * with its depth-slider listener when entering the graph tab with a nodeId.
1104
+ */
993
1105
  export function switchDetailTab(tabEl, targetId, nodeId) {
994
1106
  const allTabs = tabEl.parentElement?.querySelectorAll('.tab');
995
1107
  allTabs?.forEach(t => t.classList.remove('active'));
@@ -1017,6 +1129,11 @@ export function switchDetailTab(tabEl, targetId, nodeId) {
1017
1129
  });
1018
1130
  }
1019
1131
  }
1132
+ /**
1133
+ * Posts the comment typed into the detail modal's comment box to the item,
1134
+ * requiring non-empty text. On success it clears the box, toasts confirmation,
1135
+ * and reopens the item detail to refresh the comments tab.
1136
+ */
1020
1137
  export async function addComment(itemId) {
1021
1138
  const el = document.getElementById('new-comment');
1022
1139
  if (!el)
@@ -1036,6 +1153,10 @@ export async function addComment(itemId) {
1036
1153
  toast(err instanceof Error ? err.message : String(err), 'error');
1037
1154
  }
1038
1155
  }
1156
+ /**
1157
+ * Posts the note typed into the detail modal's note box to the item, requiring
1158
+ * non-empty text. On success it clears the box and toasts confirmation.
1159
+ */
1039
1160
  export async function addNote(itemId) {
1040
1161
  const el = document.getElementById('new-note');
1041
1162
  if (!el)
@@ -1054,6 +1175,11 @@ export async function addNote(itemId) {
1054
1175
  toast(err instanceof Error ? err.message : String(err), 'error');
1055
1176
  }
1056
1177
  }
1178
+ /**
1179
+ * Appends the text from the detail modal's append box to the item's
1180
+ * description, requiring non-empty text. On success it clears the box and
1181
+ * toasts confirmation.
1182
+ */
1057
1183
  export async function appendItem(itemId) {
1058
1184
  const el = document.getElementById('new-append');
1059
1185
  if (!el)
@@ -1072,6 +1198,11 @@ export async function appendItem(itemId) {
1072
1198
  toast(err instanceof Error ? err.message : String(err), 'error');
1073
1199
  }
1074
1200
  }
1201
+ /**
1202
+ * Saves the item detail's Update form: reads every edit field, requires a
1203
+ * title, and PATCHes the changed fields to the backend. On success it reopens
1204
+ * the item detail and refreshes the items list and project badge.
1205
+ */
1075
1206
  export async function updateItem(itemId) {
1076
1207
  const titleEl = document.getElementById('edit-title');
1077
1208
  const statusEl = document.getElementById('edit-status');
@@ -1132,6 +1263,12 @@ export async function updateItem(itemId) {
1132
1263
  toast(err instanceof Error ? err.message : String(err), 'error');
1133
1264
  }
1134
1265
  }
1266
+ /**
1267
+ * Closes or cancels the item with a required reason. For canceled it first
1268
+ * sets the status to canceled via the update endpoint before posting the close
1269
+ * reason; for closed it posts the close reason directly. Reports the outcome,
1270
+ * hides the detail modal, and refreshes the items list and project badge.
1271
+ */
1135
1272
  export async function closeItem(itemId, targetStatus) {
1136
1273
  const reasonEl = document.getElementById('close-reason');
1137
1274
  const reason = reasonEl?.value?.trim();
@@ -1157,6 +1294,10 @@ export async function closeItem(itemId, targetStatus) {
1157
1294
  toast(err instanceof Error ? err.message : String(err), 'error');
1158
1295
  }
1159
1296
  }
1297
+ /**
1298
+ * Shows a confirmation dialog and, on confirm, DELETEs the item, hides the
1299
+ * detail modal, and refreshes the items list and project badge.
1300
+ */
1160
1301
  export function confirmDeleteItem(itemId) {
1161
1302
  confirmDialog('Delete Item?', 'This action cannot be undone. The item and all its data will be permanently removed.', async () => {
1162
1303
  try {
@@ -1175,6 +1316,12 @@ export function confirmDeleteItem(itemId) {
1175
1316
  // ═══════════════════════════════════════════════════════════════
1176
1317
  // CLAIM / RELEASE / START / PAUSE
1177
1318
  // ═══════════════════════════════════════════════════════════════
1319
+ /**
1320
+ * Claims the item for the current user via the claim endpoint, dimming the
1321
+ * matching list row optimistically. On success it reopens the detail and
1322
+ * refreshes the list; on failure it restores the row opacity and toasts the
1323
+ * error.
1324
+ */
1178
1325
  export async function claimItem(itemId) {
1179
1326
  const row = document.querySelector(`.item-row[onclick*="${itemId}"]`);
1180
1327
  if (row)
@@ -1192,6 +1339,10 @@ export async function claimItem(itemId) {
1192
1339
  row.style.opacity = '';
1193
1340
  }
1194
1341
  }
1342
+ /**
1343
+ * Releases the current user's claim on the item via the release endpoint, then
1344
+ * reopens the detail and refreshes the items list.
1345
+ */
1195
1346
  export async function releaseItem(itemId) {
1196
1347
  try {
1197
1348
  await api('POST', `/projects/${state.currentProject.id}/pm/release/${itemId}`, {});
@@ -1204,6 +1355,10 @@ export async function releaseItem(itemId) {
1204
1355
  toast(err instanceof Error ? err.message : String(err), 'error');
1205
1356
  }
1206
1357
  }
1358
+ /**
1359
+ * Starts the item (moving it into in_progress) via the start-task endpoint,
1360
+ * then reopens the detail and refreshes the items list and project badge.
1361
+ */
1207
1362
  export async function startItem(itemId) {
1208
1363
  try {
1209
1364
  await api('POST', `/projects/${state.currentProject.id}/pm/start-task/${itemId}`, {});
@@ -1217,6 +1372,10 @@ export async function startItem(itemId) {
1217
1372
  toast(err instanceof Error ? err.message : String(err), 'error');
1218
1373
  }
1219
1374
  }
1375
+ /**
1376
+ * Pauses the in-progress item via the pause-task endpoint, then reopens the
1377
+ * detail and refreshes the items list and project badge.
1378
+ */
1220
1379
  export async function pauseItem(itemId) {
1221
1380
  try {
1222
1381
  await api('POST', `/projects/${state.currentProject.id}/pm/pause-task/${itemId}`, {});
@@ -1233,6 +1392,12 @@ export async function pauseItem(itemId) {
1233
1392
  // ═══════════════════════════════════════════════════════════════
1234
1393
  // DEPS / LEARNINGS / TESTS / FILES
1235
1394
  // ═══════════════════════════════════════════════════════════════
1395
+ /**
1396
+ * Adds a dependency to the item from the detail modal's dependency inputs:
1397
+ * reads and normalizes the target id and relationship, rejects an empty or
1398
+ * self-referential target, and posts to the deps endpoint. On success it
1399
+ * reopens the item detail.
1400
+ */
1236
1401
  export async function addDep(itemId) {
1237
1402
  const targetIdEl = document.getElementById('dep-target-id');
1238
1403
  const relEl = document.getElementById('dep-rel');
@@ -1255,6 +1420,11 @@ export async function addDep(itemId) {
1255
1420
  toast(err instanceof Error ? err.message : String(err), 'error');
1256
1421
  }
1257
1422
  }
1423
+ /**
1424
+ * Removes a dependency from the item after confirming with the user. Normalizes
1425
+ * the relationship, requires a target id, and DELETEs the dependency from the
1426
+ * deps endpoint; on success it reopens the item detail.
1427
+ */
1258
1428
  export async function removeDep(itemId, targetId, relation) {
1259
1429
  const rel = normalizeDepRelation(relation);
1260
1430
  if (!targetId) {
@@ -1272,6 +1442,11 @@ export async function removeDep(itemId, targetId, relation) {
1272
1442
  }
1273
1443
  }, true);
1274
1444
  }
1445
+ /**
1446
+ * Records a learning typed into the detail modal on the item, requiring
1447
+ * non-empty text. On success it toasts confirmation and reopens the item
1448
+ * detail.
1449
+ */
1275
1450
  export async function addLearning(itemId) {
1276
1451
  const el = document.getElementById('new-learning');
1277
1452
  const text = el?.value?.trim() || '';
@@ -1288,6 +1463,11 @@ export async function addLearning(itemId) {
1288
1463
  toast(err instanceof Error ? err.message : String(err), 'error');
1289
1464
  }
1290
1465
  }
1466
+ /**
1467
+ * Adds a test command (required) with an optional description to the item via
1468
+ * the tests endpoint. On success it toasts confirmation and reopens the item
1469
+ * detail.
1470
+ */
1291
1471
  export async function addTest(itemId) {
1292
1472
  const cmdEl = document.getElementById('new-test-cmd');
1293
1473
  const descEl = document.getElementById('new-test-desc');
@@ -1306,6 +1486,10 @@ export async function addTest(itemId) {
1306
1486
  toast(err instanceof Error ? err.message : String(err), 'error');
1307
1487
  }
1308
1488
  }
1489
+ /**
1490
+ * Links a file path (required) to the item via the files endpoint. On success
1491
+ * it toasts confirmation and reopens the item detail.
1492
+ */
1309
1493
  export async function addFileLink(itemId) {
1310
1494
  const el = document.getElementById('file-path-input');
1311
1495
  const filePath = el?.value?.trim() || '';
@@ -1325,6 +1509,12 @@ export async function addFileLink(itemId) {
1325
1509
  // ═══════════════════════════════════════════════════════════════
1326
1510
  // USE ITEM AS TEMPLATE
1327
1511
  // ═══════════════════════════════════════════════════════════════
1512
+ /**
1513
+ * Closes the item detail modal and opens the create view pre-filled from the
1514
+ * given item's fields, prefixing the title with "Copy of" and focusing and
1515
+ * selecting it so the user can immediately edit. The form is filled after a
1516
+ * short delay to let the create view render.
1517
+ */
1328
1518
  export function useItemAsTemplate(item) {
1329
1519
  // Close the item detail modal and navigate to create view
1330
1520
  hideModal('item-detail-modal');