hzl-web 1.16.2 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -132,7 +132,7 @@ export function createWebServer(options) {
132
132
  function handleRoot(res) {
133
133
  res.writeHead(200, {
134
134
  'Content-Type': 'text/html; charset=utf-8',
135
- 'Content-Security-Policy': "default-src 'self'; script-src 'unsafe-inline'; style-src 'unsafe-inline'",
135
+ 'Content-Security-Policy': "default-src 'self'; script-src 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'unsafe-inline'",
136
136
  'X-Content-Type-Options': 'nosniff',
137
137
  'X-Frame-Options': 'DENY',
138
138
  'Referrer-Policy': 'no-referrer',
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -15,11 +15,11 @@
15
15
  --accent: #f59e0b;
16
16
  --accent-dim: #b45309;
17
17
  --border: #404040;
18
- --status-backlog: #6b7280;
19
- --status-blocked: #ef4444;
20
- --status-ready: #22c55e;
21
- --status-in-progress: #3b82f6;
22
- --status-done: #6b7280;
18
+ --status-backlog: #6b7280; /* gray - not yet prioritized */
19
+ --status-blocked: #ef4444; /* red - stuck, needs help */
20
+ --status-ready: #3b82f6; /* blue - available to claim */
21
+ --status-in-progress: #f59e0b; /* orange - active work */
22
+ --status-done: #22c55e; /* green - completed */
23
23
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
24
24
  }
25
25
 
@@ -548,6 +548,77 @@
548
548
  margin-top: 2px;
549
549
  }
550
550
 
551
+ /* View Toggle */
552
+ .view-toggle {
553
+ display: flex;
554
+ gap: 0;
555
+ background: var(--bg-primary);
556
+ border-radius: 6px;
557
+ padding: 2px;
558
+ border: 1px solid var(--border);
559
+ }
560
+
561
+ .view-btn {
562
+ padding: 6px 12px;
563
+ border: none;
564
+ background: transparent;
565
+ color: var(--text-secondary);
566
+ cursor: pointer;
567
+ border-radius: 4px;
568
+ font-family: var(--font-mono);
569
+ font-size: 12px;
570
+ }
571
+
572
+ .view-btn.active {
573
+ background: var(--accent);
574
+ color: var(--bg-primary);
575
+ }
576
+
577
+ .view-btn:hover:not(.active):not(:disabled) {
578
+ color: var(--text-primary);
579
+ }
580
+
581
+ .view-btn:disabled {
582
+ opacity: 0.5;
583
+ cursor: not-allowed;
584
+ }
585
+
586
+ /* Graph Container */
587
+ .graph-container {
588
+ flex: 1;
589
+ position: relative;
590
+ min-height: 400px;
591
+ background: var(--bg-primary);
592
+ }
593
+
594
+ .graph-container.hidden {
595
+ display: none;
596
+ }
597
+
598
+ .graph-loading {
599
+ position: absolute;
600
+ inset: 0;
601
+ display: flex;
602
+ flex-direction: column;
603
+ align-items: center;
604
+ justify-content: center;
605
+ gap: 12px;
606
+ color: var(--text-secondary);
607
+ }
608
+
609
+ .graph-loading .spinner {
610
+ width: 24px;
611
+ height: 24px;
612
+ border: 2px solid var(--border);
613
+ border-top-color: var(--accent);
614
+ border-radius: 50%;
615
+ animation: spin 1s linear infinite;
616
+ }
617
+
618
+ @keyframes spin {
619
+ to { transform: rotate(360deg); }
620
+ }
621
+
551
622
  /* Mobile Styles */
552
623
  @media (max-width: 768px) {
553
624
  .header {
@@ -565,6 +636,14 @@
565
636
  display: none;
566
637
  }
567
638
 
639
+ .graph-container {
640
+ min-height: calc(100vh - 150px);
641
+ }
642
+
643
+ .view-toggle {
644
+ margin-left: auto;
645
+ }
646
+
568
647
  .mobile-tabs {
569
648
  display: flex;
570
649
  overflow-x: auto;
@@ -642,6 +721,10 @@
642
721
  <div class="header-left">
643
722
  <button class="hamburger" id="hamburgerBtn">&#9776;</button>
644
723
  <span class="logo">hzl dashboard</span>
724
+ <div class="view-toggle" id="viewToggle">
725
+ <button class="view-btn active" data-view="kanban">Kanban</button>
726
+ <button class="view-btn" data-view="graph">Graph</button>
727
+ </div>
645
728
  </div>
646
729
  <div class="header-filters">
647
730
  <div class="filter-group">
@@ -749,6 +832,14 @@
749
832
  </div>
750
833
  </main>
751
834
 
835
+ <!-- Graph View Container -->
836
+ <div id="graphContainer" class="graph-container hidden">
837
+ <div class="graph-loading" id="graphLoading">
838
+ <div class="spinner"></div>
839
+ <span>Loading graph...</span>
840
+ </div>
841
+ </div>
842
+
752
843
  <!-- Task Detail Modal -->
753
844
  <div class="modal-overlay" id="modalOverlay">
754
845
  <div class="modal">
@@ -782,6 +873,10 @@
782
873
  let lastPollTime = null;
783
874
  let selectedTask = null;
784
875
  let activeTab = 'ready';
876
+ let activeView = 'kanban';
877
+ let graphInstance = null;
878
+ let graphInitialized = false;
879
+ let nodeStatusMap = new Map();
785
880
 
786
881
  // DOM Elements
787
882
  const dateFilter = document.getElementById('dateFilter');
@@ -801,6 +896,10 @@
801
896
  const mobileTabs = document.getElementById('mobileTabs');
802
897
  const columnsToggle = document.getElementById('columnsToggle');
803
898
  const columnsDropdown = document.getElementById('columnsDropdown');
899
+ const viewToggle = document.getElementById('viewToggle');
900
+ const board = document.getElementById('board');
901
+ const graphContainer = document.getElementById('graphContainer');
902
+ const graphLoading = document.getElementById('graphLoading');
804
903
 
805
904
  // Column visibility
806
905
  const COLUMNS = ['backlog', 'ready', 'in_progress', 'blocked', 'done'];
@@ -830,6 +929,10 @@
830
929
  });
831
930
  updateColumnVisibility();
832
931
  }
932
+ if (prefs.activeView && prefs.activeView !== 'kanban') {
933
+ // Defer view switch until after ForceGraph may have loaded
934
+ setTimeout(() => setActiveView(prefs.activeView), 100);
935
+ }
833
936
  } catch (e) {}
834
937
  }
835
938
  }
@@ -842,10 +945,274 @@
842
945
  columnVisibility: Array.from(
843
946
  columnsDropdown.querySelectorAll('input[type="checkbox"]:checked')
844
947
  ).map(cb => cb.value),
948
+ activeView: activeView,
845
949
  };
846
950
  localStorage.setItem('hzl-dashboard-prefs', JSON.stringify(prefs));
847
951
  }
848
952
 
953
+ // Graph View Functions
954
+ function handleGraphLibError() {
955
+ console.warn('[hzl] force-graph CDN failed to load');
956
+ const graphBtn = viewToggle.querySelector('[data-view="graph"]');
957
+ if (graphBtn) {
958
+ graphBtn.disabled = true;
959
+ graphBtn.title = 'Graph unavailable - CDN failed to load';
960
+ }
961
+ }
962
+
963
+ function getStatusColor(status, type) {
964
+ if (type === 'root') return '#f59e0b';
965
+ if (type === 'project') return '#e5e5e5';
966
+ const colors = {
967
+ backlog: '#6b7280', // gray - not yet prioritized
968
+ ready: '#3b82f6', // blue - available to claim
969
+ in_progress: '#f59e0b', // orange - active work
970
+ blocked: '#ef4444', // red - stuck, needs help
971
+ done: '#22c55e', // green - completed
972
+ };
973
+ return colors[status] ?? '#6b7280';
974
+ }
975
+
976
+ function getNodeSize(node) {
977
+ if (node.type === 'root') return 20;
978
+ if (node.type === 'project') return 14;
979
+ const progress = node.progress ?? 0;
980
+ return 8 + (progress / 100) * 16;
981
+ }
982
+
983
+ function transformTasksToGraph(taskList) {
984
+ const nodes = [{ id: 'root', type: 'root', name: 'HZL', ring: 0, angle: 0 }];
985
+ const links = [];
986
+ const projectList = [];
987
+ const projectAngles = new Map();
988
+ nodeStatusMap.clear();
989
+
990
+ // First pass: collect unique projects
991
+ for (const task of taskList) {
992
+ if (task.project && !projectAngles.has(task.project)) {
993
+ projectList.push(task.project);
994
+ projectAngles.set(task.project, 0);
995
+ }
996
+ }
997
+
998
+ // Assign angles to projects (evenly distributed)
999
+ projectList.forEach((proj, i) => {
1000
+ const angle = (2 * Math.PI * i) / projectList.length;
1001
+ projectAngles.set(proj, angle);
1002
+ });
1003
+
1004
+ // Second pass: create nodes
1005
+ const addedProjects = new Set();
1006
+ for (const task of taskList) {
1007
+ if (!task.project || !task.task_id) {
1008
+ console.warn('[hzl] Skipping task with missing required fields:', task);
1009
+ continue;
1010
+ }
1011
+
1012
+ const projAngle = projectAngles.get(task.project);
1013
+
1014
+ // Add project node if not seen
1015
+ if (!addedProjects.has(task.project)) {
1016
+ addedProjects.add(task.project);
1017
+ const projectId = `project:${task.project}`;
1018
+ nodes.push({ id: projectId, type: 'project', name: task.project, ring: 1, angle: projAngle });
1019
+ links.push({ source: projectId, target: 'root', type: 'hierarchy' });
1020
+ nodeStatusMap.set(projectId, null);
1021
+ }
1022
+
1023
+ // Add task node with same angle as its project (with small random offset)
1024
+ const ring = task.parent_id ? 3 : 2;
1025
+ const angleOffset = (Math.random() - 0.5) * 0.3; // small spread within project
1026
+ nodes.push({
1027
+ id: task.task_id,
1028
+ type: task.parent_id ? 'subtask' : 'task',
1029
+ name: task.title,
1030
+ status: task.status,
1031
+ progress: task.progress ?? 0,
1032
+ ring,
1033
+ angle: projAngle + angleOffset,
1034
+ project: task.project,
1035
+ });
1036
+ nodeStatusMap.set(task.task_id, task.status);
1037
+
1038
+ // Hierarchy link
1039
+ const parent = task.parent_id || `project:${task.project}`;
1040
+ links.push({ source: task.task_id, target: parent, type: 'hierarchy' });
1041
+
1042
+ // Dependency links (for particles)
1043
+ if (task.blocked_by) {
1044
+ for (const blockerId of task.blocked_by) {
1045
+ links.push({ source: blockerId, target: task.task_id, type: 'dependency' });
1046
+ }
1047
+ }
1048
+ }
1049
+
1050
+ return { nodes, links };
1051
+ }
1052
+
1053
+ function initGraph() {
1054
+ if (graphInitialized) return;
1055
+
1056
+ if (typeof ForceGraph === 'undefined') {
1057
+ console.error('[hzl] ForceGraph not available - CDN may have failed');
1058
+ handleGraphLibError();
1059
+ return;
1060
+ }
1061
+
1062
+ // Hide loading spinner
1063
+ graphLoading.style.display = 'none';
1064
+
1065
+ const RING_RADII = { 0: 0, 1: 180, 2: 360, 3: 540 };
1066
+ const baseRadius = window.innerWidth < 768 ? 0.6 : 1;
1067
+
1068
+ // Pre-position nodes based on angle and ring
1069
+ const graphData = transformTasksToGraph(tasks);
1070
+ for (const node of graphData.nodes) {
1071
+ const radius = (RING_RADII[node.ring] ?? 300) * baseRadius;
1072
+ node.x = Math.cos(node.angle || 0) * radius;
1073
+ node.y = Math.sin(node.angle || 0) * radius;
1074
+ }
1075
+
1076
+ graphInstance = ForceGraph()(graphContainer)
1077
+ .graphData(graphData)
1078
+ .backgroundColor('#1a1a1a')
1079
+ .nodeLabel(n => n.name)
1080
+ .nodeColor(n => getStatusColor(n.status, n.type))
1081
+ .nodeVal(n => getNodeSize(n))
1082
+ .linkColor(l => l.type === 'dependency' ? '#e57373' : '#40404080')
1083
+ .linkWidth(l => l.type === 'dependency' ? 2 : 1)
1084
+ .linkLabel(l => l.type === 'dependency' ? 'blocks' : '')
1085
+ .linkDirectionalArrowLength(l => l.type === 'dependency' ? 6 : 0)
1086
+ .linkDirectionalArrowRelPos(1)
1087
+ .onNodeClick(n => {
1088
+ if (n.type !== 'root' && n.type !== 'project') {
1089
+ openTaskModal(n.id);
1090
+ }
1091
+ })
1092
+ // Forces to keep nodes in their angular sectors
1093
+ .d3Force('x', d3.forceX(d => {
1094
+ const radius = (RING_RADII[d.ring] ?? 300) * baseRadius;
1095
+ return Math.cos(d.angle || 0) * radius;
1096
+ }).strength(0.3))
1097
+ .d3Force('y', d3.forceY(d => {
1098
+ const radius = (RING_RADII[d.ring] ?? 300) * baseRadius;
1099
+ return Math.sin(d.angle || 0) * radius;
1100
+ }).strength(0.3))
1101
+ .d3Force('collision', d3.forceCollide(d => getNodeSize(d) + 15))
1102
+ .d3Force('charge', d3.forceManyBody().strength(-50))
1103
+ .d3Force('link', d3.forceLink().strength(0.1))
1104
+ // Animated particles on dependency edges
1105
+ .linkDirectionalParticles(l => l.type === 'dependency' ? 3 : 0)
1106
+ .linkDirectionalParticleWidth(3)
1107
+ .linkDirectionalParticleSpeed(0.005)
1108
+ .linkDirectionalParticleColor(() => '#e57373')
1109
+ // Custom node rendering for root glow
1110
+ .nodeCanvasObject((node, ctx, globalScale) => {
1111
+ if (node.type === 'root') {
1112
+ // Pulsing glow effect
1113
+ const pulse = Math.sin(Date.now() / 500) * 0.3 + 0.7;
1114
+ ctx.beginPath();
1115
+ ctx.arc(node.x, node.y, 25 * pulse, 0, 2 * Math.PI);
1116
+ ctx.fillStyle = `rgba(245, 158, 11, ${0.3 * pulse})`;
1117
+ ctx.fill();
1118
+ }
1119
+
1120
+ // Draw node
1121
+ const size = getNodeSize(node);
1122
+ ctx.beginPath();
1123
+ ctx.arc(node.x, node.y, size, 0, 2 * Math.PI);
1124
+ ctx.fillStyle = getStatusColor(node.status, node.type);
1125
+ ctx.fill();
1126
+
1127
+ // Label for root node
1128
+ if (node.type === 'root') {
1129
+ ctx.font = `bold ${11 / globalScale}px ui-monospace`;
1130
+ ctx.fillStyle = '#1a1a1a';
1131
+ ctx.textAlign = 'center';
1132
+ ctx.textBaseline = 'middle';
1133
+ ctx.fillText('HZL', node.x, node.y + 1);
1134
+ }
1135
+
1136
+ // Label for projects
1137
+ if (node.type === 'project' && globalScale > 0.5) {
1138
+ ctx.font = `${10 / globalScale}px ui-monospace`;
1139
+ ctx.fillStyle = '#e5e5e5';
1140
+ ctx.textAlign = 'center';
1141
+ ctx.fillText(node.name, node.x, node.y + size + 12);
1142
+ }
1143
+ })
1144
+ .nodePointerAreaPaint((node, color, ctx) => {
1145
+ const size = getNodeSize(node);
1146
+ ctx.beginPath();
1147
+ ctx.arc(node.x, node.y, size + 4, 0, 2 * Math.PI);
1148
+ ctx.fillStyle = color;
1149
+ ctx.fill();
1150
+ });
1151
+
1152
+ graphInitialized = true;
1153
+
1154
+ // Zoom to fit after layout settles
1155
+ setTimeout(() => {
1156
+ if (graphInstance) {
1157
+ graphInstance.zoomToFit(400, 50);
1158
+ }
1159
+ }, 500);
1160
+ }
1161
+
1162
+ let lastGraphDataHash = '';
1163
+
1164
+ function hashTasks(taskList) {
1165
+ // Simple hash based on task ids, statuses, and dependencies
1166
+ return taskList.map(t => `${t.task_id}:${t.status}:${t.progress}`).sort().join('|');
1167
+ }
1168
+
1169
+ function updateGraphData() {
1170
+ // Skip updates when graph isn't visible to save CPU
1171
+ if (!graphInstance || !graphInitialized || activeView !== 'graph') {
1172
+ return;
1173
+ }
1174
+
1175
+ const newHash = hashTasks(tasks);
1176
+ if (newHash !== lastGraphDataHash) {
1177
+ lastGraphDataHash = newHash;
1178
+
1179
+ // Get current node positions
1180
+ const currentData = graphInstance.graphData();
1181
+ const positionMap = new Map();
1182
+ for (const node of currentData.nodes) {
1183
+ if (node.x !== undefined && node.y !== undefined) {
1184
+ positionMap.set(node.id, { x: node.x, y: node.y, vx: node.vx, vy: node.vy });
1185
+ }
1186
+ }
1187
+
1188
+ // Create new graph data and preserve positions
1189
+ const newData = transformTasksToGraph(tasks);
1190
+ for (const node of newData.nodes) {
1191
+ const pos = positionMap.get(node.id);
1192
+ if (pos) {
1193
+ node.x = pos.x;
1194
+ node.y = pos.y;
1195
+ node.vx = pos.vx;
1196
+ node.vy = pos.vy;
1197
+ }
1198
+ }
1199
+
1200
+ graphInstance.graphData(newData);
1201
+ }
1202
+ }
1203
+
1204
+ // Debounced resize handling
1205
+ let resizeTimeout;
1206
+ window.addEventListener('resize', () => {
1207
+ clearTimeout(resizeTimeout);
1208
+ resizeTimeout = setTimeout(() => {
1209
+ if (graphInstance && activeView === 'graph') {
1210
+ graphInstance.width(graphContainer.clientWidth);
1211
+ graphInstance.height(graphContainer.clientHeight);
1212
+ }
1213
+ }, 100);
1214
+ });
1215
+
849
1216
  // API calls
850
1217
  async function fetchTasks() {
851
1218
  const since = dateFilter.value;
@@ -1154,6 +1521,7 @@
1154
1521
 
1155
1522
  renderBoard();
1156
1523
  renderActivity();
1524
+ updateGraphData();
1157
1525
 
1158
1526
  lastPollTime = Date.now();
1159
1527
  updateConnectionStatus(true);
@@ -1316,6 +1684,51 @@
1316
1684
  savePreferences();
1317
1685
  });
1318
1686
 
1687
+ // View toggle (Kanban/Graph)
1688
+ function setActiveView(view) {
1689
+ activeView = view;
1690
+
1691
+ // Update toggle buttons
1692
+ viewToggle.querySelectorAll('.view-btn').forEach(btn => {
1693
+ btn.classList.toggle('active', btn.dataset.view === view);
1694
+ });
1695
+
1696
+ // Show/hide containers
1697
+ if (view === 'kanban') {
1698
+ board.style.display = '';
1699
+ graphContainer.classList.add('hidden');
1700
+ mobileTabs.style.display = '';
1701
+ document.getElementById('mobileCardsContainer').style.display = '';
1702
+
1703
+ // Pause graph to save CPU/memory when not visible
1704
+ if (graphInstance) {
1705
+ graphInstance.pauseAnimation();
1706
+ }
1707
+ } else {
1708
+ board.style.display = 'none';
1709
+ graphContainer.classList.remove('hidden');
1710
+ mobileTabs.style.display = 'none';
1711
+ document.getElementById('mobileCardsContainer').style.display = 'none';
1712
+
1713
+ // Initialize graph on first view
1714
+ if (!graphInitialized && typeof ForceGraph !== 'undefined') {
1715
+ initGraph();
1716
+ } else if (graphInstance) {
1717
+ // Resume graph animation when switching back
1718
+ graphInstance.resumeAnimation();
1719
+ }
1720
+ }
1721
+
1722
+ savePreferences();
1723
+ }
1724
+
1725
+ viewToggle.addEventListener('click', (e) => {
1726
+ const btn = e.target.closest('.view-btn');
1727
+ if (btn && !btn.disabled && btn.dataset.view !== activeView) {
1728
+ setActiveView(btn.dataset.view);
1729
+ }
1730
+ });
1731
+
1319
1732
  // Initialize
1320
1733
  loadPreferences();
1321
1734
  startPolling();
@@ -1328,5 +1741,12 @@
1328
1741
  }
1329
1742
  }, 1000);
1330
1743
  </script>
1744
+
1745
+ <!-- d3 for graph layout forces -->
1746
+ <script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js" defer></script>
1747
+ <!-- Force-graph library for Graph view -->
1748
+ <script src="https://cdn.jsdelivr.net/npm/force-graph@1/dist/force-graph.min.js"
1749
+ defer
1750
+ onerror="handleGraphLibError()"></script>
1331
1751
  </body>
1332
1752
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hzl-web",
3
- "version": "1.16.2",
3
+ "version": "1.17.0",
4
4
  "description": "Web dashboard for HZL - A Kanban-style task monitoring UI.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",