claude-kanban 0.6.0 → 0.6.2
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/bin/cli.js +360 -38
- package/dist/bin/cli.js.map +1 -1
- package/dist/server/index.js +360 -38
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -3194,6 +3194,16 @@ let state = {
|
|
|
3194
3194
|
planningOutput: [],
|
|
3195
3195
|
sidePanelTab: 'logs', // 'logs' or 'details'
|
|
3196
3196
|
darkMode: localStorage.getItem('darkMode') === 'true', // Add dark mode state
|
|
3197
|
+
// View state (board or roadmap)
|
|
3198
|
+
currentView: 'board', // 'board' or 'roadmap'
|
|
3199
|
+
// Roadmap state
|
|
3200
|
+
roadmap: null,
|
|
3201
|
+
roadmapGenerating: false,
|
|
3202
|
+
roadmapProgress: null,
|
|
3203
|
+
roadmapError: null,
|
|
3204
|
+
roadmapSelectedFeature: null,
|
|
3205
|
+
roadmapEnableCompetitors: false,
|
|
3206
|
+
roadmapCustomPrompt: '',
|
|
3197
3207
|
};
|
|
3198
3208
|
|
|
3199
3209
|
// Toast notifications
|
|
@@ -3440,6 +3450,41 @@ socket.on('planning:cancelled', () => {
|
|
|
3440
3450
|
render();
|
|
3441
3451
|
});
|
|
3442
3452
|
|
|
3453
|
+
// Roadmap events
|
|
3454
|
+
socket.on('roadmap:started', () => {
|
|
3455
|
+
state.roadmapGenerating = true;
|
|
3456
|
+
state.roadmapProgress = { phase: 'analyzing', message: 'Starting roadmap generation...' };
|
|
3457
|
+
state.roadmapError = null;
|
|
3458
|
+
render();
|
|
3459
|
+
});
|
|
3460
|
+
|
|
3461
|
+
socket.on('roadmap:progress', ({ phase, message }) => {
|
|
3462
|
+
state.roadmapProgress = { phase, message };
|
|
3463
|
+
render();
|
|
3464
|
+
});
|
|
3465
|
+
|
|
3466
|
+
socket.on('roadmap:completed', ({ roadmap }) => {
|
|
3467
|
+
state.roadmap = roadmap;
|
|
3468
|
+
state.roadmapGenerating = false;
|
|
3469
|
+
state.roadmapProgress = null;
|
|
3470
|
+
state.showModal = null;
|
|
3471
|
+
showToast('Roadmap generated successfully!', 'success');
|
|
3472
|
+
render();
|
|
3473
|
+
});
|
|
3474
|
+
|
|
3475
|
+
socket.on('roadmap:failed', ({ error }) => {
|
|
3476
|
+
state.roadmapGenerating = false;
|
|
3477
|
+
state.roadmapProgress = null;
|
|
3478
|
+
state.roadmapError = error;
|
|
3479
|
+
showToast('Roadmap generation failed: ' + error, 'error');
|
|
3480
|
+
render();
|
|
3481
|
+
});
|
|
3482
|
+
|
|
3483
|
+
socket.on('roadmap:updated', (roadmap) => {
|
|
3484
|
+
state.roadmap = roadmap;
|
|
3485
|
+
render();
|
|
3486
|
+
});
|
|
3487
|
+
|
|
3443
3488
|
// Load templates
|
|
3444
3489
|
fetch('/api/templates').then(r => r.json()).then(data => {
|
|
3445
3490
|
state.templates = data.templates;
|
|
@@ -3531,6 +3576,69 @@ async function cancelPlanning() {
|
|
|
3531
3576
|
await fetch('/api/plan/cancel', { method: 'POST' });
|
|
3532
3577
|
}
|
|
3533
3578
|
|
|
3579
|
+
// Roadmap API functions
|
|
3580
|
+
async function loadRoadmap() {
|
|
3581
|
+
const res = await fetch('/api/roadmap');
|
|
3582
|
+
const data = await res.json();
|
|
3583
|
+
state.roadmap = data.roadmap;
|
|
3584
|
+
render();
|
|
3585
|
+
}
|
|
3586
|
+
|
|
3587
|
+
async function generateRoadmap() {
|
|
3588
|
+
state.roadmapGenerating = true;
|
|
3589
|
+
state.roadmapError = null;
|
|
3590
|
+
render();
|
|
3591
|
+
try {
|
|
3592
|
+
await fetch('/api/roadmap/generate', {
|
|
3593
|
+
method: 'POST',
|
|
3594
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3595
|
+
body: JSON.stringify({
|
|
3596
|
+
enableCompetitorResearch: state.roadmapEnableCompetitors,
|
|
3597
|
+
customPrompt: state.roadmapCustomPrompt || undefined
|
|
3598
|
+
})
|
|
3599
|
+
});
|
|
3600
|
+
} catch (e) {
|
|
3601
|
+
state.roadmapGenerating = false;
|
|
3602
|
+
state.roadmapError = e.message;
|
|
3603
|
+
render();
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3607
|
+
async function cancelRoadmap() {
|
|
3608
|
+
await fetch('/api/roadmap/cancel', { method: 'POST' });
|
|
3609
|
+
state.roadmapGenerating = false;
|
|
3610
|
+
state.roadmapProgress = null;
|
|
3611
|
+
render();
|
|
3612
|
+
}
|
|
3613
|
+
|
|
3614
|
+
async function addFeatureToKanban(featureId) {
|
|
3615
|
+
try {
|
|
3616
|
+
const res = await fetch('/api/roadmap/features/' + featureId + '/add-to-kanban', {
|
|
3617
|
+
method: 'POST'
|
|
3618
|
+
});
|
|
3619
|
+
const data = await res.json();
|
|
3620
|
+
if (data.task) {
|
|
3621
|
+
showToast('Feature added to kanban!', 'success');
|
|
3622
|
+
// Reload roadmap to update addedToKanban status
|
|
3623
|
+
await loadRoadmap();
|
|
3624
|
+
}
|
|
3625
|
+
} catch (e) {
|
|
3626
|
+
showToast('Failed to add feature: ' + e.message, 'error');
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3630
|
+
async function deleteRoadmapFeature(featureId) {
|
|
3631
|
+
try {
|
|
3632
|
+
await fetch('/api/roadmap/features/' + featureId, { method: 'DELETE' });
|
|
3633
|
+
showToast('Feature removed', 'info');
|
|
3634
|
+
} catch (e) {
|
|
3635
|
+
showToast('Failed to remove feature: ' + e.message, 'error');
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
|
|
3639
|
+
// Load roadmap on init
|
|
3640
|
+
loadRoadmap();
|
|
3641
|
+
|
|
3534
3642
|
// Enhanced Drag and drop
|
|
3535
3643
|
let draggedTask = null;
|
|
3536
3644
|
let draggedElement = null;
|
|
@@ -3703,6 +3811,185 @@ function showTaskMenu(taskId) {
|
|
|
3703
3811
|
openSidePanel(taskId);
|
|
3704
3812
|
}
|
|
3705
3813
|
|
|
3814
|
+
// Roadmap rendering functions
|
|
3815
|
+
function renderRoadmap() {
|
|
3816
|
+
if (state.roadmapGenerating) {
|
|
3817
|
+
return \`
|
|
3818
|
+
<div class="flex-1 flex items-center justify-center">
|
|
3819
|
+
<div class="text-center">
|
|
3820
|
+
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-accent mx-auto mb-4"></div>
|
|
3821
|
+
<h3 class="text-lg font-medium text-canvas-800 mb-2">Generating Roadmap...</h3>
|
|
3822
|
+
<p class="text-canvas-500">\${state.roadmapProgress?.message || 'Please wait...'}</p>
|
|
3823
|
+
<button onclick="cancelRoadmap()" class="btn btn-ghost mt-4 text-sm">Cancel</button>
|
|
3824
|
+
</div>
|
|
3825
|
+
</div>
|
|
3826
|
+
\`;
|
|
3827
|
+
}
|
|
3828
|
+
|
|
3829
|
+
if (!state.roadmap) {
|
|
3830
|
+
return \`
|
|
3831
|
+
<div class="flex-1 flex items-center justify-center">
|
|
3832
|
+
<div class="text-center max-w-md">
|
|
3833
|
+
<div class="text-6xl mb-4">\u{1F5FA}\uFE0F</div>
|
|
3834
|
+
<h3 class="text-xl font-semibold text-canvas-800 mb-2">No Roadmap Yet</h3>
|
|
3835
|
+
<p class="text-canvas-500 mb-6">Generate a strategic feature roadmap using AI to analyze your project and suggest features.</p>
|
|
3836
|
+
<button onclick="state.showModal = 'roadmap'; render();" class="btn btn-primary px-6 py-2">
|
|
3837
|
+
\u{1F680} Generate Roadmap
|
|
3838
|
+
</button>
|
|
3839
|
+
</div>
|
|
3840
|
+
</div>
|
|
3841
|
+
\`;
|
|
3842
|
+
}
|
|
3843
|
+
|
|
3844
|
+
const roadmap = state.roadmap;
|
|
3845
|
+
const phases = roadmap.phases || [];
|
|
3846
|
+
const features = roadmap.features || [];
|
|
3847
|
+
|
|
3848
|
+
return \`
|
|
3849
|
+
<div class="flex-1 overflow-y-auto p-6">
|
|
3850
|
+
<!-- Roadmap Header -->
|
|
3851
|
+
<div class="mb-6 flex items-start justify-between">
|
|
3852
|
+
<div>
|
|
3853
|
+
<h2 class="text-2xl font-semibold text-canvas-900">\${escapeHtml(roadmap.projectName)} Roadmap</h2>
|
|
3854
|
+
<p class="text-canvas-500 mt-1">\${escapeHtml(roadmap.projectDescription || '')}</p>
|
|
3855
|
+
<p class="text-sm text-canvas-400 mt-2">Target: \${escapeHtml(roadmap.targetAudience || 'Developers')}</p>
|
|
3856
|
+
</div>
|
|
3857
|
+
<div class="flex gap-2">
|
|
3858
|
+
<button onclick="state.showModal = 'roadmap'; render();" class="btn btn-ghost text-sm">
|
|
3859
|
+
\u{1F504} Regenerate
|
|
3860
|
+
</button>
|
|
3861
|
+
</div>
|
|
3862
|
+
</div>
|
|
3863
|
+
|
|
3864
|
+
<!-- Competitors (if available) -->
|
|
3865
|
+
\${roadmap.competitors && roadmap.competitors.length > 0 ? \`
|
|
3866
|
+
<div class="mb-6">
|
|
3867
|
+
<h3 class="text-sm font-medium text-canvas-700 mb-2">Competitor Insights</h3>
|
|
3868
|
+
<div class="flex gap-2 flex-wrap">
|
|
3869
|
+
\${roadmap.competitors.map(c => \`
|
|
3870
|
+
<span class="px-3 py-1 bg-canvas-100 rounded-full text-sm text-canvas-600">
|
|
3871
|
+
\${escapeHtml(c.name)}
|
|
3872
|
+
</span>
|
|
3873
|
+
\`).join('')}
|
|
3874
|
+
</div>
|
|
3875
|
+
</div>
|
|
3876
|
+
\` : ''}
|
|
3877
|
+
|
|
3878
|
+
<!-- Phases -->
|
|
3879
|
+
<div class="space-y-8">
|
|
3880
|
+
\${phases.map(phase => {
|
|
3881
|
+
const phaseFeatures = features.filter(f => f.phase === phase.name);
|
|
3882
|
+
return \`
|
|
3883
|
+
<div class="phase-section">
|
|
3884
|
+
<div class="flex items-center gap-3 mb-4">
|
|
3885
|
+
<h3 class="text-lg font-semibold text-canvas-800">\${escapeHtml(phase.name)}</h3>
|
|
3886
|
+
<span class="text-xs bg-canvas-100 px-2 py-0.5 rounded-full text-canvas-500">\${phaseFeatures.length} features</span>
|
|
3887
|
+
</div>
|
|
3888
|
+
<p class="text-sm text-canvas-500 mb-4">\${escapeHtml(phase.description || '')}</p>
|
|
3889
|
+
<div class="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
|
3890
|
+
\${phaseFeatures.map(f => renderRoadmapFeature(f)).join('')}
|
|
3891
|
+
</div>
|
|
3892
|
+
</div>
|
|
3893
|
+
\`;
|
|
3894
|
+
}).join('')}
|
|
3895
|
+
</div>
|
|
3896
|
+
</div>
|
|
3897
|
+
\`;
|
|
3898
|
+
}
|
|
3899
|
+
|
|
3900
|
+
function renderRoadmapFeature(feature) {
|
|
3901
|
+
const priorityColors = {
|
|
3902
|
+
must: 'bg-red-100 text-red-700',
|
|
3903
|
+
should: 'bg-orange-100 text-orange-700',
|
|
3904
|
+
could: 'bg-blue-100 text-blue-700',
|
|
3905
|
+
wont: 'bg-gray-100 text-gray-500'
|
|
3906
|
+
};
|
|
3907
|
+
const priorityLabels = {
|
|
3908
|
+
must: 'Must Have',
|
|
3909
|
+
should: 'Should Have',
|
|
3910
|
+
could: 'Could Have',
|
|
3911
|
+
wont: "Won't Have"
|
|
3912
|
+
};
|
|
3913
|
+
const effortIcons = {
|
|
3914
|
+
low: '\u26A1',
|
|
3915
|
+
medium: '\u23F1\uFE0F',
|
|
3916
|
+
high: '\u{1F3CB}\uFE0F'
|
|
3917
|
+
};
|
|
3918
|
+
const impactIcons = {
|
|
3919
|
+
low: '\u{1F4C9}',
|
|
3920
|
+
medium: '\u{1F4CA}',
|
|
3921
|
+
high: '\u{1F4C8}'
|
|
3922
|
+
};
|
|
3923
|
+
|
|
3924
|
+
return \`
|
|
3925
|
+
<div class="card p-4 \${feature.addedToKanban ? 'opacity-60' : ''}" onclick="state.roadmapSelectedFeature = '\${feature.id}'; render();">
|
|
3926
|
+
<div class="flex items-start justify-between mb-2">
|
|
3927
|
+
<h4 class="font-medium text-canvas-800 text-sm">\${escapeHtml(feature.title)}</h4>
|
|
3928
|
+
<span class="text-xs px-2 py-0.5 rounded-full \${priorityColors[feature.priority] || 'bg-gray-100'}">\${priorityLabels[feature.priority] || feature.priority}</span>
|
|
3929
|
+
</div>
|
|
3930
|
+
<p class="text-xs text-canvas-500 mb-3 line-clamp-2">\${escapeHtml(feature.description)}</p>
|
|
3931
|
+
<div class="flex items-center justify-between">
|
|
3932
|
+
<div class="flex gap-2 text-xs text-canvas-400">
|
|
3933
|
+
<span title="Effort">\${effortIcons[feature.effort] || '\u23F1\uFE0F'} \${feature.effort}</span>
|
|
3934
|
+
<span title="Impact">\${impactIcons[feature.impact] || '\u{1F4CA}'} \${feature.impact}</span>
|
|
3935
|
+
</div>
|
|
3936
|
+
\${feature.addedToKanban ? \`
|
|
3937
|
+
<span class="text-xs text-green-600">\u2713 Added</span>
|
|
3938
|
+
\` : \`
|
|
3939
|
+
<button onclick="event.stopPropagation(); addFeatureToKanban('\${feature.id}')" class="text-xs text-accent hover:underline">+ Add to Kanban</button>
|
|
3940
|
+
\`}
|
|
3941
|
+
</div>
|
|
3942
|
+
</div>
|
|
3943
|
+
\`;
|
|
3944
|
+
}
|
|
3945
|
+
|
|
3946
|
+
function renderRoadmapModal() {
|
|
3947
|
+
return \`
|
|
3948
|
+
<div class="modal-backdrop fixed inset-0 flex items-center justify-center z-50" onclick="if(event.target === event.currentTarget) { state.showModal = null; render(); }">
|
|
3949
|
+
<div class="modal-content card rounded-xl w-full max-w-lg mx-4">
|
|
3950
|
+
<div class="px-6 py-4 border-b border-canvas-200 flex justify-between items-center">
|
|
3951
|
+
<h3 class="font-display font-semibold text-canvas-800 text-lg">\u{1F5FA}\uFE0F Generate Roadmap</h3>
|
|
3952
|
+
<button onclick="state.showModal = null; render();" class="text-canvas-400 hover:text-canvas-600 text-xl leading-none">×</button>
|
|
3953
|
+
</div>
|
|
3954
|
+
<div class="p-6">
|
|
3955
|
+
<p class="text-sm text-canvas-500 mb-6">
|
|
3956
|
+
Generate a strategic feature roadmap by analyzing your project structure and optionally researching competitors.
|
|
3957
|
+
</p>
|
|
3958
|
+
|
|
3959
|
+
<div class="space-y-5">
|
|
3960
|
+
<label class="flex items-start gap-3 cursor-pointer p-3 rounded-lg border border-canvas-200 hover:border-canvas-300 transition-colors">
|
|
3961
|
+
<input type="checkbox"
|
|
3962
|
+
\${state.roadmapEnableCompetitors ? 'checked' : ''}
|
|
3963
|
+
onchange="state.roadmapEnableCompetitors = this.checked; render();"
|
|
3964
|
+
class="w-4 h-4 mt-0.5 accent-accent">
|
|
3965
|
+
<div>
|
|
3966
|
+
<span class="text-sm font-medium text-canvas-700">Enable competitor research</span>
|
|
3967
|
+
<p class="text-xs text-canvas-400 mt-0.5">Use web search to analyze competitors (takes longer)</p>
|
|
3968
|
+
</div>
|
|
3969
|
+
</label>
|
|
3970
|
+
|
|
3971
|
+
<div>
|
|
3972
|
+
<label class="block text-sm font-medium text-canvas-700 mb-1.5">Additional context (optional)</label>
|
|
3973
|
+
<textarea
|
|
3974
|
+
class="input w-full text-sm"
|
|
3975
|
+
rows="3"
|
|
3976
|
+
placeholder="E.g., Focus on mobile features, target enterprise users..."
|
|
3977
|
+
oninput="state.roadmapCustomPrompt = this.value;"
|
|
3978
|
+
>\${escapeHtml(state.roadmapCustomPrompt)}</textarea>
|
|
3979
|
+
</div>
|
|
3980
|
+
</div>
|
|
3981
|
+
</div>
|
|
3982
|
+
<div class="px-6 py-4 border-t border-canvas-200 flex justify-end gap-3">
|
|
3983
|
+
<button onclick="state.showModal = null; render();" class="btn btn-ghost px-4 py-2">Cancel</button>
|
|
3984
|
+
<button onclick="generateRoadmap(); state.showModal = null; render();" class="btn btn-primary px-4 py-2">
|
|
3985
|
+
\u{1F680} Generate Roadmap
|
|
3986
|
+
</button>
|
|
3987
|
+
</div>
|
|
3988
|
+
</div>
|
|
3989
|
+
</div>
|
|
3990
|
+
\`;
|
|
3991
|
+
}
|
|
3992
|
+
|
|
3706
3993
|
function renderColumn(status, title, tasks) {
|
|
3707
3994
|
const columnTasks = tasks.filter(t => t.status === status);
|
|
3708
3995
|
const statusLabels = {
|
|
@@ -4049,6 +4336,11 @@ function renderModal() {
|
|
|
4049
4336
|
\`;
|
|
4050
4337
|
}
|
|
4051
4338
|
|
|
4339
|
+
// Roadmap generation modal
|
|
4340
|
+
if (state.showModal === 'roadmap') {
|
|
4341
|
+
return renderRoadmapModal();
|
|
4342
|
+
}
|
|
4343
|
+
|
|
4052
4344
|
return '';
|
|
4053
4345
|
}
|
|
4054
4346
|
|
|
@@ -4368,7 +4660,7 @@ function escapeHtml(str) {
|
|
|
4368
4660
|
// Main render
|
|
4369
4661
|
function render() {
|
|
4370
4662
|
const app = document.getElementById('app');
|
|
4371
|
-
const hasSidePanel = state.sidePanel !== null;
|
|
4663
|
+
const hasSidePanel = state.sidePanel !== null && state.currentView === 'board';
|
|
4372
4664
|
|
|
4373
4665
|
app.innerHTML = \`
|
|
4374
4666
|
<div class="min-h-screen flex flex-col bg-canvas-50">
|
|
@@ -4377,51 +4669,76 @@ function render() {
|
|
|
4377
4669
|
<div class="px-6 py-3 flex items-center justify-between">
|
|
4378
4670
|
<div class="flex items-center gap-6">
|
|
4379
4671
|
<h1 class="text-lg font-semibold text-canvas-900">Claude Kanban</h1>
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4672
|
+
<!-- Navigation Tabs -->
|
|
4673
|
+
<nav class="flex items-center gap-1 bg-canvas-100 rounded-lg p-1">
|
|
4674
|
+
<button onclick="state.currentView = 'board'; render();"
|
|
4675
|
+
class="px-3 py-1.5 text-sm rounded-md transition-colors \${state.currentView === 'board' ? 'bg-white shadow-sm text-canvas-900 font-medium' : 'text-canvas-500 hover:text-canvas-700'}">
|
|
4676
|
+
\u{1F4CB} Board
|
|
4677
|
+
</button>
|
|
4678
|
+
<button onclick="state.currentView = 'roadmap'; render();"
|
|
4679
|
+
class="px-3 py-1.5 text-sm rounded-md transition-colors \${state.currentView === 'roadmap' ? 'bg-white shadow-sm text-canvas-900 font-medium' : 'text-canvas-500 hover:text-canvas-700'}">
|
|
4680
|
+
\u{1F5FA}\uFE0F Roadmap
|
|
4681
|
+
</button>
|
|
4682
|
+
</nav>
|
|
4683
|
+
\${state.currentView === 'board' ? \`
|
|
4684
|
+
<div class="flex items-center gap-2">
|
|
4685
|
+
<input type="text"
|
|
4686
|
+
placeholder="Search tasks..."
|
|
4687
|
+
value="\${escapeHtml(state.searchQuery)}"
|
|
4688
|
+
oninput="state.searchQuery = this.value; render();"
|
|
4689
|
+
class="input text-sm py-1.5 w-48">
|
|
4690
|
+
</div>
|
|
4691
|
+
\` : ''}
|
|
4387
4692
|
</div>
|
|
4388
4693
|
<div class="flex items-center gap-2">
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4694
|
+
\${state.currentView === 'board' ? \`
|
|
4695
|
+
<button onclick="openPlanningModal();"
|
|
4696
|
+
class="btn px-4 py-2 text-sm bg-blue-500 hover:bg-blue-600 text-white \${state.planning ? 'opacity-50 cursor-not-allowed' : ''}"
|
|
4697
|
+
\${state.planning ? 'disabled' : ''}
|
|
4698
|
+
title="AI Task Planner">
|
|
4699
|
+
\u{1F3AF} \${state.planning ? 'Planning...' : 'Plan'}
|
|
4700
|
+
</button>
|
|
4701
|
+
<button onclick="state.showModal = 'new'; render();"
|
|
4702
|
+
class="btn btn-primary px-4 py-2 text-sm">
|
|
4703
|
+
+ Add Task
|
|
4704
|
+
</button>
|
|
4705
|
+
<button onclick="state.showModal = 'afk'; render();"
|
|
4706
|
+
class="btn btn-ghost px-3 py-2 text-sm \${state.afk.running ? 'text-status-success' : ''}"
|
|
4707
|
+
title="AFK Mode">
|
|
4708
|
+
\u{1F504} \${state.afk.running ? 'AFK On' : 'AFK'}
|
|
4709
|
+
</button>
|
|
4710
|
+
\` : \`
|
|
4711
|
+
<button onclick="state.showModal = 'roadmap'; render();"
|
|
4712
|
+
class="btn btn-primary px-4 py-2 text-sm \${state.roadmapGenerating ? 'opacity-50 cursor-not-allowed' : ''}"
|
|
4713
|
+
\${state.roadmapGenerating ? 'disabled' : ''}>
|
|
4714
|
+
\${state.roadmapGenerating ? '\u23F3 Generating...' : '\u{1F680} Generate Roadmap'}
|
|
4715
|
+
</button>
|
|
4716
|
+
\`}
|
|
4404
4717
|
</div>
|
|
4405
4718
|
</div>
|
|
4406
4719
|
</header>
|
|
4407
4720
|
|
|
4408
|
-
\${renderAFKBar()}
|
|
4721
|
+
\${state.currentView === 'board' ? renderAFKBar() : ''}
|
|
4409
4722
|
|
|
4410
|
-
<!-- Main Content Area
|
|
4723
|
+
<!-- Main Content Area -->
|
|
4411
4724
|
<div class="flex flex-1 overflow-hidden">
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
<
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4725
|
+
\${state.currentView === 'board' ? \`
|
|
4726
|
+
<!-- Kanban Board -->
|
|
4727
|
+
<main class="main-content overflow-x-auto p-6">
|
|
4728
|
+
<div class="flex gap-4">
|
|
4729
|
+
\${renderColumn('draft', 'To Do', filterTasks(state.tasks))}
|
|
4730
|
+
\${renderColumn('ready', 'Ready', filterTasks(state.tasks))}
|
|
4731
|
+
\${renderColumn('in_progress', 'In Progress', filterTasks(state.tasks))}
|
|
4732
|
+
\${renderColumn('completed', 'Done', filterTasks(state.tasks))}
|
|
4733
|
+
\${renderColumn('failed', 'Failed', filterTasks(state.tasks))}
|
|
4734
|
+
</div>
|
|
4735
|
+
</main>
|
|
4736
|
+
<!-- Side Panel (pushes content when open) -->
|
|
4737
|
+
\${hasSidePanel ? renderSidePanel() : ''}
|
|
4738
|
+
\` : \`
|
|
4739
|
+
<!-- Roadmap View -->
|
|
4740
|
+
\${renderRoadmap()}
|
|
4741
|
+
\`}
|
|
4425
4742
|
</div>
|
|
4426
4743
|
|
|
4427
4744
|
\${renderModal()}
|
|
@@ -4483,6 +4800,11 @@ window.closeSidePanel = closeSidePanel;
|
|
|
4483
4800
|
window.showTaskMenu = showTaskMenu;
|
|
4484
4801
|
window.filterTasks = filterTasks;
|
|
4485
4802
|
window.scrollSidePanelLog = scrollSidePanelLog;
|
|
4803
|
+
window.generateRoadmap = generateRoadmap;
|
|
4804
|
+
window.cancelRoadmap = cancelRoadmap;
|
|
4805
|
+
window.addFeatureToKanban = addFeatureToKanban;
|
|
4806
|
+
window.deleteRoadmapFeature = deleteRoadmapFeature;
|
|
4807
|
+
window.loadRoadmap = loadRoadmap;
|
|
4486
4808
|
|
|
4487
4809
|
// Keyboard shortcuts
|
|
4488
4810
|
document.addEventListener('keydown', (e) => {
|