@unbrained/pm-web 2026.9.18 → 2026.9.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +1 -4
- package/dist/app.d.ts +10 -0
- package/dist/app.js +19 -9
- package/dist/app.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/routes/admin.js +6 -6
- package/dist/routes/admin.js.map +1 -1
- package/dist/routes/auth.js +2 -2
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/extensions.js +24 -37
- package/dist/routes/extensions.js.map +1 -1
- package/dist/routes/github.js +169 -107
- package/dist/routes/github.js.map +1 -1
- package/dist/routes/groups.js +9 -14
- package/dist/routes/groups.js.map +1 -1
- package/dist/routes/pm.js +532 -1015
- package/dist/routes/pm.js.map +1 -1
- package/dist/routes/projects.js +10 -6
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/route-helpers.d.ts +74 -0
- package/dist/routes/route-helpers.js +85 -0
- package/dist/routes/route-helpers.js.map +1 -0
- package/dist/routes/sharing.js +30 -18
- package/dist/routes/sharing.js.map +1 -1
- package/dist/server.js +1 -20
- package/dist/server.js.map +1 -1
- package/dist/services/mutation-event-watcher.js +3 -28
- package/dist/services/mutation-event-watcher.js.map +1 -1
- package/dist/services/pm-runner.js +3 -1
- package/dist/services/pm-runner.js.map +1 -1
- package/dist/services/project-watcher.js +14 -38
- package/dist/services/project-watcher.js.map +1 -1
- package/dist/services/watcher-utils.d.ts +42 -0
- package/dist/services/watcher-utils.js +60 -0
- package/dist/services/watcher-utils.js.map +1 -0
- package/manifest.json +2 -2
- package/package.json +17 -6
- package/public/src/app.js +22 -52
- package/public/src/app.js.map +1 -1
- package/public/src/app.ts +18 -75
- package/public/src/browser-window.js +29 -0
- package/public/src/browser-window.js.map +1 -0
- package/public/src/browser-window.ts +56 -0
- package/public/src/components/modals.js +1 -1
- package/public/src/components/modals.js.map +1 -1
- package/public/src/components/modals.ts +1 -1
- package/public/src/i18n.js +14 -6
- package/public/src/i18n.js.map +1 -1
- package/public/src/i18n.ts +16 -6
- package/public/src/sw.ts +1 -0
- package/public/src/utils.js +58 -0
- package/public/src/utils.js.map +1 -1
- package/public/src/utils.ts +53 -0
- package/public/src/views/admin.js +2 -5
- package/public/src/views/admin.js.map +1 -1
- package/public/src/views/admin.ts +2 -2
- package/public/src/views/auth.js +5 -1
- package/public/src/views/auth.js.map +1 -1
- package/public/src/views/auth.ts +6 -1
- package/public/src/views/calendar.js +1 -2
- package/public/src/views/calendar.js.map +1 -1
- package/public/src/views/calendar.ts +1 -2
- package/public/src/views/config.js +14 -21
- package/public/src/views/config.js.map +1 -1
- package/public/src/views/config.ts +14 -19
- package/public/src/views/context.js +11 -12
- package/public/src/views/context.js.map +1 -1
- package/public/src/views/context.ts +13 -12
- package/public/src/views/create.js +3 -11
- package/public/src/views/create.js.map +1 -1
- package/public/src/views/create.ts +3 -3
- package/public/src/views/export.js +2 -3
- package/public/src/views/export.js.map +1 -1
- package/public/src/views/export.ts +2 -3
- package/public/src/views/github.js +33 -47
- package/public/src/views/github.js.map +1 -1
- package/public/src/views/github.ts +35 -28
- package/public/src/views/graph-canvas.js +92 -99
- package/public/src/views/graph-canvas.js.map +1 -1
- package/public/src/views/graph-canvas.ts +94 -93
- package/public/src/views/graph.js +88 -73
- package/public/src/views/graph.js.map +1 -1
- package/public/src/views/graph.ts +97 -63
- package/public/src/views/groups.js +2 -5
- package/public/src/views/groups.js.map +1 -1
- package/public/src/views/groups.ts +2 -2
- package/public/src/views/guide.js +11 -10
- package/public/src/views/guide.js.map +1 -1
- package/public/src/views/guide.ts +12 -10
- package/public/src/views/items.js +100 -130
- package/public/src/views/items.js.map +1 -1
- package/public/src/views/items.ts +99 -117
- package/public/src/views/normalize.js +2 -9
- package/public/src/views/normalize.js.map +1 -1
- package/public/src/views/normalize.ts +2 -9
- package/public/src/views/packages.js +7 -4
- package/public/src/views/packages.js.map +1 -1
- package/public/src/views/packages.ts +8 -4
- package/public/src/views/plan-execution.js.map +1 -1
- package/public/src/views/plan-execution.ts +5 -15
- package/public/src/views/plan.js +2 -1
- package/public/src/views/plan.js.map +1 -1
- package/public/src/views/plan.ts +6 -17
- package/public/src/views/projects.js +5 -6
- package/public/src/views/projects.js.map +1 -1
- package/public/src/views/projects.ts +5 -6
- package/public/src/views/search.js +5 -1
- package/public/src/views/search.js.map +1 -1
- package/public/src/views/search.ts +7 -1
- package/public/src/views/settings.js +30 -55
- package/public/src/views/settings.js.map +1 -1
- package/public/src/views/settings.ts +23 -17
- package/public/src/views/shared.js +2 -5
- package/public/src/views/shared.js.map +1 -1
- package/public/src/views/shared.ts +2 -5
- package/public/src/views/sharing.js +2 -5
- package/public/src/views/sharing.js.map +1 -1
- package/public/src/views/sharing.ts +2 -2
- package/public/src/views/templates.js +9 -16
- package/public/src/views/templates.js.map +1 -1
- package/public/src/views/templates.ts +9 -14
- package/public/src/views/validate.js +2 -9
- package/public/src/views/validate.js.map +1 -1
- package/public/src/views/validate.ts +2 -9
- package/public/sw.js +1 -0
|
@@ -6,7 +6,8 @@ import { state } from '../state.js';
|
|
|
6
6
|
import type { GraphNode, GraphRelationship, ProjectGraph } from '../types.js';
|
|
7
7
|
import { escHtml } from '../utils.js';
|
|
8
8
|
import { toast } from '../components/toast.js';
|
|
9
|
-
import {
|
|
9
|
+
import { browserWindow } from '../browser-window.js';
|
|
10
|
+
import { GraphCanvas, type CanvasNode, type CanvasEdge, type LayoutMode, tagColorMapFromFreq } from './graph-canvas.js';
|
|
10
11
|
|
|
11
12
|
type GraphResponse = {
|
|
12
13
|
graph?: ProjectGraph;
|
|
@@ -34,6 +35,11 @@ let selectedNodeId = '';
|
|
|
34
35
|
const canvasRef: { current: GraphCanvas | null } = { current: null };
|
|
35
36
|
let physicsLabel = 'Pause Physics';
|
|
36
37
|
let graphSyncInFlight = false;
|
|
38
|
+
|
|
39
|
+
/** Record whether a graph sync is running, outside the async sync function. */
|
|
40
|
+
function setGraphSyncInFlight(active: boolean): void {
|
|
41
|
+
graphSyncInFlight = active;
|
|
42
|
+
}
|
|
37
43
|
let infoDrawerOpen = false;
|
|
38
44
|
let relDrawerOpen = false;
|
|
39
45
|
let filterOpen = false;
|
|
@@ -98,7 +104,7 @@ function showCtxMenu(nodeId: string, x: number, y: number): void {
|
|
|
98
104
|
};
|
|
99
105
|
|
|
100
106
|
if (isItem) {
|
|
101
|
-
menu.appendChild(btn('⊡', 'Open Item', () =>
|
|
107
|
+
menu.appendChild(btn('⊡', 'Open Item', () => browserWindow().__app?.openItemDetail(nodeId)));
|
|
102
108
|
const sep1 = document.createElement('div'); sep1.className = 'graph-ctx-sep'; menu.appendChild(sep1);
|
|
103
109
|
}
|
|
104
110
|
menu.appendChild(btn('⊙', 'Select & Focus', () => {
|
|
@@ -333,7 +339,7 @@ function blockerStats(rels: GraphRelationship[]): Map<string, { blockers: Set<st
|
|
|
333
339
|
function visibleGraph(graph: ProjectGraph): { nodes: GraphNode[]; rels: GraphRelationship[]; connected: Set<string> } {
|
|
334
340
|
const nodes = graph.nodes || [];
|
|
335
341
|
let rels = graph.relationships || [];
|
|
336
|
-
const connected =
|
|
342
|
+
const connected = connectedNodeIds(rels);
|
|
337
343
|
|
|
338
344
|
// Dep mode: restrict to dependency/block edges
|
|
339
345
|
if (filter.depMode) {
|
|
@@ -379,24 +385,78 @@ function visibleGraph(graph: ProjectGraph): { nodes: GraphNode[]; rels: GraphRel
|
|
|
379
385
|
return { nodes: visNodes, rels: visRels, connected };
|
|
380
386
|
}
|
|
381
387
|
|
|
382
|
-
// ──
|
|
388
|
+
// ── Shared graph helpers ─────────────────────────────────────
|
|
383
389
|
|
|
384
|
-
/**
|
|
385
|
-
function
|
|
386
|
-
|
|
387
|
-
|
|
390
|
+
/** Returns the set of node ids that appear as either endpoint of any relationship in the given list. */
|
|
391
|
+
function connectedNodeIds(rels: GraphRelationship[]): Set<string> {
|
|
392
|
+
return new Set(rels.flatMap((r) => [r.from, r.to]));
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Counts how many times each relationship type appears, returning a type-to-count record. */
|
|
396
|
+
function countRelTypes(rels: GraphRelationship[]): Record<string, number> {
|
|
397
|
+
return rels.reduce<Record<string, number>>((acc, r) => { acc[r.type] = (acc[r.type] || 0) + 1; return acc; }, {});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** Counts how many nodes fall into each type category, returning a type-to-count record. */
|
|
401
|
+
function countNodeTypes(nodes: GraphNode[]): Record<string, number> {
|
|
402
|
+
return nodes.reduce<Record<string, number>>((acc, n) => {
|
|
403
|
+
const t = nodeType(n); acc[t] = (acc[t] || 0) + 1; return acc;
|
|
404
|
+
}, {});
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/** Converts a single graph node into a canvas display node with the given edge degree, copying id, label, type, status, lane, degree, and tags. */
|
|
408
|
+
function nodeToCanvasNode(n: GraphNode, degree: number): CanvasNode {
|
|
409
|
+
return {
|
|
388
410
|
id: n.id,
|
|
389
411
|
label: nodeTitle(n),
|
|
390
412
|
type: nodeType(n),
|
|
391
413
|
status: nodeStatus(n),
|
|
392
414
|
lane: nodeLane(n),
|
|
393
|
-
degree
|
|
415
|
+
degree,
|
|
394
416
|
tags: Array.isArray(n.properties?.tags)
|
|
395
417
|
? (n.properties.tags as unknown[]).map(String)
|
|
396
418
|
: [],
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/** Destroys the current canvas instance and re-renders the graph view from scratch. */
|
|
423
|
+
function refreshGraph(): void {
|
|
424
|
+
canvasRef.current?.destroy();
|
|
425
|
+
canvasRef.current = null;
|
|
426
|
+
void renderGraphView();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/** Displays an error message on a form-error element, extracting the message from an unknown catch value. */
|
|
430
|
+
function displayFormError(errEl: HTMLElement | null, err: unknown): void {
|
|
431
|
+
if (errEl) { errEl.textContent = err instanceof Error ? err.message : String(err); errEl.style.display = ''; }
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** Reads the four physics-slider input elements from the DOM, returning null for any that are missing. */
|
|
435
|
+
function getPhysicsInputs(): {
|
|
436
|
+
rep: HTMLInputElement | null;
|
|
437
|
+
linkDist: HTMLInputElement | null;
|
|
438
|
+
linkStr: HTMLInputElement | null;
|
|
439
|
+
gravity: HTMLInputElement | null;
|
|
440
|
+
} {
|
|
441
|
+
return {
|
|
442
|
+
rep: document.getElementById('graph-physics-repulsion') as HTMLInputElement | null,
|
|
443
|
+
linkDist: document.getElementById('graph-physics-linkdist') as HTMLInputElement | null,
|
|
444
|
+
linkStr: document.getElementById('graph-physics-linkstr') as HTMLInputElement | null,
|
|
445
|
+
gravity: document.getElementById('graph-physics-gravity') as HTMLInputElement | null,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// ── Canvas data conversion ────────────────────────────────────
|
|
450
|
+
|
|
451
|
+
/** Converts graph nodes into canvas display nodes, attaching each node's edge degree (computed from the relationships) so the renderer can size them. */
|
|
452
|
+
function toCanvasNodes(nodes: GraphNode[], rels: GraphRelationship[]): CanvasNode[] {
|
|
453
|
+
const deg = degreeMap(rels);
|
|
454
|
+
return nodes.map((n) => {
|
|
455
|
+
const cn = nodeToCanvasNode(n, deg.get(n.id) || 0);
|
|
456
|
+
cn.priority = n.properties?.priority !== undefined ? Number(n.properties.priority) : undefined;
|
|
457
|
+
cn.assignee = n.properties?.assignee ? String(n.properties.assignee) : undefined;
|
|
458
|
+
return cn;
|
|
459
|
+
});
|
|
400
460
|
}
|
|
401
461
|
|
|
402
462
|
function toCanvasEdges(rels: GraphRelationship[]): CanvasEdge[] {
|
|
@@ -657,14 +717,12 @@ function renderInfoPanel(data: GraphResponse, fullItem?: Record<string, unknown>
|
|
|
657
717
|
const nodes = graph.nodes || [];
|
|
658
718
|
const rels = graph.relationships || [];
|
|
659
719
|
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
660
|
-
const connected =
|
|
720
|
+
const connected = connectedNodeIds(rels);
|
|
661
721
|
const itemNodes = nodes.filter(isItemNode);
|
|
662
|
-
const relCounts = rels
|
|
722
|
+
const relCounts = countRelTypes(rels);
|
|
663
723
|
const selectedNode = selectedNodeId ? byId.get(selectedNodeId) : undefined;
|
|
664
724
|
|
|
665
|
-
const typeCounts = nodes
|
|
666
|
-
const t = nodeType(n); acc[t] = (acc[t] || 0) + 1; return acc;
|
|
667
|
-
}, {});
|
|
725
|
+
const typeCounts = countNodeTypes(nodes);
|
|
668
726
|
|
|
669
727
|
return `
|
|
670
728
|
<div class="graph-panel-title">Graph Coverage</div>
|
|
@@ -776,9 +834,9 @@ function renderGraphShell(data: GraphResponse): string {
|
|
|
776
834
|
const rels = graph.relationships || [];
|
|
777
835
|
const itemNodes = nodes.filter(isItemNode);
|
|
778
836
|
const facetNodes = nodes.filter(isFacetNode);
|
|
779
|
-
const connected =
|
|
837
|
+
const connected = connectedNodeIds(rels);
|
|
780
838
|
const isolated = itemNodes.filter((n) => !connected.has(n.id)).length;
|
|
781
|
-
const relCounts = rels
|
|
839
|
+
const relCounts = countRelTypes(rels);
|
|
782
840
|
const relOptions = Object.keys(relCounts).sort();
|
|
783
841
|
const errText = compactError(data.extensionError);
|
|
784
842
|
const { rels: visRels } = visibleGraph(graph);
|
|
@@ -1053,7 +1111,7 @@ function initCanvas(): void {
|
|
|
1053
1111
|
else selectedItemCache = null;
|
|
1054
1112
|
},
|
|
1055
1113
|
onOpenNode(id) {
|
|
1056
|
-
|
|
1114
|
+
browserWindow().__app?.openItemDetail(id);
|
|
1057
1115
|
},
|
|
1058
1116
|
onContextMenu(id, x, y) { showCtxMenu(id, x, y); },
|
|
1059
1117
|
});
|
|
@@ -1095,7 +1153,6 @@ const TYPE_COLORS_MAP: Record<string, string> = {
|
|
|
1095
1153
|
task:'#2dd4bf', feature:'#60a5fa', epic:'#a78bfa', bug:'#f87171',
|
|
1096
1154
|
milestone:'#fbbf24', story:'#34d399', chore:'#94a3b8', release:'#38bdf8',
|
|
1097
1155
|
};
|
|
1098
|
-
const TAG_PALETTE_JS = ['#2dd4bf','#60a5fa','#a78bfa','#f87171','#fbbf24','#34d399','#fb923c','#e879f9'];
|
|
1099
1156
|
|
|
1100
1157
|
/** 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. */
|
|
1101
1158
|
function computeTagColorMap(nodes: GraphNode[]): Map<string, string> {
|
|
@@ -1104,8 +1161,7 @@ function computeTagColorMap(nodes: GraphNode[]): Map<string, string> {
|
|
|
1104
1161
|
const tags = Array.isArray(n.properties?.tags) ? (n.properties.tags as unknown[]).map(String) : [];
|
|
1105
1162
|
for (const t of tags) freq.set(t, (freq.get(t) ?? 0) + 1);
|
|
1106
1163
|
}
|
|
1107
|
-
|
|
1108
|
-
return new Map(top.map((t, i) => [t, TAG_PALETTE_JS[i]]));
|
|
1164
|
+
return tagColorMapFromFreq(freq);
|
|
1109
1165
|
}
|
|
1110
1166
|
|
|
1111
1167
|
/** 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. */
|
|
@@ -1115,9 +1171,7 @@ function updateLegend(): void {
|
|
|
1115
1171
|
const nodes = currentGraph?.graph?.nodes ?? [];
|
|
1116
1172
|
|
|
1117
1173
|
if (filter.colorMode === 'type') {
|
|
1118
|
-
const typeCounts = nodes.filter(isItemNode)
|
|
1119
|
-
const t = nodeType(n); acc[t] = (acc[t] || 0) + 1; return acc;
|
|
1120
|
-
}, {});
|
|
1174
|
+
const typeCounts = countNodeTypes(nodes.filter(isItemNode));
|
|
1121
1175
|
const shown = Object.entries(typeCounts).sort((a, b) => b[1] - a[1]).slice(0, 6);
|
|
1122
1176
|
legend.innerHTML = `
|
|
1123
1177
|
<span><i class="legend-dot legend-facet"></i>Metadata</span>
|
|
@@ -1185,7 +1239,7 @@ function updateLegend(): void {
|
|
|
1185
1239
|
/** 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. */
|
|
1186
1240
|
function bindInfoPanelEvents(): void {
|
|
1187
1241
|
document.getElementById('graph-open-selected')?.addEventListener('click', () => {
|
|
1188
|
-
if (selectedNodeId)
|
|
1242
|
+
if (selectedNodeId) browserWindow().__app?.openItemDetail(selectedNodeId);
|
|
1189
1243
|
});
|
|
1190
1244
|
document.getElementById('graph-clear-selected')?.addEventListener('click', () => {
|
|
1191
1245
|
selectedNodeId = '';
|
|
@@ -1245,15 +1299,15 @@ function bindHudEvents(): void {
|
|
|
1245
1299
|
document.getElementById('graph-back-btn')?.addEventListener('click', () => {
|
|
1246
1300
|
removeCtxMenu();
|
|
1247
1301
|
// Remove graph keyboard handler
|
|
1248
|
-
const kh =
|
|
1249
|
-
if (kh) { document.removeEventListener('keydown', kh); delete
|
|
1250
|
-
|
|
1302
|
+
const kh = browserWindow().__graphKeyHandler;
|
|
1303
|
+
if (kh) { document.removeEventListener('keydown', kh); delete browserWindow().__graphKeyHandler; }
|
|
1304
|
+
browserWindow().__app?.showView('items');
|
|
1251
1305
|
});
|
|
1252
1306
|
|
|
1253
1307
|
const runGraphSync = async (): Promise<void> => {
|
|
1254
1308
|
if (!state.currentProject || graphSyncInFlight) return;
|
|
1255
1309
|
const syncBtn = document.getElementById('graph-sync-btn') as HTMLButtonElement | null;
|
|
1256
|
-
|
|
1310
|
+
setGraphSyncInFlight(true);
|
|
1257
1311
|
if (syncBtn) {
|
|
1258
1312
|
syncBtn.disabled = true;
|
|
1259
1313
|
syncBtn.textContent = 'Syncing…';
|
|
@@ -1265,7 +1319,7 @@ function bindHudEvents(): void {
|
|
|
1265
1319
|
} catch (err: unknown) {
|
|
1266
1320
|
toast(err instanceof Error ? err.message : String(err), 'error');
|
|
1267
1321
|
} finally {
|
|
1268
|
-
|
|
1322
|
+
setGraphSyncInFlight(false);
|
|
1269
1323
|
if (syncBtn) {
|
|
1270
1324
|
syncBtn.disabled = false;
|
|
1271
1325
|
syncBtn.textContent = '⧉ Sync';
|
|
@@ -1439,10 +1493,7 @@ function bindHudEvents(): void {
|
|
|
1439
1493
|
document.getElementById('graph-physics-panel-toggle')?.classList.toggle('active', physicsOpen);
|
|
1440
1494
|
if (physicsOpen && canvasRef.current) {
|
|
1441
1495
|
const params = canvasRef.current.getPhysicsParams();
|
|
1442
|
-
const repEl
|
|
1443
|
-
const ldEl = document.getElementById('graph-physics-linkdist') as HTMLInputElement | null;
|
|
1444
|
-
const lsEl = document.getElementById('graph-physics-linkstr') as HTMLInputElement | null;
|
|
1445
|
-
const gEl = document.getElementById('graph-physics-gravity') as HTMLInputElement | null;
|
|
1496
|
+
const { rep: repEl, linkDist: ldEl, linkStr: lsEl, gravity: gEl } = getPhysicsInputs();
|
|
1446
1497
|
if (repEl) { repEl.value = String(params.repulsion); }
|
|
1447
1498
|
if (ldEl) { ldEl.value = String(params.linkDistance); }
|
|
1448
1499
|
if (lsEl) { lsEl.value = String(Math.round(params.linkStrength * 100)); }
|
|
@@ -1471,10 +1522,7 @@ function bindHudEvents(): void {
|
|
|
1471
1522
|
|
|
1472
1523
|
document.getElementById('graph-physics-reset')?.addEventListener('click', () => {
|
|
1473
1524
|
canvasRef.current?.setPhysicsParams({ repulsion: 2000, linkDistance: 140, linkStrength: 0.065, centerForce: 0.010 });
|
|
1474
|
-
const repEl
|
|
1475
|
-
const ldEl = document.getElementById('graph-physics-linkdist') as HTMLInputElement | null;
|
|
1476
|
-
const lsEl = document.getElementById('graph-physics-linkstr') as HTMLInputElement | null;
|
|
1477
|
-
const gEl = document.getElementById('graph-physics-gravity') as HTMLInputElement | null;
|
|
1525
|
+
const { rep: repEl, linkDist: ldEl, linkStr: lsEl, gravity: gEl } = getPhysicsInputs();
|
|
1478
1526
|
if (repEl) repEl.value = '2000';
|
|
1479
1527
|
if (ldEl) ldEl.value = '140';
|
|
1480
1528
|
if (lsEl) lsEl.value = '7';
|
|
@@ -1572,7 +1620,7 @@ function bindHudEvents(): void {
|
|
|
1572
1620
|
};
|
|
1573
1621
|
document.addEventListener('keydown', graphKeyHandler);
|
|
1574
1622
|
// Store for cleanup on graph exit
|
|
1575
|
-
|
|
1623
|
+
browserWindow().__graphKeyHandler = graphKeyHandler;
|
|
1576
1624
|
}
|
|
1577
1625
|
|
|
1578
1626
|
// ── URL routing (pushState) ─────────────────────────────────
|
|
@@ -1674,12 +1722,9 @@ function showAddDependencyModal(): void {
|
|
|
1674
1722
|
try {
|
|
1675
1723
|
await api('POST', `/projects/${state.currentProject!.id}/pm/deps/${fromId}`, { targetId: toId, rel: relType });
|
|
1676
1724
|
document.getElementById('graph-add-dep-modal')?.remove();
|
|
1677
|
-
|
|
1678
|
-
canvasRef.current?.destroy();
|
|
1679
|
-
canvasRef.current = null;
|
|
1680
|
-
void renderGraphView();
|
|
1725
|
+
refreshGraph();
|
|
1681
1726
|
} catch (err: unknown) {
|
|
1682
|
-
|
|
1727
|
+
displayFormError(errEl, err);
|
|
1683
1728
|
}
|
|
1684
1729
|
});
|
|
1685
1730
|
}
|
|
@@ -1699,7 +1744,7 @@ function showRemoveDependencyModal(): void {
|
|
|
1699
1744
|
}).join('');
|
|
1700
1745
|
|
|
1701
1746
|
if (!depRels.length) {
|
|
1702
|
-
|
|
1747
|
+
browserWindow().__app?.toast('No dependencies to remove', 'info');
|
|
1703
1748
|
return;
|
|
1704
1749
|
}
|
|
1705
1750
|
|
|
@@ -1739,12 +1784,9 @@ function showRemoveDependencyModal(): void {
|
|
|
1739
1784
|
try {
|
|
1740
1785
|
await api('DELETE', `/projects/${state.currentProject!.id}/pm/deps/${rel.from}`, { targetId: rel.to, rel: rel.type });
|
|
1741
1786
|
document.getElementById('graph-remove-dep-modal')?.remove();
|
|
1742
|
-
|
|
1743
|
-
canvasRef.current?.destroy();
|
|
1744
|
-
canvasRef.current = null;
|
|
1745
|
-
void renderGraphView();
|
|
1787
|
+
refreshGraph();
|
|
1746
1788
|
} catch (err: unknown) {
|
|
1747
|
-
|
|
1789
|
+
displayFormError(errEl, err);
|
|
1748
1790
|
}
|
|
1749
1791
|
});
|
|
1750
1792
|
}
|
|
@@ -1893,25 +1935,17 @@ export async function renderLocalGraph(
|
|
|
1893
1935
|
if (id !== nodeId) {
|
|
1894
1936
|
const n = nodes.find((nd) => nd.id === id);
|
|
1895
1937
|
if (n && isItemNode(n)) {
|
|
1896
|
-
|
|
1938
|
+
browserWindow().__app?.openItemDetail(id);
|
|
1897
1939
|
}
|
|
1898
1940
|
}
|
|
1899
1941
|
},
|
|
1900
1942
|
onOpenNode(id) {
|
|
1901
|
-
|
|
1943
|
+
browserWindow().__app?.openItemDetail(id);
|
|
1902
1944
|
},
|
|
1903
1945
|
onContextMenu() { /* no context menu in local graph */ },
|
|
1904
1946
|
});
|
|
1905
1947
|
|
|
1906
|
-
const canvasNodes: CanvasNode[] = subNodes.map((n) => (
|
|
1907
|
-
id: n.id,
|
|
1908
|
-
label: nodeTitle(n),
|
|
1909
|
-
type: nodeType(n),
|
|
1910
|
-
status: nodeStatus(n),
|
|
1911
|
-
lane: nodeLane(n),
|
|
1912
|
-
degree: deg.get(n.id) || 0,
|
|
1913
|
-
tags: Array.isArray(n.properties?.tags) ? (n.properties.tags as unknown[]).map(String) : [],
|
|
1914
|
-
}));
|
|
1948
|
+
const canvasNodes: CanvasNode[] = subNodes.map((n) => nodeToCanvasNode(n, deg.get(n.id) || 0));
|
|
1915
1949
|
const canvasEdges: CanvasEdge[] = subRels.map((r) => ({ from: r.from, to: r.to, type: r.type }));
|
|
1916
1950
|
|
|
1917
1951
|
canvas.setData(canvasNodes, canvasEdges);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { api } from '../api.js';
|
|
2
|
-
import { escHtml } from '../utils.js';
|
|
2
|
+
import { escHtml, showError } from '../utils.js';
|
|
3
3
|
import { showModal, hideModal, createModal, confirmDialog } from '../components/modals.js';
|
|
4
4
|
import { toast } from '../components/toast.js';
|
|
5
5
|
/**
|
|
@@ -104,10 +104,7 @@ export async function submitCreateGroup() {
|
|
|
104
104
|
await loadGroups();
|
|
105
105
|
}
|
|
106
106
|
catch (err) {
|
|
107
|
-
|
|
108
|
-
errEl.textContent = err instanceof Error ? err.message : String(err);
|
|
109
|
-
errEl.style.display = 'block';
|
|
110
|
-
}
|
|
107
|
+
showError(errEl, err);
|
|
111
108
|
}
|
|
112
109
|
}
|
|
113
110
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groups.js","sourceRoot":"","sources":["groups.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"groups.js","sourceRoot":"","sources":["groups.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// ═══════════════════════════════════════════════════════════════
|
|
4
4
|
import { state } from '../state.js';
|
|
5
5
|
import { api } from '../api.js';
|
|
6
|
-
import { escHtml } from '../utils.js';
|
|
6
|
+
import { escHtml, showError } from '../utils.js';
|
|
7
7
|
import { showModal, hideModal, createModal, confirmDialog } from '../components/modals.js';
|
|
8
8
|
import { toast } from '../components/toast.js';
|
|
9
9
|
import type { GroupDetailResponse, GroupMemberRow, GroupsResponse, GroupListRow } from '../api-types.js';
|
|
@@ -103,7 +103,7 @@ export async function submitCreateGroup(): Promise<void> {
|
|
|
103
103
|
hideModal('create-group-modal');
|
|
104
104
|
await loadGroups();
|
|
105
105
|
} catch(err: unknown) {
|
|
106
|
-
|
|
106
|
+
showError(errEl, err);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
@@ -5,6 +5,15 @@ import { state } from '../state.js';
|
|
|
5
5
|
import { getGuide, getGuideTopic } from '../api.js';
|
|
6
6
|
import { escHtml } from '../utils.js';
|
|
7
7
|
import { toast } from '../components/toast.js';
|
|
8
|
+
/** Replaces the guide content with the shared error state and toasts the
|
|
9
|
+
* failure, falling back to the given message when the error has none. */
|
|
10
|
+
function showGuideError(err, fallback) {
|
|
11
|
+
const contentEl = document.getElementById('guide-content');
|
|
12
|
+
if (contentEl) {
|
|
13
|
+
contentEl.innerHTML = `<div class="empty-state"><div class="empty-state-text">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
|
|
14
|
+
}
|
|
15
|
+
toast(err instanceof Error ? err.message : fallback, 'error');
|
|
16
|
+
}
|
|
8
17
|
const TOPIC_ICONS = {
|
|
9
18
|
quickstart: '🚀',
|
|
10
19
|
commands: '⌨️',
|
|
@@ -120,11 +129,7 @@ export async function renderGuideView(topicId) {
|
|
|
120
129
|
contentEl.innerHTML = renderTopicDetail(topic);
|
|
121
130
|
}
|
|
122
131
|
catch (err) {
|
|
123
|
-
|
|
124
|
-
if (contentEl) {
|
|
125
|
-
contentEl.innerHTML = `<div class="empty-state"><div class="empty-state-text">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
|
|
126
|
-
}
|
|
127
|
-
toast(err instanceof Error ? err.message : 'Failed to load topic', 'error');
|
|
132
|
+
showGuideError(err, 'Failed to load topic');
|
|
128
133
|
}
|
|
129
134
|
}
|
|
130
135
|
else {
|
|
@@ -143,11 +148,7 @@ export async function renderGuideView(topicId) {
|
|
|
143
148
|
contentEl.innerHTML = renderTopicCards(topics);
|
|
144
149
|
}
|
|
145
150
|
catch (err) {
|
|
146
|
-
|
|
147
|
-
if (contentEl) {
|
|
148
|
-
contentEl.innerHTML = `<div class="empty-state"><div class="empty-state-text">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
|
|
149
|
-
}
|
|
150
|
-
toast(err instanceof Error ? err.message : 'Failed to load guide', 'error');
|
|
151
|
+
showGuideError(err, 'Failed to load guide');
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guide.js","sourceRoot":"","sources":["guide.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,aAAa;AACb,kEAAkE;AAClE,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAE/C,MAAM,WAAW,GAA2B;IAC1C,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,IAAI;IACd,SAAS,EAAE,IAAI;IACf,GAAG,EAAE,KAAK;IACV,UAAU,EAAE,IAAI;IAChB,MAAM,EAAE,IAAI;IACZ,SAAS,EAAE,IAAI;IACf,OAAO,EAAE,IAAI;CACd,CAAC;AAEF,SAAS,SAAS,CAAC,EAAU;IAC3B,OAAO,WAAW,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC;AACjC,CAAC;AAED,4GAA4G;AAC5G,SAAS,gBAAgB,CAAC,MAA6E;IACrG,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,2FAA2F,CAAC;IACrG,CAAC;IACD,OAAO,iCAAiC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;uHAC6D,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;sCAC9F,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;;yCAEZ,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC;2CACd,OAAO,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC;;WAEpE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC;AAC9B,CAAC;AAED,wKAAwK;AACxK,SAAS,iBAAiB,CAAC,KAS1B;IACC,MAAM,QAAQ,GAAa,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC;IACxE,MAAM,OAAO,GAAa,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC;IAE9C,OAAO;;;;;;;kCAOyB,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;UACnE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,8BAA8B,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;;;MAGnF,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;;;0CAGoB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;;aAEnD,CAAC,CAAC,CAAC,EAAE;MACZ,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;;;;YAIhB,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;;6CAEa,OAAO,CAAC,GAAG,CAAC;;0DAEC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;;mBAExE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;;aAEhB,CAAC,CAAC,CAAC,EAAE;MACZ,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;;;;YAI1C,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;;8EAEwC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC;gBAC9E,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,yEAAyE,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;gBAChH,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;;iDAE9B,OAAO,CAAC,GAAG,CAAC;;8DAEC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;;uBAExE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;mBACnB,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;;aAEhB,CAAC,CAAC,CAAC,EAAE;MACZ,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;;;;YAIf,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;;8EAEiD,OAAO,CAAC,CAAC,CAAC;4DAC5B,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;;aAEzE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACrB,CAAC;AAED,uLAAuL;AACvL,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,OAAgB;IACpD,MAAM,EAAE,GAAG,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;IACpD,IAAI,CAAC,EAAE;QAAE,OAAO;IAChB,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;QAC1B,EAAE,CAAC,SAAS,GAAG,wFAAwF,CAAC;QACxG,OAAO;IACT,CAAC;IAED,IAAI,OAAO,EAAE,CAAC;QACZ,2BAA2B;QAC3B,EAAE,CAAC,SAAS,GAAG;;;;;yGAKsF,CAAC;QAEtG,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,cAAc,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;YACnE,MAAM,KAAK,GAAI,IAA0B,CAAC,KAAK,IAAI,IAAI,CAAC;YACxD,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;YAC3D,IAAI,SAAS;gBAAE,SAAS,CAAC,SAAS,GAAG,iBAAiB,CAAC,KAAgD,CAAC,CAAC;QAC3G,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,
|
|
1
|
+
{"version":3,"file":"guide.js","sourceRoot":"","sources":["guide.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,16 @@ import { getGuide, getGuideTopic } from '../api.js';
|
|
|
6
6
|
import { escHtml } from '../utils.js';
|
|
7
7
|
import { toast } from '../components/toast.js';
|
|
8
8
|
|
|
9
|
+
/** Replaces the guide content with the shared error state and toasts the
|
|
10
|
+
* failure, falling back to the given message when the error has none. */
|
|
11
|
+
function showGuideError(err: unknown, fallback: string): void {
|
|
12
|
+
const contentEl = document.getElementById('guide-content');
|
|
13
|
+
if (contentEl) {
|
|
14
|
+
contentEl.innerHTML = `<div class="empty-state"><div class="empty-state-text">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
|
|
15
|
+
}
|
|
16
|
+
toast(err instanceof Error ? err.message : fallback, 'error');
|
|
17
|
+
}
|
|
18
|
+
|
|
9
19
|
const TOPIC_ICONS: Record<string, string> = {
|
|
10
20
|
quickstart: '🚀',
|
|
11
21
|
commands: '⌨️',
|
|
@@ -134,11 +144,7 @@ export async function renderGuideView(topicId?: string): Promise<void> {
|
|
|
134
144
|
const contentEl = document.getElementById('guide-content');
|
|
135
145
|
if (contentEl) contentEl.innerHTML = renderTopicDetail(topic as Parameters<typeof renderTopicDetail>[0]);
|
|
136
146
|
} catch (err: unknown) {
|
|
137
|
-
|
|
138
|
-
if (contentEl) {
|
|
139
|
-
contentEl.innerHTML = `<div class="empty-state"><div class="empty-state-text">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
|
|
140
|
-
}
|
|
141
|
-
toast(err instanceof Error ? err.message : 'Failed to load topic', 'error');
|
|
147
|
+
showGuideError(err, 'Failed to load topic');
|
|
142
148
|
}
|
|
143
149
|
} else {
|
|
144
150
|
// Show topic list
|
|
@@ -155,11 +161,7 @@ export async function renderGuideView(topicId?: string): Promise<void> {
|
|
|
155
161
|
const contentEl = document.getElementById('guide-content');
|
|
156
162
|
if (contentEl) contentEl.innerHTML = renderTopicCards(topics as Parameters<typeof renderTopicCards>[0]);
|
|
157
163
|
} catch (err: unknown) {
|
|
158
|
-
|
|
159
|
-
if (contentEl) {
|
|
160
|
-
contentEl.innerHTML = `<div class="empty-state"><div class="empty-state-text">Error: ${escHtml(err instanceof Error ? err.message : String(err))}</div></div>`;
|
|
161
|
-
}
|
|
162
|
-
toast(err instanceof Error ? err.message : 'Failed to load guide', 'error');
|
|
164
|
+
showGuideError(err, 'Failed to load guide');
|
|
163
165
|
}
|
|
164
166
|
}
|
|
165
167
|
}
|