claude-autopm 3.22.2 → 3.23.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.
|
@@ -429,6 +429,23 @@ function renderHTML() {
|
|
|
429
429
|
.panzoom-hint {
|
|
430
430
|
position: absolute; bottom: 8px; right: 12px; color: #484f58; font-size: 10px; pointer-events: none; z-index: 1;
|
|
431
431
|
}
|
|
432
|
+
/* Diagram card actions */
|
|
433
|
+
.diagram-grid-item .card-actions {
|
|
434
|
+
display: flex; gap: 6px; margin-top: 8px; border-top: 1px solid #21262d; padding-top: 8px;
|
|
435
|
+
}
|
|
436
|
+
.diagram-grid-item .card-actions button {
|
|
437
|
+
background: #21262d; color: #8b949e; border: 1px solid #30363d;
|
|
438
|
+
padding: 3px 10px; border-radius: 4px; cursor: pointer; font-size: 11px;
|
|
439
|
+
}
|
|
440
|
+
.diagram-grid-item .card-actions button:hover { background: #30363d; color: #c9d1d9; }
|
|
441
|
+
.diagram-grid-item .card-actions .btn-remove { color: #f85149; }
|
|
442
|
+
.diagram-grid-item .card-actions .btn-remove:hover { background: #3d1116; border-color: #f85149; }
|
|
443
|
+
/* Fullscreen editor */
|
|
444
|
+
.diagram-editor.fullscreen {
|
|
445
|
+
position: fixed; inset: 0; z-index: 998; background: #0d1117;
|
|
446
|
+
padding: 12px; display: flex; flex-direction: column;
|
|
447
|
+
}
|
|
448
|
+
.diagram-editor.fullscreen .diagram-split { min-height: 0; flex: 1; }
|
|
432
449
|
</style>
|
|
433
450
|
</head>
|
|
434
451
|
<body>
|
|
@@ -562,7 +579,27 @@ function renderHTML() {
|
|
|
562
579
|
<!-- Diagram list view -->
|
|
563
580
|
<div id="diagram-list-view">
|
|
564
581
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
|
|
565
|
-
<p class="desc" style="margin:0;">
|
|
582
|
+
<p class="desc" style="margin:0;">Project diagrams stored in <code>.claude/pm/diagrams/</code></p>
|
|
583
|
+
<button class="btn" onclick="showNewDiagramForm()">+ New Diagram</button>
|
|
584
|
+
</div>
|
|
585
|
+
<!-- New diagram form (hidden by default) -->
|
|
586
|
+
<div id="new-diagram-form" class="card" style="display:none;margin-bottom:16px;">
|
|
587
|
+
<div class="row">
|
|
588
|
+
<div><label>Name (slug)</label><input type="text" id="new-diagram-name" placeholder="e.g. frontend, backend, database"></div>
|
|
589
|
+
<div><label>Type</label><select id="new-diagram-type">
|
|
590
|
+
<option value="architecture">architecture</option>
|
|
591
|
+
<option value="modules">modules</option>
|
|
592
|
+
<option value="data-flow">data-flow</option>
|
|
593
|
+
<option value="dependencies">dependencies</option>
|
|
594
|
+
<option value="custom">custom</option>
|
|
595
|
+
</select></div>
|
|
596
|
+
</div>
|
|
597
|
+
<label>Description</label>
|
|
598
|
+
<input type="text" id="new-diagram-desc" placeholder="e.g. Frontend React component tree">
|
|
599
|
+
<div style="margin-top:12px;">
|
|
600
|
+
<button class="btn" onclick="createNewDiagram()">Create</button>
|
|
601
|
+
<button class="btn" style="background:#21262d;margin-left:6px;" onclick="hideNewDiagramForm()">Cancel</button>
|
|
602
|
+
</div>
|
|
566
603
|
</div>
|
|
567
604
|
<div id="diagram-grid" class="diagram-grid"></div>
|
|
568
605
|
</div>
|
|
@@ -572,15 +609,15 @@ function renderHTML() {
|
|
|
572
609
|
<div class="left">
|
|
573
610
|
<button class="btn-back" onclick="closeDiagramEditor()">← Back</button>
|
|
574
611
|
<strong id="editor-diagram-name" style="color:#c9d1d9;font-size:14px;"></strong>
|
|
575
|
-
<span id="editor-diagram-meta" style="color:#8b949e;font-size:11px;"></span>
|
|
576
612
|
<span id="editor-unsaved" style="color:#d29922;font-size:11px;display:none;">● unsaved</span>
|
|
577
613
|
</div>
|
|
578
614
|
<div class="right">
|
|
579
615
|
<button onclick="downloadCurrentDiagram()">↓ .mmd</button>
|
|
616
|
+
<button onclick="toggleEditorFullscreen()">⤢ Fullscreen</button>
|
|
580
617
|
<button class="btn-save" onclick="saveDiagram()">Save</button>
|
|
581
618
|
</div>
|
|
582
619
|
</div>
|
|
583
|
-
<div class="diagram-split">
|
|
620
|
+
<div class="diagram-split" id="diagram-split">
|
|
584
621
|
<div class="code-pane">
|
|
585
622
|
<div class="pane-header"><span>Mermaid Source</span></div>
|
|
586
623
|
<textarea id="diagram-code" spellcheck="false"></textarea>
|
|
@@ -654,16 +691,10 @@ function renderDiagramsTab() {
|
|
|
654
691
|
fetch('/api/diagrams', { headers: { 'Authorization': 'Bearer ' + TOKEN } })
|
|
655
692
|
.then(r => r.json())
|
|
656
693
|
.then(data => {
|
|
657
|
-
// Merge system-generated + project diagrams into one list
|
|
658
694
|
allDiagrams = [];
|
|
659
|
-
if (data.epicFlowHasData) {
|
|
660
|
-
allDiagrams.push({ name: 'epic-flow', content: data.epicFlow, type: 'system', meta: 'PRD → Epic → Tasks' });
|
|
661
|
-
}
|
|
662
|
-
allDiagrams.push({ name: 'plugins', content: data.pluginGraph, type: 'system', meta: 'Plugin dependencies' });
|
|
663
|
-
allDiagrams.push({ name: 'agents', content: data.agentTree, type: 'system', meta: 'Agent selection tree' });
|
|
664
695
|
if (data.projectDiagrams) {
|
|
665
696
|
data.projectDiagrams.forEach(d => {
|
|
666
|
-
allDiagrams.push({ name: d.name, content: d.content, type:
|
|
697
|
+
allDiagrams.push({ name: d.name, content: d.content, type: d.type || 'custom' });
|
|
667
698
|
});
|
|
668
699
|
}
|
|
669
700
|
renderDiagramGrid();
|
|
@@ -675,49 +706,112 @@ function renderDiagramGrid() {
|
|
|
675
706
|
const grid = document.getElementById('diagram-grid');
|
|
676
707
|
grid.textContent = '';
|
|
677
708
|
if (allDiagrams.length === 0) {
|
|
678
|
-
|
|
709
|
+
const msg = document.createElement('p');
|
|
710
|
+
msg.className = 'desc';
|
|
711
|
+
msg.textContent = 'No diagrams yet. Click "+ New Diagram" or run /pm:diagram-new <name>.';
|
|
712
|
+
grid.appendChild(msg);
|
|
679
713
|
return;
|
|
680
714
|
}
|
|
681
715
|
allDiagrams.forEach(d => {
|
|
682
716
|
const item = document.createElement('div');
|
|
683
717
|
item.className = 'diagram-grid-item';
|
|
684
|
-
item.setAttribute('role', 'button');
|
|
685
718
|
item.setAttribute('tabindex', '0');
|
|
686
|
-
item.onclick = function() { openDiagramEditor(d.name); };
|
|
687
|
-
item.onkeydown = function(e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openDiagramEditor(d.name); } };
|
|
688
719
|
const h4 = document.createElement('h4');
|
|
689
720
|
h4.textContent = d.name;
|
|
690
721
|
const meta = document.createElement('div');
|
|
691
722
|
meta.className = 'diagram-meta';
|
|
692
|
-
meta.textContent = d.type
|
|
723
|
+
meta.textContent = d.type;
|
|
693
724
|
const preview = document.createElement('div');
|
|
694
725
|
preview.className = 'diagram-preview';
|
|
695
726
|
const pre = document.createElement('pre');
|
|
696
727
|
pre.className = 'mermaid';
|
|
697
728
|
pre.textContent = d.content;
|
|
698
729
|
preview.appendChild(pre);
|
|
730
|
+
// Action buttons
|
|
731
|
+
const actions = document.createElement('div');
|
|
732
|
+
actions.className = 'card-actions';
|
|
733
|
+
const editBtn = document.createElement('button');
|
|
734
|
+
editBtn.textContent = 'Edit';
|
|
735
|
+
editBtn.onclick = function(e) { e.stopPropagation(); openDiagramEditor(d.name); };
|
|
736
|
+
const dlBtn = document.createElement('button');
|
|
737
|
+
dlBtn.textContent = '↓ .mmd';
|
|
738
|
+
dlBtn.onclick = function(e) { e.stopPropagation(); downloadDiagram(d.name, d.content); };
|
|
739
|
+
const rmBtn = document.createElement('button');
|
|
740
|
+
rmBtn.textContent = 'Remove';
|
|
741
|
+
rmBtn.className = 'btn-remove';
|
|
742
|
+
rmBtn.onclick = function(e) { e.stopPropagation(); removeDiagram(d.name); };
|
|
743
|
+
actions.appendChild(editBtn);
|
|
744
|
+
actions.appendChild(dlBtn);
|
|
745
|
+
actions.appendChild(rmBtn);
|
|
699
746
|
item.appendChild(h4);
|
|
700
747
|
item.appendChild(meta);
|
|
701
748
|
item.appendChild(preview);
|
|
749
|
+
item.appendChild(actions);
|
|
750
|
+
// Click card body (not actions) opens editor
|
|
751
|
+
item.onclick = function(e) { if (!e.target.closest('.card-actions')) openDiagramEditor(d.name); };
|
|
752
|
+
item.onkeydown = function(e) {
|
|
753
|
+
if (e.target && e.target.closest('.card-actions')) return;
|
|
754
|
+
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openDiagramEditor(d.name); }
|
|
755
|
+
};
|
|
702
756
|
grid.appendChild(item);
|
|
703
757
|
});
|
|
704
|
-
// Render mini previews
|
|
705
758
|
if (typeof mermaid !== 'undefined' && mermaid.run) {
|
|
706
759
|
mermaid.run({ nodes: grid.querySelectorAll('.mermaid') });
|
|
707
760
|
}
|
|
708
761
|
}
|
|
709
762
|
|
|
763
|
+
// New diagram form
|
|
764
|
+
function showNewDiagramForm() { document.getElementById('new-diagram-form').style.display = ''; }
|
|
765
|
+
function hideNewDiagramForm() { document.getElementById('new-diagram-form').style.display = 'none'; }
|
|
766
|
+
|
|
767
|
+
async function createNewDiagram() {
|
|
768
|
+
const name = document.getElementById('new-diagram-name').value.trim().replace(/[^a-zA-Z0-9_-]/g, '-');
|
|
769
|
+
if (!name) { toast('Name is required', false); return; }
|
|
770
|
+
if (allDiagrams.find(d => d.name === name)) { toast('Diagram "' + name + '" already exists', false); return; }
|
|
771
|
+
const type = document.getElementById('new-diagram-type').value;
|
|
772
|
+
const desc = document.getElementById('new-diagram-desc').value.trim();
|
|
773
|
+
const template = 'graph TD\\n A[' + esc(desc || name) + ']';
|
|
774
|
+
try {
|
|
775
|
+
await api('POST', '/api/diagrams/' + encodeURIComponent(name), { content: template, type: type, description: desc });
|
|
776
|
+
hideNewDiagramForm();
|
|
777
|
+
document.getElementById('new-diagram-name').value = '';
|
|
778
|
+
document.getElementById('new-diagram-desc').value = '';
|
|
779
|
+
diagramsLoaded = false;
|
|
780
|
+
renderDiagramsTab();
|
|
781
|
+
toast('Diagram "' + name + '" created', true);
|
|
782
|
+
} catch (e) { /* api() already shows toast */ }
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
async function removeDiagram(name) {
|
|
786
|
+
if (!confirm('Remove diagram "' + name + '"?')) return;
|
|
787
|
+
try {
|
|
788
|
+
await api('DELETE', '/api/diagrams/' + encodeURIComponent(name));
|
|
789
|
+
allDiagrams = allDiagrams.filter(d => d.name !== name);
|
|
790
|
+
renderDiagramGrid();
|
|
791
|
+
toast('Diagram removed', true);
|
|
792
|
+
} catch (e) { /* api() already shows toast */ }
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
function downloadDiagram(name, content) {
|
|
796
|
+
const safeName = name.replace(/[^a-zA-Z0-9_-]/g, '_') + '.mmd';
|
|
797
|
+
const blob = new Blob([content], { type: 'text/plain' });
|
|
798
|
+
const a = document.createElement('a');
|
|
799
|
+
const url = URL.createObjectURL(blob);
|
|
800
|
+
a.href = url; a.download = safeName; a.style.display = 'none';
|
|
801
|
+
document.body.appendChild(a);
|
|
802
|
+
try { a.click(); } finally { document.body.removeChild(a); setTimeout(function() { URL.revokeObjectURL(url); }, 100); }
|
|
803
|
+
}
|
|
804
|
+
|
|
710
805
|
function openDiagramEditor(name) {
|
|
711
806
|
const d = allDiagrams.find(x => x.name === name);
|
|
712
807
|
if (!d) return;
|
|
713
808
|
currentDiagramName = name;
|
|
714
809
|
currentDiagramSaved = d.content;
|
|
715
810
|
document.getElementById('editor-diagram-name').textContent = name;
|
|
716
|
-
document.getElementById('editor-diagram-meta').textContent = d.type === 'system' ? '(system — read-only)' : '';
|
|
717
811
|
document.getElementById('editor-unsaved').style.display = 'none';
|
|
718
812
|
const codeEl = document.getElementById('diagram-code');
|
|
719
813
|
codeEl.value = d.content;
|
|
720
|
-
codeEl.readOnly =
|
|
814
|
+
codeEl.readOnly = false;
|
|
721
815
|
document.getElementById('diagram-list-view').style.display = 'none';
|
|
722
816
|
document.getElementById('diagram-editor-view').classList.add('active');
|
|
723
817
|
renderPreview(d.content);
|
|
@@ -726,11 +820,19 @@ function openDiagramEditor(name) {
|
|
|
726
820
|
function closeDiagramEditor() {
|
|
727
821
|
clearTimeout(previewDebounce); previewDebounce = null;
|
|
728
822
|
if (panzoomInstance) { panzoomInstance.dispose(); panzoomInstance = null; }
|
|
729
|
-
document.getElementById('diagram-editor-view')
|
|
823
|
+
const editor = document.getElementById('diagram-editor-view');
|
|
824
|
+
editor.classList.remove('active', 'fullscreen');
|
|
730
825
|
document.getElementById('diagram-list-view').style.display = '';
|
|
731
826
|
currentDiagramName = null;
|
|
732
827
|
}
|
|
733
828
|
|
|
829
|
+
function toggleEditorFullscreen() {
|
|
830
|
+
document.getElementById('diagram-editor-view').classList.toggle('fullscreen');
|
|
831
|
+
// Re-render preview to fit new size
|
|
832
|
+
if (panzoomInstance) { panzoomInstance.dispose(); panzoomInstance = null; }
|
|
833
|
+
setTimeout(function() { renderPreview(document.getElementById('diagram-code').value); }, 100);
|
|
834
|
+
}
|
|
835
|
+
|
|
734
836
|
function renderPreview(source) {
|
|
735
837
|
const container = document.getElementById('diagram-preview-render');
|
|
736
838
|
container.textContent = '';
|
|
@@ -776,7 +878,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
776
878
|
async function saveDiagram() {
|
|
777
879
|
if (!currentDiagramName) return;
|
|
778
880
|
const d = allDiagrams.find(x => x.name === currentDiagramName);
|
|
779
|
-
if (d && d.type === 'system') { toast('System diagrams are read-only', false); return; }
|
|
780
881
|
const content = document.getElementById('diagram-code').value;
|
|
781
882
|
try {
|
|
782
883
|
await api('POST', '/api/diagrams/' + encodeURIComponent(currentDiagramName), { content: content });
|
|
@@ -787,7 +888,7 @@ async function saveDiagram() {
|
|
|
787
888
|
diagramsLoaded = false;
|
|
788
889
|
toast('Diagram saved', true);
|
|
789
890
|
} catch (e) {
|
|
790
|
-
|
|
891
|
+
/* api() already shows toast */
|
|
791
892
|
}
|
|
792
893
|
}
|
|
793
894
|
|
|
@@ -1273,6 +1374,8 @@ const server = http.createServer(async (req, res) => {
|
|
|
1273
1374
|
let meta = {};
|
|
1274
1375
|
try { meta = JSON.parse(fs.readFileSync(metaPath, 'utf8')); } catch {}
|
|
1275
1376
|
meta.name = meta.name || name;
|
|
1377
|
+
if (typeof body.type === 'string') meta.type = body.type.slice(0, 50);
|
|
1378
|
+
if (typeof body.description === 'string') meta.description = body.description.slice(0, 500);
|
|
1276
1379
|
meta.updated = new Date().toISOString().replace(/\\.\\d{3}Z$/, 'Z');
|
|
1277
1380
|
if (!meta.created) meta.created = meta.updated;
|
|
1278
1381
|
fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2), 'utf8');
|
package/package.json
CHANGED