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 +1 -1
- package/dist/server.js.map +1 -1
- package/dist/ui/index.html +425 -5
- package/package.json +1 -1
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',
|
package/dist/server.js.map
CHANGED
|
@@ -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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"}
|
package/dist/ui/index.html
CHANGED
|
@@ -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: #
|
|
21
|
-
--status-in-progress: #
|
|
22
|
-
--status-done: #
|
|
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">☰</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>
|