claude-kanban 0.6.0 → 0.6.1
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/bin/cli.js
CHANGED
|
@@ -3522,6 +3522,16 @@ let state = {
|
|
|
3522
3522
|
planningOutput: [],
|
|
3523
3523
|
sidePanelTab: 'logs', // 'logs' or 'details'
|
|
3524
3524
|
darkMode: localStorage.getItem('darkMode') === 'true', // Add dark mode state
|
|
3525
|
+
// View state (board or roadmap)
|
|
3526
|
+
currentView: 'board', // 'board' or 'roadmap'
|
|
3527
|
+
// Roadmap state
|
|
3528
|
+
roadmap: null,
|
|
3529
|
+
roadmapGenerating: false,
|
|
3530
|
+
roadmapProgress: null,
|
|
3531
|
+
roadmapError: null,
|
|
3532
|
+
roadmapSelectedFeature: null,
|
|
3533
|
+
roadmapEnableCompetitors: false,
|
|
3534
|
+
roadmapCustomPrompt: '',
|
|
3525
3535
|
};
|
|
3526
3536
|
|
|
3527
3537
|
// Toast notifications
|
|
@@ -3768,6 +3778,41 @@ socket.on('planning:cancelled', () => {
|
|
|
3768
3778
|
render();
|
|
3769
3779
|
});
|
|
3770
3780
|
|
|
3781
|
+
// Roadmap events
|
|
3782
|
+
socket.on('roadmap:started', () => {
|
|
3783
|
+
state.roadmapGenerating = true;
|
|
3784
|
+
state.roadmapProgress = { phase: 'analyzing', message: 'Starting roadmap generation...' };
|
|
3785
|
+
state.roadmapError = null;
|
|
3786
|
+
render();
|
|
3787
|
+
});
|
|
3788
|
+
|
|
3789
|
+
socket.on('roadmap:progress', ({ phase, message }) => {
|
|
3790
|
+
state.roadmapProgress = { phase, message };
|
|
3791
|
+
render();
|
|
3792
|
+
});
|
|
3793
|
+
|
|
3794
|
+
socket.on('roadmap:completed', ({ roadmap }) => {
|
|
3795
|
+
state.roadmap = roadmap;
|
|
3796
|
+
state.roadmapGenerating = false;
|
|
3797
|
+
state.roadmapProgress = null;
|
|
3798
|
+
state.showModal = null;
|
|
3799
|
+
showToast('Roadmap generated successfully!', 'success');
|
|
3800
|
+
render();
|
|
3801
|
+
});
|
|
3802
|
+
|
|
3803
|
+
socket.on('roadmap:failed', ({ error }) => {
|
|
3804
|
+
state.roadmapGenerating = false;
|
|
3805
|
+
state.roadmapProgress = null;
|
|
3806
|
+
state.roadmapError = error;
|
|
3807
|
+
showToast('Roadmap generation failed: ' + error, 'error');
|
|
3808
|
+
render();
|
|
3809
|
+
});
|
|
3810
|
+
|
|
3811
|
+
socket.on('roadmap:updated', (roadmap) => {
|
|
3812
|
+
state.roadmap = roadmap;
|
|
3813
|
+
render();
|
|
3814
|
+
});
|
|
3815
|
+
|
|
3771
3816
|
// Load templates
|
|
3772
3817
|
fetch('/api/templates').then(r => r.json()).then(data => {
|
|
3773
3818
|
state.templates = data.templates;
|
|
@@ -3859,6 +3904,69 @@ async function cancelPlanning() {
|
|
|
3859
3904
|
await fetch('/api/plan/cancel', { method: 'POST' });
|
|
3860
3905
|
}
|
|
3861
3906
|
|
|
3907
|
+
// Roadmap API functions
|
|
3908
|
+
async function loadRoadmap() {
|
|
3909
|
+
const res = await fetch('/api/roadmap');
|
|
3910
|
+
const data = await res.json();
|
|
3911
|
+
state.roadmap = data.roadmap;
|
|
3912
|
+
render();
|
|
3913
|
+
}
|
|
3914
|
+
|
|
3915
|
+
async function generateRoadmap() {
|
|
3916
|
+
state.roadmapGenerating = true;
|
|
3917
|
+
state.roadmapError = null;
|
|
3918
|
+
render();
|
|
3919
|
+
try {
|
|
3920
|
+
await fetch('/api/roadmap/generate', {
|
|
3921
|
+
method: 'POST',
|
|
3922
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3923
|
+
body: JSON.stringify({
|
|
3924
|
+
enableCompetitorResearch: state.roadmapEnableCompetitors,
|
|
3925
|
+
customPrompt: state.roadmapCustomPrompt || undefined
|
|
3926
|
+
})
|
|
3927
|
+
});
|
|
3928
|
+
} catch (e) {
|
|
3929
|
+
state.roadmapGenerating = false;
|
|
3930
|
+
state.roadmapError = e.message;
|
|
3931
|
+
render();
|
|
3932
|
+
}
|
|
3933
|
+
}
|
|
3934
|
+
|
|
3935
|
+
async function cancelRoadmap() {
|
|
3936
|
+
await fetch('/api/roadmap/cancel', { method: 'POST' });
|
|
3937
|
+
state.roadmapGenerating = false;
|
|
3938
|
+
state.roadmapProgress = null;
|
|
3939
|
+
render();
|
|
3940
|
+
}
|
|
3941
|
+
|
|
3942
|
+
async function addFeatureToKanban(featureId) {
|
|
3943
|
+
try {
|
|
3944
|
+
const res = await fetch('/api/roadmap/features/' + featureId + '/add-to-kanban', {
|
|
3945
|
+
method: 'POST'
|
|
3946
|
+
});
|
|
3947
|
+
const data = await res.json();
|
|
3948
|
+
if (data.task) {
|
|
3949
|
+
showToast('Feature added to kanban!', 'success');
|
|
3950
|
+
// Reload roadmap to update addedToKanban status
|
|
3951
|
+
await loadRoadmap();
|
|
3952
|
+
}
|
|
3953
|
+
} catch (e) {
|
|
3954
|
+
showToast('Failed to add feature: ' + e.message, 'error');
|
|
3955
|
+
}
|
|
3956
|
+
}
|
|
3957
|
+
|
|
3958
|
+
async function deleteRoadmapFeature(featureId) {
|
|
3959
|
+
try {
|
|
3960
|
+
await fetch('/api/roadmap/features/' + featureId, { method: 'DELETE' });
|
|
3961
|
+
showToast('Feature removed', 'info');
|
|
3962
|
+
} catch (e) {
|
|
3963
|
+
showToast('Failed to remove feature: ' + e.message, 'error');
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
|
|
3967
|
+
// Load roadmap on init
|
|
3968
|
+
loadRoadmap();
|
|
3969
|
+
|
|
3862
3970
|
// Enhanced Drag and drop
|
|
3863
3971
|
let draggedTask = null;
|
|
3864
3972
|
let draggedElement = null;
|
|
@@ -4031,6 +4139,185 @@ function showTaskMenu(taskId) {
|
|
|
4031
4139
|
openSidePanel(taskId);
|
|
4032
4140
|
}
|
|
4033
4141
|
|
|
4142
|
+
// Roadmap rendering functions
|
|
4143
|
+
function renderRoadmap() {
|
|
4144
|
+
if (state.roadmapGenerating) {
|
|
4145
|
+
return \`
|
|
4146
|
+
<div class="flex-1 flex items-center justify-center">
|
|
4147
|
+
<div class="text-center">
|
|
4148
|
+
<div class="animate-spin rounded-full h-12 w-12 border-b-2 border-accent mx-auto mb-4"></div>
|
|
4149
|
+
<h3 class="text-lg font-medium text-canvas-800 mb-2">Generating Roadmap...</h3>
|
|
4150
|
+
<p class="text-canvas-500">\${state.roadmapProgress?.message || 'Please wait...'}</p>
|
|
4151
|
+
<button onclick="cancelRoadmap()" class="btn btn-ghost mt-4 text-sm">Cancel</button>
|
|
4152
|
+
</div>
|
|
4153
|
+
</div>
|
|
4154
|
+
\`;
|
|
4155
|
+
}
|
|
4156
|
+
|
|
4157
|
+
if (!state.roadmap) {
|
|
4158
|
+
return \`
|
|
4159
|
+
<div class="flex-1 flex items-center justify-center">
|
|
4160
|
+
<div class="text-center max-w-md">
|
|
4161
|
+
<div class="text-6xl mb-4">\u{1F5FA}\uFE0F</div>
|
|
4162
|
+
<h3 class="text-xl font-semibold text-canvas-800 mb-2">No Roadmap Yet</h3>
|
|
4163
|
+
<p class="text-canvas-500 mb-6">Generate a strategic feature roadmap using AI to analyze your project and suggest features.</p>
|
|
4164
|
+
<button onclick="state.showModal = 'roadmap'; render();" class="btn btn-primary px-6 py-2">
|
|
4165
|
+
\u{1F680} Generate Roadmap
|
|
4166
|
+
</button>
|
|
4167
|
+
</div>
|
|
4168
|
+
</div>
|
|
4169
|
+
\`;
|
|
4170
|
+
}
|
|
4171
|
+
|
|
4172
|
+
const roadmap = state.roadmap;
|
|
4173
|
+
const phases = roadmap.phases || [];
|
|
4174
|
+
const features = roadmap.features || [];
|
|
4175
|
+
|
|
4176
|
+
return \`
|
|
4177
|
+
<div class="flex-1 overflow-y-auto p-6">
|
|
4178
|
+
<!-- Roadmap Header -->
|
|
4179
|
+
<div class="mb-6 flex items-start justify-between">
|
|
4180
|
+
<div>
|
|
4181
|
+
<h2 class="text-2xl font-semibold text-canvas-900">\${escapeHtml(roadmap.projectName)} Roadmap</h2>
|
|
4182
|
+
<p class="text-canvas-500 mt-1">\${escapeHtml(roadmap.projectDescription || '')}</p>
|
|
4183
|
+
<p class="text-sm text-canvas-400 mt-2">Target: \${escapeHtml(roadmap.targetAudience || 'Developers')}</p>
|
|
4184
|
+
</div>
|
|
4185
|
+
<div class="flex gap-2">
|
|
4186
|
+
<button onclick="state.showModal = 'roadmap'; render();" class="btn btn-ghost text-sm">
|
|
4187
|
+
\u{1F504} Regenerate
|
|
4188
|
+
</button>
|
|
4189
|
+
</div>
|
|
4190
|
+
</div>
|
|
4191
|
+
|
|
4192
|
+
<!-- Competitors (if available) -->
|
|
4193
|
+
\${roadmap.competitors && roadmap.competitors.length > 0 ? \`
|
|
4194
|
+
<div class="mb-6">
|
|
4195
|
+
<h3 class="text-sm font-medium text-canvas-700 mb-2">Competitor Insights</h3>
|
|
4196
|
+
<div class="flex gap-2 flex-wrap">
|
|
4197
|
+
\${roadmap.competitors.map(c => \`
|
|
4198
|
+
<span class="px-3 py-1 bg-canvas-100 rounded-full text-sm text-canvas-600">
|
|
4199
|
+
\${escapeHtml(c.name)}
|
|
4200
|
+
</span>
|
|
4201
|
+
\`).join('')}
|
|
4202
|
+
</div>
|
|
4203
|
+
</div>
|
|
4204
|
+
\` : ''}
|
|
4205
|
+
|
|
4206
|
+
<!-- Phases -->
|
|
4207
|
+
<div class="space-y-8">
|
|
4208
|
+
\${phases.map(phase => {
|
|
4209
|
+
const phaseFeatures = features.filter(f => f.phase === phase.name);
|
|
4210
|
+
return \`
|
|
4211
|
+
<div class="phase-section">
|
|
4212
|
+
<div class="flex items-center gap-3 mb-4">
|
|
4213
|
+
<h3 class="text-lg font-semibold text-canvas-800">\${escapeHtml(phase.name)}</h3>
|
|
4214
|
+
<span class="text-xs bg-canvas-100 px-2 py-0.5 rounded-full text-canvas-500">\${phaseFeatures.length} features</span>
|
|
4215
|
+
</div>
|
|
4216
|
+
<p class="text-sm text-canvas-500 mb-4">\${escapeHtml(phase.description || '')}</p>
|
|
4217
|
+
<div class="grid gap-3 md:grid-cols-2 lg:grid-cols-3">
|
|
4218
|
+
\${phaseFeatures.map(f => renderRoadmapFeature(f)).join('')}
|
|
4219
|
+
</div>
|
|
4220
|
+
</div>
|
|
4221
|
+
\`;
|
|
4222
|
+
}).join('')}
|
|
4223
|
+
</div>
|
|
4224
|
+
</div>
|
|
4225
|
+
\`;
|
|
4226
|
+
}
|
|
4227
|
+
|
|
4228
|
+
function renderRoadmapFeature(feature) {
|
|
4229
|
+
const priorityColors = {
|
|
4230
|
+
must: 'bg-red-100 text-red-700',
|
|
4231
|
+
should: 'bg-orange-100 text-orange-700',
|
|
4232
|
+
could: 'bg-blue-100 text-blue-700',
|
|
4233
|
+
wont: 'bg-gray-100 text-gray-500'
|
|
4234
|
+
};
|
|
4235
|
+
const priorityLabels = {
|
|
4236
|
+
must: 'Must Have',
|
|
4237
|
+
should: 'Should Have',
|
|
4238
|
+
could: 'Could Have',
|
|
4239
|
+
wont: "Won't Have"
|
|
4240
|
+
};
|
|
4241
|
+
const effortIcons = {
|
|
4242
|
+
low: '\u26A1',
|
|
4243
|
+
medium: '\u23F1\uFE0F',
|
|
4244
|
+
high: '\u{1F3CB}\uFE0F'
|
|
4245
|
+
};
|
|
4246
|
+
const impactIcons = {
|
|
4247
|
+
low: '\u{1F4C9}',
|
|
4248
|
+
medium: '\u{1F4CA}',
|
|
4249
|
+
high: '\u{1F4C8}'
|
|
4250
|
+
};
|
|
4251
|
+
|
|
4252
|
+
return \`
|
|
4253
|
+
<div class="card p-4 \${feature.addedToKanban ? 'opacity-60' : ''}" onclick="state.roadmapSelectedFeature = '\${feature.id}'; render();">
|
|
4254
|
+
<div class="flex items-start justify-between mb-2">
|
|
4255
|
+
<h4 class="font-medium text-canvas-800 text-sm">\${escapeHtml(feature.title)}</h4>
|
|
4256
|
+
<span class="text-xs px-2 py-0.5 rounded-full \${priorityColors[feature.priority] || 'bg-gray-100'}">\${priorityLabels[feature.priority] || feature.priority}</span>
|
|
4257
|
+
</div>
|
|
4258
|
+
<p class="text-xs text-canvas-500 mb-3 line-clamp-2">\${escapeHtml(feature.description)}</p>
|
|
4259
|
+
<div class="flex items-center justify-between">
|
|
4260
|
+
<div class="flex gap-2 text-xs text-canvas-400">
|
|
4261
|
+
<span title="Effort">\${effortIcons[feature.effort] || '\u23F1\uFE0F'} \${feature.effort}</span>
|
|
4262
|
+
<span title="Impact">\${impactIcons[feature.impact] || '\u{1F4CA}'} \${feature.impact}</span>
|
|
4263
|
+
</div>
|
|
4264
|
+
\${feature.addedToKanban ? \`
|
|
4265
|
+
<span class="text-xs text-green-600">\u2713 Added</span>
|
|
4266
|
+
\` : \`
|
|
4267
|
+
<button onclick="event.stopPropagation(); addFeatureToKanban('\${feature.id}')" class="text-xs text-accent hover:underline">+ Add to Kanban</button>
|
|
4268
|
+
\`}
|
|
4269
|
+
</div>
|
|
4270
|
+
</div>
|
|
4271
|
+
\`;
|
|
4272
|
+
}
|
|
4273
|
+
|
|
4274
|
+
function renderRoadmapModal() {
|
|
4275
|
+
return \`
|
|
4276
|
+
<div class="modal-backdrop" onclick="state.showModal = null; render();">
|
|
4277
|
+
<div class="modal-content max-w-lg" onclick="event.stopPropagation();">
|
|
4278
|
+
<div class="modal-header">
|
|
4279
|
+
<h2 class="modal-title">Generate Roadmap</h2>
|
|
4280
|
+
<button onclick="state.showModal = null; render();" class="modal-close">\xD7</button>
|
|
4281
|
+
</div>
|
|
4282
|
+
<div class="modal-body">
|
|
4283
|
+
<p class="text-sm text-canvas-500 mb-4">
|
|
4284
|
+
Generate a strategic feature roadmap by analyzing your project structure and optionally researching competitors.
|
|
4285
|
+
</p>
|
|
4286
|
+
|
|
4287
|
+
<div class="space-y-4">
|
|
4288
|
+
<label class="flex items-center gap-3 cursor-pointer">
|
|
4289
|
+
<input type="checkbox"
|
|
4290
|
+
\${state.roadmapEnableCompetitors ? 'checked' : ''}
|
|
4291
|
+
onchange="state.roadmapEnableCompetitors = this.checked; render();"
|
|
4292
|
+
class="w-4 h-4 accent-accent">
|
|
4293
|
+
<div>
|
|
4294
|
+
<span class="text-sm font-medium text-canvas-700">Enable competitor research</span>
|
|
4295
|
+
<p class="text-xs text-canvas-400">Use web search to analyze competitors (takes longer)</p>
|
|
4296
|
+
</div>
|
|
4297
|
+
</label>
|
|
4298
|
+
|
|
4299
|
+
<div>
|
|
4300
|
+
<label class="text-sm font-medium text-canvas-700">Additional context (optional)</label>
|
|
4301
|
+
<textarea
|
|
4302
|
+
class="input w-full mt-1 text-sm"
|
|
4303
|
+
rows="3"
|
|
4304
|
+
placeholder="E.g., Focus on mobile features, target enterprise users..."
|
|
4305
|
+
oninput="state.roadmapCustomPrompt = this.value;"
|
|
4306
|
+
>\${escapeHtml(state.roadmapCustomPrompt)}</textarea>
|
|
4307
|
+
</div>
|
|
4308
|
+
</div>
|
|
4309
|
+
</div>
|
|
4310
|
+
<div class="modal-footer">
|
|
4311
|
+
<button onclick="state.showModal = null; render();" class="btn btn-ghost">Cancel</button>
|
|
4312
|
+
<button onclick="generateRoadmap(); state.showModal = null; render();" class="btn btn-primary">
|
|
4313
|
+
\u{1F680} Generate Roadmap
|
|
4314
|
+
</button>
|
|
4315
|
+
</div>
|
|
4316
|
+
</div>
|
|
4317
|
+
</div>
|
|
4318
|
+
\`;
|
|
4319
|
+
}
|
|
4320
|
+
|
|
4034
4321
|
function renderColumn(status, title, tasks) {
|
|
4035
4322
|
const columnTasks = tasks.filter(t => t.status === status);
|
|
4036
4323
|
const statusLabels = {
|
|
@@ -4377,6 +4664,11 @@ function renderModal() {
|
|
|
4377
4664
|
\`;
|
|
4378
4665
|
}
|
|
4379
4666
|
|
|
4667
|
+
// Roadmap generation modal
|
|
4668
|
+
if (state.showModal === 'roadmap') {
|
|
4669
|
+
return renderRoadmapModal();
|
|
4670
|
+
}
|
|
4671
|
+
|
|
4380
4672
|
return '';
|
|
4381
4673
|
}
|
|
4382
4674
|
|
|
@@ -4696,7 +4988,7 @@ function escapeHtml(str) {
|
|
|
4696
4988
|
// Main render
|
|
4697
4989
|
function render() {
|
|
4698
4990
|
const app = document.getElementById('app');
|
|
4699
|
-
const hasSidePanel = state.sidePanel !== null;
|
|
4991
|
+
const hasSidePanel = state.sidePanel !== null && state.currentView === 'board';
|
|
4700
4992
|
|
|
4701
4993
|
app.innerHTML = \`
|
|
4702
4994
|
<div class="min-h-screen flex flex-col bg-canvas-50">
|
|
@@ -4705,51 +4997,76 @@ function render() {
|
|
|
4705
4997
|
<div class="px-6 py-3 flex items-center justify-between">
|
|
4706
4998
|
<div class="flex items-center gap-6">
|
|
4707
4999
|
<h1 class="text-lg font-semibold text-canvas-900">Claude Kanban</h1>
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
5000
|
+
<!-- Navigation Tabs -->
|
|
5001
|
+
<nav class="flex items-center gap-1 bg-canvas-100 rounded-lg p-1">
|
|
5002
|
+
<button onclick="state.currentView = 'board'; render();"
|
|
5003
|
+
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'}">
|
|
5004
|
+
\u{1F4CB} Board
|
|
5005
|
+
</button>
|
|
5006
|
+
<button onclick="state.currentView = 'roadmap'; render();"
|
|
5007
|
+
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'}">
|
|
5008
|
+
\u{1F5FA}\uFE0F Roadmap
|
|
5009
|
+
</button>
|
|
5010
|
+
</nav>
|
|
5011
|
+
\${state.currentView === 'board' ? \`
|
|
5012
|
+
<div class="flex items-center gap-2">
|
|
5013
|
+
<input type="text"
|
|
5014
|
+
placeholder="Search tasks..."
|
|
5015
|
+
value="\${escapeHtml(state.searchQuery)}"
|
|
5016
|
+
oninput="state.searchQuery = this.value; render();"
|
|
5017
|
+
class="input text-sm py-1.5 w-48">
|
|
5018
|
+
</div>
|
|
5019
|
+
\` : ''}
|
|
4715
5020
|
</div>
|
|
4716
5021
|
<div class="flex items-center gap-2">
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
5022
|
+
\${state.currentView === 'board' ? \`
|
|
5023
|
+
<button onclick="openPlanningModal();"
|
|
5024
|
+
class="btn px-4 py-2 text-sm bg-blue-500 hover:bg-blue-600 text-white \${state.planning ? 'opacity-50 cursor-not-allowed' : ''}"
|
|
5025
|
+
\${state.planning ? 'disabled' : ''}
|
|
5026
|
+
title="AI Task Planner">
|
|
5027
|
+
\u{1F3AF} \${state.planning ? 'Planning...' : 'Plan'}
|
|
5028
|
+
</button>
|
|
5029
|
+
<button onclick="state.showModal = 'new'; render();"
|
|
5030
|
+
class="btn btn-primary px-4 py-2 text-sm">
|
|
5031
|
+
+ Add Task
|
|
5032
|
+
</button>
|
|
5033
|
+
<button onclick="state.showModal = 'afk'; render();"
|
|
5034
|
+
class="btn btn-ghost px-3 py-2 text-sm \${state.afk.running ? 'text-status-success' : ''}"
|
|
5035
|
+
title="AFK Mode">
|
|
5036
|
+
\u{1F504} \${state.afk.running ? 'AFK On' : 'AFK'}
|
|
5037
|
+
</button>
|
|
5038
|
+
\` : \`
|
|
5039
|
+
<button onclick="state.showModal = 'roadmap'; render();"
|
|
5040
|
+
class="btn btn-primary px-4 py-2 text-sm \${state.roadmapGenerating ? 'opacity-50 cursor-not-allowed' : ''}"
|
|
5041
|
+
\${state.roadmapGenerating ? 'disabled' : ''}>
|
|
5042
|
+
\${state.roadmapGenerating ? '\u23F3 Generating...' : '\u{1F680} Generate Roadmap'}
|
|
5043
|
+
</button>
|
|
5044
|
+
\`}
|
|
4732
5045
|
</div>
|
|
4733
5046
|
</div>
|
|
4734
5047
|
</header>
|
|
4735
5048
|
|
|
4736
|
-
\${renderAFKBar()}
|
|
5049
|
+
\${state.currentView === 'board' ? renderAFKBar() : ''}
|
|
4737
5050
|
|
|
4738
|
-
<!-- Main Content Area
|
|
5051
|
+
<!-- Main Content Area -->
|
|
4739
5052
|
<div class="flex flex-1 overflow-hidden">
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
<
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
5053
|
+
\${state.currentView === 'board' ? \`
|
|
5054
|
+
<!-- Kanban Board -->
|
|
5055
|
+
<main class="main-content overflow-x-auto p-6">
|
|
5056
|
+
<div class="flex gap-4">
|
|
5057
|
+
\${renderColumn('draft', 'To Do', filterTasks(state.tasks))}
|
|
5058
|
+
\${renderColumn('ready', 'Ready', filterTasks(state.tasks))}
|
|
5059
|
+
\${renderColumn('in_progress', 'In Progress', filterTasks(state.tasks))}
|
|
5060
|
+
\${renderColumn('completed', 'Done', filterTasks(state.tasks))}
|
|
5061
|
+
\${renderColumn('failed', 'Failed', filterTasks(state.tasks))}
|
|
5062
|
+
</div>
|
|
5063
|
+
</main>
|
|
5064
|
+
<!-- Side Panel (pushes content when open) -->
|
|
5065
|
+
\${hasSidePanel ? renderSidePanel() : ''}
|
|
5066
|
+
\` : \`
|
|
5067
|
+
<!-- Roadmap View -->
|
|
5068
|
+
\${renderRoadmap()}
|
|
5069
|
+
\`}
|
|
4753
5070
|
</div>
|
|
4754
5071
|
|
|
4755
5072
|
\${renderModal()}
|
|
@@ -4811,6 +5128,11 @@ window.closeSidePanel = closeSidePanel;
|
|
|
4811
5128
|
window.showTaskMenu = showTaskMenu;
|
|
4812
5129
|
window.filterTasks = filterTasks;
|
|
4813
5130
|
window.scrollSidePanelLog = scrollSidePanelLog;
|
|
5131
|
+
window.generateRoadmap = generateRoadmap;
|
|
5132
|
+
window.cancelRoadmap = cancelRoadmap;
|
|
5133
|
+
window.addFeatureToKanban = addFeatureToKanban;
|
|
5134
|
+
window.deleteRoadmapFeature = deleteRoadmapFeature;
|
|
5135
|
+
window.loadRoadmap = loadRoadmap;
|
|
4814
5136
|
|
|
4815
5137
|
// Keyboard shortcuts
|
|
4816
5138
|
document.addEventListener('keydown', (e) => {
|