@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
@@ -75,6 +75,7 @@ function removeCtxMenu(): void {
75
75
  if (ctxMenuEl) { ctxMenuEl.remove(); ctxMenuEl = null; }
76
76
  }
77
77
 
78
+ /** Builds and shows the right-click context menu for a graph node, with actions to open the item, select and focus, show its neighborhood, expand two hops, and copy its id. */
78
79
  function showCtxMenu(nodeId: string, x: number, y: number): void {
79
80
  removeCtxMenu();
80
81
  const graph = currentGraph?.graph || {};
@@ -149,6 +150,7 @@ function nodeTitle(node: GraphNode): string {
149
150
  return String(node.properties?.title || node.id);
150
151
  }
151
152
 
153
+ /** Returns a display type for a node, preferring the `kind` property, then `type`, then a non-core label, defaulting to 'Item'. */
152
154
  function nodeType(node: GraphNode): string {
153
155
  return String(
154
156
  node.properties?.kind
@@ -176,6 +178,7 @@ function nodeLane(node: GraphNode): CanvasNode['lane'] {
176
178
  return 'item';
177
179
  }
178
180
 
181
+ /** Extracts a short user-facing error message from a raw error string (JSON detail, title, or code, otherwise the trimmed raw text), returning an empty string when the error is just the missing pm-graph extension. */
179
182
  function compactError(raw: string | undefined): string {
180
183
  if (!raw) return '';
181
184
  try {
@@ -190,6 +193,7 @@ function compactError(raw: string | undefined): string {
190
193
 
191
194
  // ── Graph data processing ─────────────────────────────────────
192
195
 
196
+ /** Returns the set of ids directly connected to the given node (plus the node itself) across the provided relationships. */
193
197
  function directNeighborIds(nodeId: string, rels: GraphRelationship[]): Set<string> {
194
198
  const ids = new Set<string>([nodeId]);
195
199
  for (const r of rels) {
@@ -199,6 +203,7 @@ function directNeighborIds(nodeId: string, rels: GraphRelationship[]): Set<strin
199
203
  return ids;
200
204
  }
201
205
 
206
+ /** Returns the neighbor id(s) on the other end of a relationship relative to a node, filtered by traversal direction (incoming, outgoing, or connected). */
202
207
  function walkDirectionMatch(rel: GraphRelationship, nodeId: string, dir: GraphFilter['direction']): string[] {
203
208
  if (dir === 'incoming') {
204
209
  return rel.to === nodeId ? [rel.from] : [];
@@ -209,6 +214,7 @@ function walkDirectionMatch(rel: GraphRelationship, nodeId: string, dir: GraphFi
209
214
  return rel.to === nodeId ? [rel.from] : rel.from === nodeId ? [rel.to] : [];
210
215
  }
211
216
 
217
+ /** Performs a bounded breadth-first traversal out to `depth` hops from a node, returning all reachable ids (including the node itself) while respecting the traversal direction. */
212
218
  function expandedNeighborIds(nodeId: string, rels: GraphRelationship[], depth: number, direction: GraphFilter['direction']): Set<string> {
213
219
  const ids = new Set<string>([nodeId]);
214
220
  let frontier = new Set<string>([nodeId]);
@@ -229,6 +235,7 @@ function expandedNeighborIds(nodeId: string, rels: GraphRelationship[], depth: n
229
235
  return ids;
230
236
  }
231
237
 
238
+ /** Counts both endpoints of every relationship, returning a map of node id to its total edge degree (incrementing the count for both `from` and `to`). */
232
239
  function degreeMap(rels: GraphRelationship[]): Map<string, number> {
233
240
  const m = new Map<string, number>();
234
241
  for (const r of rels) {
@@ -248,6 +255,7 @@ function blockingPair(rel: GraphRelationship): { blocked: string; blocker: strin
248
255
  return null;
249
256
  }
250
257
 
258
+ /** Maps a relationship type to a human-readable dependency label (for example 'Depends on'), falling back to the lowercased type with underscores replaced by spaces. */
251
259
  function dependencyLabel(rel: GraphRelationship): string {
252
260
  const labels: Record<string, string> = {
253
261
  DEPENDS_ON: 'Depends on',
@@ -263,6 +271,7 @@ function dependencyLabel(rel: GraphRelationship): string {
263
271
  return labels[rel.type] ?? rel.type.replace(/_/g, ' ').toLowerCase();
264
272
  }
265
273
 
274
+ /** Computes the critical (longest) dependency path through the given relationships and returns its node ids as a set, or an empty set when there is no multi-step chain. */
266
275
  function computeCriticalPath(rels: GraphRelationship[]): Set<string> {
267
276
  const depRels = rels.filter(isDependencyRel);
268
277
  if (!depRels.length) return new Set();
@@ -279,6 +288,7 @@ function computeCriticalPath(rels: GraphRelationship[]): Set<string> {
279
288
  }
280
289
 
281
290
  const memo = new Map<string, string[]>();
291
+ /** Memoized depth-first search returning the longest chain of blockers starting from the given id, guarding against cycles with a visited set. */
282
292
  function longestPathFrom(id: string, seen = new Set<string>()): string[] {
283
293
  if (memo.has(id)) return memo.get(id)!;
284
294
  if (seen.has(id)) return [id];
@@ -303,6 +313,7 @@ function computeCriticalPath(rels: GraphRelationship[]): Set<string> {
303
313
  return longest.length < 2 ? new Set() : new Set(longest);
304
314
  }
305
315
 
316
+ /** Builds per-node dependency stats from blocking relationships, mapping each node id to the sets of items that block it and that it blocks. */
306
317
  function blockerStats(rels: GraphRelationship[]): Map<string, { blockers: Set<string>; blocked: Set<string> }> {
307
318
  const stats = new Map<string, { blockers: Set<string>; blocked: Set<string> }>();
308
319
  const entry = (id: string) => {
@@ -318,6 +329,7 @@ function blockerStats(rels: GraphRelationship[]): Map<string, { blockers: Set<st
318
329
  return stats;
319
330
  }
320
331
 
332
+ /** Applies the current filters (dep mode, focus neighborhood, node kind, status, text query, relationship type, and direction) to a graph, returning the surviving nodes, relationships, and the set of all connected node ids. */
321
333
  function visibleGraph(graph: ProjectGraph): { nodes: GraphNode[]; rels: GraphRelationship[]; connected: Set<string> } {
322
334
  const nodes = graph.nodes || [];
323
335
  let rels = graph.relationships || [];
@@ -369,6 +381,7 @@ function visibleGraph(graph: ProjectGraph): { nodes: GraphNode[]; rels: GraphRel
369
381
 
370
382
  // ── Canvas data conversion ────────────────────────────────────
371
383
 
384
+ /** Converts graph nodes into canvas display nodes, attaching each node's edge degree (computed from the relationships) so the renderer can size them. */
372
385
  function toCanvasNodes(nodes: GraphNode[], rels: GraphRelationship[]): CanvasNode[] {
373
386
  const deg = degreeMap(rels);
374
387
  return nodes.map((n) => ({
@@ -392,6 +405,7 @@ function toCanvasEdges(rels: GraphRelationship[]): CanvasEdge[] {
392
405
 
393
406
  // ── Info panel rendering ──────────────────────────────────────
394
407
 
408
+ /** Renders the graph-coverage HTML: a linked-item percentage bar, the external cross-project reference count, and the top relationship type with its edge count. */
395
409
  function renderCoverage(
396
410
  itemNodes: GraphNode[],
397
411
  rels: GraphRelationship[],
@@ -429,6 +443,7 @@ function renderCoverage(
429
443
  </div>`;
430
444
  }
431
445
 
446
+ /** Renders the selected-node HTML panel, including metadata, action buttons, blocker counts, the property grid, tags, an optional description, and its direct relationships. */
432
447
  function renderSelectedNode(
433
448
  node: GraphNode | undefined,
434
449
  rels: GraphRelationship[],
@@ -502,6 +517,7 @@ function renderSelectedNode(
502
517
  </div>`;
503
518
  }
504
519
 
520
+ /** Renders the neighborhood HTML for the selected node, showing clickable one-hop and two-hop neighbor chips. */
505
521
  function renderPaths(
506
522
  node: GraphNode | undefined,
507
523
  rels: GraphRelationship[],
@@ -525,6 +541,7 @@ function renderPaths(
525
541
  <div class="graph-path-section"><div class="graph-path-label">Two hops (${twoHop.size})</div><div class="graph-path-chips">${chips(twoHop) || '<span>None.</span>'}</div></div>`;
526
542
  }
527
543
 
544
+ /** Renders the item-hubs HTML, listing the top eight most-connected items by degree with their outgoing and incoming edge counts. */
528
545
  function renderHubs(nodes: GraphNode[], rels: GraphRelationship[]): string {
529
546
  const deg = degreeMap(rels);
530
547
  const hubs = nodes.filter(isItemNode)
@@ -541,6 +558,7 @@ function renderHubs(nodes: GraphNode[], rels: GraphRelationship[]): string {
541
558
  </button>`).join('');
542
559
  }
543
560
 
561
+ /** Renders the dependency-blocker HTML, listing the top eight items that block or are blocked by the most other items and flagging those on the critical path. */
544
562
  function renderBlockingInsights(nodes: GraphNode[], rels: GraphRelationship[]): string {
545
563
  const byId = new Map(nodes.map((n) => [n.id, n]));
546
564
  const stats = blockerStats(rels);
@@ -558,6 +576,7 @@ function renderBlockingInsights(nodes: GraphNode[], rels: GraphRelationship[]):
558
576
  </button>`).join('');
559
577
  }
560
578
 
579
+ /** Renders the dependency-chains HTML: the longest dependency chains, potential cycle warnings, and root blockers, with critical-path highlighting. */
561
580
  function renderDependencyChains(nodes: GraphNode[], rels: GraphRelationship[]): string {
562
581
  const byId = new Map(nodes.map((n) => [n.id, n]));
563
582
  const depRels = rels.filter(isDependencyRel);
@@ -581,6 +600,7 @@ function renderDependencyChains(nodes: GraphNode[], rels: GraphRelationship[]):
581
600
  .sort((a, b) => b.blocked - a.blocked || nodeTitle(byId.get(a.id) || { id: a.id }).localeCompare(nodeTitle(byId.get(b.id) || { id: b.id })))
582
601
  .slice(0, 4);
583
602
 
603
+ /** Depth-first trace of a dependency chain from the given id, recording detected cycle starts and returning the longest chain of blockers found. */
584
604
  function traceFrom(id: string, seen = new Set<string>()): string[] {
585
605
  if (seen.has(id)) {
586
606
  cycleStarts.add(id);
@@ -631,6 +651,7 @@ function renderDependencyChains(nodes: GraphNode[], rels: GraphRelationship[]):
631
651
  </div>`;
632
652
  }
633
653
 
654
+ /** Renders the full graph-analysis info panel HTML by composing the coverage, selected node, neighborhood, hubs, blockers, dependency-chain, and node/relationship count sections. */
634
655
  function renderInfoPanel(data: GraphResponse, fullItem?: Record<string, unknown>): string {
635
656
  const graph = data.graph || {};
636
657
  const nodes = graph.nodes || [];
@@ -670,6 +691,7 @@ function renderInfoPanel(data: GraphResponse, fullItem?: Record<string, unknown>
670
691
 
671
692
  // ── Rel list (inside bottom drawer) ──────────────────────────
672
693
 
694
+ /** Renders the relationship list shown in the bottom drawer: add/remove dependency buttons and up to 100 filtered relationship rows, each linking its two endpoints. */
673
695
  function renderRelList(data: GraphResponse): string {
674
696
  const graph = data.graph || {};
675
697
  const nodes = graph.nodes || [];
@@ -708,6 +730,7 @@ function renderRelList(data: GraphResponse): string {
708
730
 
709
731
  // ── Immersive shell ───────────────────────────────────────────
710
732
 
733
+ /** Returns whether the given preset id (knowledge, dependency, unlinked, metadata, critical, or tags) matches the current filter configuration. */
711
734
  function graphPresetActive(id: string): boolean {
712
735
  if (id === 'knowledge') {
713
736
  return !filter.depMode && (filter.kind === 'items' || filter.kind === 'all') && filter.rel === 'all' && filter.scope === 'all' && filter.colorMode === 'status';
@@ -720,6 +743,7 @@ function graphPresetActive(id: string): boolean {
720
743
  return false;
721
744
  }
722
745
 
746
+ /** Renders the preset rail toolbar HTML, with one button per view preset (Knowledge, Dependencies, Unlinked, Tags, Metadata, Critical) showing live counts and active state. */
723
747
  function renderGraphPresets(depRels: GraphRelationship[], isolatedCount: number): string {
724
748
  const allNodes = currentGraph?.graph?.nodes ?? [];
725
749
  const tagSet = new Set<string>();
@@ -745,6 +769,7 @@ function renderGraphPresets(depRels: GraphRelationship[], isolatedCount: number)
745
769
  </div>`;
746
770
  }
747
771
 
772
+ /** Renders the full immersive graph shell HTML: the canvas host, the top HUD bar with search and presets, physics controls, filter overlay, legend, info drawer, and relationship drawer. */
748
773
  function renderGraphShell(data: GraphResponse): string {
749
774
  const graph = data.graph || {};
750
775
  const nodes = graph.nodes || [];
@@ -945,6 +970,7 @@ function renderGraphShell(data: GraphResponse): string {
945
970
 
946
971
  // ── Canvas init / update ──────────────────────────────────────
947
972
 
973
+ /** Fetches the full item record for the selected item-lane node and re-renders the info panel with its description; clears the cached item for non-item nodes or on failure. */
948
974
  async function fetchAndUpdateSelectedItem(nodeId: string): Promise<void> {
949
975
  if (!state.currentProject || !nodeId) return;
950
976
  // Only fetch for item-lane nodes (not facets)
@@ -968,6 +994,7 @@ async function fetchAndUpdateSelectedItem(nodeId: string): Promise<void> {
968
994
  }
969
995
  }
970
996
 
997
+ /** Pushes the current filters and selection onto the canvas and, when searching with no node selected, jumps the camera to the first matching node. */
971
998
  function syncCanvas(): void {
972
999
  if (!canvasRef.current || !currentGraph) return;
973
1000
  const graph = currentGraph.graph || {};
@@ -994,6 +1021,7 @@ function syncCanvas(): void {
994
1021
  }
995
1022
  }
996
1023
 
1024
+ /** Creates the GraphCanvas instance for the current graph, wires its selection, open, and context-menu callbacks, computes the critical path, loads the data, and applies the initial filter. */
997
1025
  function initCanvas(): void {
998
1026
  const host = document.getElementById('graph-canvas-host') as HTMLElement | null;
999
1027
  if (!host || !currentGraph) return;
@@ -1036,6 +1064,7 @@ function initCanvas(): void {
1036
1064
 
1037
1065
  // ── Panel / drawer updates ────────────────────────────────────
1038
1066
 
1067
+ /** Re-renders the info panel and relationship list into the DOM from the current graph (and cached selected item) and re-binds their event handlers. */
1039
1068
  function updateInfoPanel(): void {
1040
1069
  if (!currentGraph) return;
1041
1070
  const panel = document.getElementById('graph-info-panel');
@@ -1045,6 +1074,7 @@ function updateInfoPanel(): void {
1045
1074
  bindInfoPanelEvents();
1046
1075
  }
1047
1076
 
1077
+ /** Synchronizes the filter toolbar DOM (direction select enabled state, depth slider and label, scope button text, and preset active states) with the current selection and filter. */
1048
1078
  function updateFilterToolbarState(): void {
1049
1079
  const dirSel = document.getElementById('graph-filter-direction') as HTMLSelectElement | null;
1050
1080
  const depthSldr = document.getElementById('graph-filter-depth') as HTMLInputElement | null;
@@ -1067,6 +1097,7 @@ const TYPE_COLORS_MAP: Record<string, string> = {
1067
1097
  };
1068
1098
  const TAG_PALETTE_JS = ['#2dd4bf','#60a5fa','#a78bfa','#f87171','#fbbf24','#34d399','#fb923c','#e879f9'];
1069
1099
 
1100
+ /** Computes a tag-to-color map by ranking tags by how often they appear across the nodes and assigning each of the most frequent ones a palette color. */
1070
1101
  function computeTagColorMap(nodes: GraphNode[]): Map<string, string> {
1071
1102
  const freq = new Map<string, number>();
1072
1103
  for (const n of nodes) {
@@ -1077,6 +1108,7 @@ function computeTagColorMap(nodes: GraphNode[]): Map<string, string> {
1077
1108
  return new Map(top.map((t, i) => [t, TAG_PALETTE_JS[i]]));
1078
1109
  }
1079
1110
 
1111
+ /** Rebuilds the legend HUD HTML to match the current color mode (status, type, or tag) or dependency mode, including clickable tag chips when coloring by tag. */
1080
1112
  function updateLegend(): void {
1081
1113
  const legend = document.getElementById('graph-legend-hud');
1082
1114
  if (!legend) return;
@@ -1150,6 +1182,7 @@ function updateLegend(): void {
1150
1182
 
1151
1183
  // ── Event bindings ────────────────────────────────────────────
1152
1184
 
1185
+ /** Binds click handlers for the info-panel controls (open and clear the selected item, add and remove dependency) and for neighbor, tag, and relationship rows so they update the selection and canvas. */
1153
1186
  function bindInfoPanelEvents(): void {
1154
1187
  document.getElementById('graph-open-selected')?.addEventListener('click', () => {
1155
1188
  if (selectedNodeId) window.__app?.openItemDetail(selectedNodeId);
@@ -1206,6 +1239,7 @@ function bindInfoPanelEvents(): void {
1206
1239
  });
1207
1240
  }
1208
1241
 
1242
+ /** Binds all HUD control handlers for the graph view: back/refresh/sync/fit buttons, presets, physics toggles and sliders, filter selects, search input, drawer toggles, color mode, and global keyboard shortcuts. */
1209
1243
  function bindHudEvents(): void {
1210
1244
  // Back button
1211
1245
  document.getElementById('graph-back-btn')?.addEventListener('click', () => {
@@ -1543,6 +1577,7 @@ function bindHudEvents(): void {
1543
1577
 
1544
1578
  // ── URL routing (pushState) ─────────────────────────────────
1545
1579
 
1580
+ /** Serializes the current selection and filter into URL query parameters and replaces the browser history entry so the view can be restored or shared. */
1546
1581
  function pushGraphState(): void {
1547
1582
  if (!state.currentProject) return;
1548
1583
  const params = new URLSearchParams();
@@ -1561,6 +1596,7 @@ function pushGraphState(): void {
1561
1596
 
1562
1597
  let urlStateRestored = false;
1563
1598
 
1599
+ /** Reads graph view parameters from the current URL query string and applies them to the selection and filter, marking state as restored. */
1564
1600
  function restoreGraphState(): void {
1565
1601
  const params = new URLSearchParams(window.location.search);
1566
1602
  if (!params.has('graph')) return;
@@ -1575,6 +1611,7 @@ function restoreGraphState(): void {
1575
1611
 
1576
1612
  // ── Dependency editing modals ────────────────────────────────
1577
1613
 
1614
+ /** Shows the add-dependency modal listing item nodes as source and target selects plus a relationship type, and POSTs the new dependency then refreshes the graph on submit. */
1578
1615
  function showAddDependencyModal(): void {
1579
1616
  if (!state.currentProject || !currentGraph) return;
1580
1617
  const graph = currentGraph.graph || {};
@@ -1647,6 +1684,7 @@ function showAddDependencyModal(): void {
1647
1684
  });
1648
1685
  }
1649
1686
 
1687
+ /** Shows the remove-dependency modal listing existing dependency relationships and DELETEs the chosen one on submit, then refreshes the graph. */
1650
1688
  function showRemoveDependencyModal(): void {
1651
1689
  if (!state.currentProject || !currentGraph) return;
1652
1690
  const graph = currentGraph.graph || {};
@@ -1713,6 +1751,7 @@ function showRemoveDependencyModal(): void {
1713
1751
 
1714
1752
  // ── Main entry point ──────────────────────────────────────────
1715
1753
 
1754
+ /** Renders the full graph view into the `content-graph` element: clears any prior canvas, restores URL state, fetches the project graph, computes the critical path, renders the shell, binds events, and initializes the canvas. */
1716
1755
  export async function renderGraphView(): Promise<void> {
1717
1756
  const el = document.getElementById('content-graph');
1718
1757
  if (!el) return;
@@ -1785,7 +1824,7 @@ export async function refreshGraphData(): Promise<void> {
1785
1824
  updateInfoPanel();
1786
1825
  syncCanvas();
1787
1826
  } catch {
1788
- // Silently ignore — user can hit Refresh manually
1827
+ // Silently ignore — user can hit Refresh manually
1789
1828
  }
1790
1829
  }
1791
1830
 
@@ -1794,11 +1833,13 @@ export async function refreshGraphData(): Promise<void> {
1794
1833
  // Registry of active local graph canvases so they can be cleaned up
1795
1834
  const localGraphRegistry = new Map<string, GraphCanvas>();
1796
1835
 
1836
+ /** Destroys and unregisters the local mini-graph canvas previously created for the given container id, if any. */
1797
1837
  export function destroyLocalGraph(containerId: string): void {
1798
1838
  const existing = localGraphRegistry.get(containerId);
1799
1839
  if (existing) { existing.destroy(); localGraphRegistry.delete(containerId); }
1800
1840
  }
1801
1841
 
1842
+ /** Renders an embedded mini-graph for a single item showing its neighborhood out to the given depth, with neighbor-node clicks navigating to that item's detail view. */
1802
1843
  export async function renderLocalGraph(
1803
1844
  containerId: string,
1804
1845
  nodeId: string,
@@ -2,6 +2,10 @@ import { api } from '../api.js';
2
2
  import { escHtml } from '../utils.js';
3
3
  import { showModal, hideModal, createModal, confirmDialog } from '../components/modals.js';
4
4
  import { toast } from '../components/toast.js';
5
+ /**
6
+ * Renders the groups page header and a loading placeholder, then fetches and
7
+ * displays the list of teams.
8
+ */
5
9
  export async function renderGroupsView() {
6
10
  const el = document.getElementById('content-groups');
7
11
  if (!el)
@@ -17,6 +21,11 @@ export async function renderGroupsView() {
17
21
  <div id="groups-list"><div class="loading-state"><div class="loading-spinner"></div></div></div>`;
18
22
  await loadGroups();
19
23
  }
24
+ /**
25
+ * Fetches the user's teams from the API and renders each as a clickable row
26
+ * showing avatar initials, name, description, and member count, or an empty
27
+ * state when none exist.
28
+ */
20
29
  async function loadGroups() {
21
30
  const el = document.getElementById('groups-list');
22
31
  if (!el)
@@ -53,6 +62,10 @@ async function loadGroups() {
53
62
  el.innerHTML = `<div class="empty-state"><div class="empty-state-text">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
54
63
  }
55
64
  }
65
+ /**
66
+ * Builds and displays the modal form for creating a new team, with name and
67
+ * description inputs and a validation slot.
68
+ */
56
69
  export function openCreateGroupModal() {
57
70
  createModal('create-group-modal', 'New Group', `
58
71
  <div class="form-group">
@@ -67,6 +80,10 @@ export function openCreateGroupModal() {
67
80
  <button class="btn btn-primary" onclick="window.__app.submitCreateGroup()"><span>Create Group</span></button>`);
68
81
  showModal('create-group-modal');
69
82
  }
83
+ /**
84
+ * Reads the create-team form, requires a name, posts the new team, and
85
+ * reloads the list on success, surfacing validation and server errors inline.
86
+ */
70
87
  export async function submitCreateGroup() {
71
88
  const name = document.getElementById('cg-name')?.value?.trim() || '';
72
89
  const description = document.getElementById('cg-desc')?.value?.trim() || '';
@@ -93,6 +110,13 @@ export async function submitCreateGroup() {
93
110
  }
94
111
  }
95
112
  }
113
+ /**
114
+ * Confirms and permanently deletes the named team, then refreshes the list,
115
+ * showing any errors as toasts.
116
+ *
117
+ * @param groupId - identifier of the team to delete.
118
+ * @param name - display name shown in the confirmation prompt.
119
+ */
96
120
  export function deleteGroup(groupId, name) {
97
121
  confirmDialog('Delete Group?', `Delete group "${name}"? This cannot be undone.`, async () => {
98
122
  try {
@@ -105,6 +129,14 @@ export function deleteGroup(groupId, name) {
105
129
  }
106
130
  }, true);
107
131
  }
132
+ /**
133
+ * Opens a modal showing a team's members with an invite-by-email field,
134
+ * fetching the membership from the API and replacing the loading placeholder
135
+ * with the rendered list.
136
+ *
137
+ * @param groupId - identifier of the team to display.
138
+ * @param _groupName - fallback title used before the API response resolves.
139
+ */
108
140
  export async function openGroupDetail(groupId, _groupName) {
109
141
  createModal('group-detail-modal', _groupName, `<div class="loading-state"><div class="loading-spinner"></div></div>`, '', true);
110
142
  showModal('group-detail-modal');
@@ -153,6 +185,12 @@ export async function openGroupDetail(groupId, _groupName) {
153
185
  bodyEl.innerHTML = `<div class="empty-state"><div class="empty-state-text">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
154
186
  }
155
187
  }
188
+ /**
189
+ * Reads the invite field for the given team, requires an email, adds the
190
+ * member through the API, and reopens the team detail on success.
191
+ *
192
+ * @param groupId - identifier of the team to invite into.
193
+ */
156
194
  export async function inviteMember(groupId) {
157
195
  const emailEl = document.getElementById(`invite-email-${groupId}`);
158
196
  const email = emailEl?.value?.trim() || '';
@@ -171,6 +209,13 @@ export async function inviteMember(groupId) {
171
209
  toast(err instanceof Error ? err.message : String(err), 'error');
172
210
  }
173
211
  }
212
+ /**
213
+ * Confirms and removes a member from the specified team, then reopens the
214
+ * team detail and surfaces errors as toasts.
215
+ *
216
+ * @param groupId - identifier of the team to modify.
217
+ * @param userId - identifier of the member to remove.
218
+ */
174
219
  export function removeMember(groupId, userId) {
175
220
  confirmDialog('Remove Member?', 'Remove this member from the group?', async () => {
176
221
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"groups.js","sourceRoot":"","sources":["groups.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"groups.js","sourceRoot":"","sources":["groups.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,10 @@ import { showModal, hideModal, createModal, confirmDialog } from '../components/
8
8
  import { toast } from '../components/toast.js';
9
9
  import type { GroupDetailResponse, GroupMemberRow, GroupsResponse, GroupListRow } from '../api-types.js';
10
10
 
11
+ /**
12
+ * Renders the groups page header and a loading placeholder, then fetches and
13
+ * displays the list of teams.
14
+ */
11
15
  export async function renderGroupsView(): Promise<void> {
12
16
  const el = document.getElementById('content-groups');
13
17
  if (!el) return;
@@ -23,6 +27,11 @@ export async function renderGroupsView(): Promise<void> {
23
27
  await loadGroups();
24
28
  }
25
29
 
30
+ /**
31
+ * Fetches the user's teams from the API and renders each as a clickable row
32
+ * showing avatar initials, name, description, and member count, or an empty
33
+ * state when none exist.
34
+ */
26
35
  async function loadGroups(): Promise<void> {
27
36
  const el = document.getElementById('groups-list');
28
37
  if (!el) return;
@@ -57,6 +66,10 @@ async function loadGroups(): Promise<void> {
57
66
  }
58
67
  }
59
68
 
69
+ /**
70
+ * Builds and displays the modal form for creating a new team, with name and
71
+ * description inputs and a validation slot.
72
+ */
60
73
  export function openCreateGroupModal(): void {
61
74
  createModal('create-group-modal','New Group',`
62
75
  <div class="form-group">
@@ -74,6 +87,10 @@ export function openCreateGroupModal(): void {
74
87
  showModal('create-group-modal');
75
88
  }
76
89
 
90
+ /**
91
+ * Reads the create-team form, requires a name, posts the new team, and
92
+ * reloads the list on success, surfacing validation and server errors inline.
93
+ */
77
94
  export async function submitCreateGroup(): Promise<void> {
78
95
  const name = (document.getElementById('cg-name') as HTMLInputElement | null)?.value?.trim() || '';
79
96
  const description = (document.getElementById('cg-desc') as HTMLInputElement | null)?.value?.trim() || '';
@@ -90,6 +107,13 @@ export async function submitCreateGroup(): Promise<void> {
90
107
  }
91
108
  }
92
109
 
110
+ /**
111
+ * Confirms and permanently deletes the named team, then refreshes the list,
112
+ * showing any errors as toasts.
113
+ *
114
+ * @param groupId - identifier of the team to delete.
115
+ * @param name - display name shown in the confirmation prompt.
116
+ */
93
117
  export function deleteGroup(groupId: string, name: string): void {
94
118
  confirmDialog('Delete Group?', `Delete group "${name}"? This cannot be undone.`, async () => {
95
119
  try {
@@ -100,6 +124,14 @@ export function deleteGroup(groupId: string, name: string): void {
100
124
  }, true);
101
125
  }
102
126
 
127
+ /**
128
+ * Opens a modal showing a team's members with an invite-by-email field,
129
+ * fetching the membership from the API and replacing the loading placeholder
130
+ * with the rendered list.
131
+ *
132
+ * @param groupId - identifier of the team to display.
133
+ * @param _groupName - fallback title used before the API response resolves.
134
+ */
103
135
  export async function openGroupDetail(groupId: string, _groupName: string): Promise<void> {
104
136
  createModal('group-detail-modal', _groupName,
105
137
  `<div class="loading-state"><div class="loading-spinner"></div></div>`, '', true);
@@ -150,6 +182,12 @@ export async function openGroupDetail(groupId: string, _groupName: string): Prom
150
182
  }
151
183
  }
152
184
 
185
+ /**
186
+ * Reads the invite field for the given team, requires an email, adds the
187
+ * member through the API, and reopens the team detail on success.
188
+ *
189
+ * @param groupId - identifier of the team to invite into.
190
+ */
153
191
  export async function inviteMember(groupId: string): Promise<void> {
154
192
  const emailEl = document.getElementById(`invite-email-${groupId}`) as HTMLInputElement | null;
155
193
  const email = emailEl?.value?.trim() || '';
@@ -162,6 +200,13 @@ export async function inviteMember(groupId: string): Promise<void> {
162
200
  } catch(err: unknown) { toast(err instanceof Error ? err.message : String(err),'error'); }
163
201
  }
164
202
 
203
+ /**
204
+ * Confirms and removes a member from the specified team, then reopens the
205
+ * team detail and surfaces errors as toasts.
206
+ *
207
+ * @param groupId - identifier of the team to modify.
208
+ * @param userId - identifier of the member to remove.
209
+ */
165
210
  export function removeMember(groupId: string, userId: string): void {
166
211
  confirmDialog('Remove Member?', 'Remove this member from the group?', async () => {
167
212
  try {
@@ -18,6 +18,7 @@ const TOPIC_ICONS = {
18
18
  function topicIcon(id) {
19
19
  return TOPIC_ICONS[id] ?? '📖';
20
20
  }
21
+ /** Renders the grid of clickable guide topic cards, each showing its icon, title, and summary or intent. */
21
22
  function renderTopicCards(topics) {
22
23
  if (topics.length === 0) {
23
24
  return '<div class="empty-state"><div class="empty-state-text">No guide topics found.</div></div>';
@@ -31,6 +32,7 @@ function renderTopicCards(topics) {
31
32
  </div>
32
33
  </div>`).join('')}</div>`;
33
34
  }
35
+ /** Renders the detail markup for a single guide topic: a back link, title and intent, summary, quick commands with copy buttons, workflows, and related-topic links. */
34
36
  function renderTopicDetail(topic) {
35
37
  const commands = topic.commands ?? topic.quick_commands ?? [];
36
38
  const related = topic.related ?? [];
@@ -93,6 +95,7 @@ function renderTopicDetail(topic) {
93
95
  </div>
94
96
  </div>` : ''}`;
95
97
  }
98
+ /** Renders the guide view for the current project: a list of topic cards when no topic id is given, or a single topic's detail (fetched from the guide topic endpoint) when one is. */
96
99
  export async function renderGuideView(topicId) {
97
100
  const el = document.getElementById('content-guide');
98
101
  if (!el)
@@ -1 +1 @@
1
- {"version":3,"file":"guide.js","sourceRoot":"","sources":["guide.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"guide.js","sourceRoot":"","sources":["guide.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,7 @@ function topicIcon(id: string): string {
21
21
  return TOPIC_ICONS[id] ?? '📖';
22
22
  }
23
23
 
24
+ /** Renders the grid of clickable guide topic cards, each showing its icon, title, and summary or intent. */
24
25
  function renderTopicCards(topics: Array<{id: string; title: string; summary?: string; intent?: string}>): string {
25
26
  if (topics.length === 0) {
26
27
  return '<div class="empty-state"><div class="empty-state-text">No guide topics found.</div></div>';
@@ -35,6 +36,7 @@ function renderTopicCards(topics: Array<{id: string; title: string; summary?: st
35
36
  </div>`).join('')}</div>`;
36
37
  }
37
38
 
39
+ /** Renders the detail markup for a single guide topic: a back link, title and intent, summary, quick commands with copy buttons, workflows, and related-topic links. */
38
40
  function renderTopicDetail(topic: {
39
41
  id: string;
40
42
  title: string;
@@ -108,6 +110,7 @@ function renderTopicDetail(topic: {
108
110
  </div>` : ''}`;
109
111
  }
110
112
 
113
+ /** Renders the guide view for the current project: a list of topic cards when no topic id is given, or a single topic's detail (fetched from the guide topic endpoint) when one is. */
111
114
  export async function renderGuideView(topicId?: string): Promise<void> {
112
115
  const el = document.getElementById('content-guide');
113
116
  if (!el) return;
@@ -15,6 +15,7 @@ function isHistoryDriftIssue(issue) {
15
15
  const msg = (issue.message || issue.description || issue.type || '').toLowerCase();
16
16
  return msg.includes('history') && (msg.includes('drift') || msg.includes('repair') || msg.includes('mismatch'));
17
17
  }
18
+ /** 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. */
18
19
  function renderIssueRow(issue, projectId) {
19
20
  const msg = escHtml(issue.message || issue.description || String(issue));
20
21
  if (isHistoryDriftIssue(issue)) {
@@ -28,6 +29,7 @@ function renderIssueRow(issue, projectId) {
28
29
  }
29
30
  return `<div class="health-issue-item">⚠ ${msg}</div>`;
30
31
  }
32
+ /** 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. */
31
33
  export async function repairItemHistory(projectId, itemId, dryRun) {
32
34
  try {
33
35
  const data = await api('POST', `/projects/${projectId}/pm/items/${encodeURIComponent(itemId)}/history-repair`, { dryRun });
@@ -56,6 +58,7 @@ export async function repairItemHistory(projectId, itemId, dryRun) {
56
58
  toast(`Repair failed: ${err instanceof Error ? err.message : String(err)}`, 'error');
57
59
  }
58
60
  }
61
+ /** 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. */
59
62
  export async function renderHealthView() {
60
63
  const el = document.getElementById('content-health');
61
64
  if (!el)