egregore-artifacts 0.3.0 → 0.4.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/lib/parsers/board.js +34 -5
- package/lib/templates/board.js +293 -10
- package/package.json +1 -1
package/lib/parsers/board.js
CHANGED
|
@@ -25,7 +25,29 @@ export function parseBoard(input) {
|
|
|
25
25
|
|
|
26
26
|
const today = new Date().toISOString().split('T')[0];
|
|
27
27
|
|
|
28
|
-
//
|
|
28
|
+
// Compute most recent Monday for weekly Done-tab auto-cleanse
|
|
29
|
+
const now = new Date();
|
|
30
|
+
const dayOfWeek = now.getDay();
|
|
31
|
+
const daysSinceMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
|
|
32
|
+
const weekStart = new Date(now);
|
|
33
|
+
weekStart.setHours(0, 0, 0, 0);
|
|
34
|
+
weekStart.setDate(weekStart.getDate() - daysSinceMonday);
|
|
35
|
+
const weekStartStr = weekStart.toISOString().split('T')[0];
|
|
36
|
+
|
|
37
|
+
// Auto-archive: filter out done cards completed before this Monday
|
|
38
|
+
for (const activity of data.activities || []) {
|
|
39
|
+
const filterStale = (cards) => (cards || []).filter(
|
|
40
|
+
c => !(c.status === 'done' && c.completedAt && c.completedAt < weekStartStr)
|
|
41
|
+
);
|
|
42
|
+
if (activity.cards) activity.cards = filterStale(activity.cards);
|
|
43
|
+
if (activity.subactivities) {
|
|
44
|
+
for (const sub of activity.subactivities) {
|
|
45
|
+
if (sub.cards) sub.cards = filterStale(sub.cards);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Flatten all cards from the hierarchy
|
|
29
51
|
const allCards = [];
|
|
30
52
|
for (const activity of data.activities || []) {
|
|
31
53
|
if (activity.subactivities) {
|
|
@@ -40,9 +62,13 @@ export function parseBoard(input) {
|
|
|
40
62
|
}
|
|
41
63
|
}
|
|
42
64
|
|
|
43
|
-
//
|
|
65
|
+
// Split active vs done for different views
|
|
66
|
+
const activeCards = allCards.filter(c => c.status !== 'done');
|
|
67
|
+
const doneCards = allCards.filter(c => c.status === 'done');
|
|
68
|
+
|
|
69
|
+
// Group active cards by person
|
|
44
70
|
const personMap = {};
|
|
45
|
-
for (const card of
|
|
71
|
+
for (const card of activeCards) {
|
|
46
72
|
for (const owner of card.owners || []) {
|
|
47
73
|
if (!personMap[owner]) personMap[owner] = [];
|
|
48
74
|
personMap[owner].push(card);
|
|
@@ -66,8 +92,8 @@ export function parseBoard(input) {
|
|
|
66
92
|
}))
|
|
67
93
|
.sort((a, b) => b.stats.p0 - a.stats.p0 || b.stats.total - a.stats.total);
|
|
68
94
|
|
|
69
|
-
// Timeline: cards with dates, sorted by startDate
|
|
70
|
-
const timeline =
|
|
95
|
+
// Timeline: active cards with dates, sorted by startDate
|
|
96
|
+
const timeline = activeCards
|
|
71
97
|
.filter(c => c.startDate || c.dueDate)
|
|
72
98
|
.sort((a, b) => (a.startDate || a.dueDate || '').localeCompare(b.startDate || b.dueDate || ''));
|
|
73
99
|
|
|
@@ -102,6 +128,9 @@ export function parseBoard(input) {
|
|
|
102
128
|
updatedAt: data.updated,
|
|
103
129
|
activities: data.activities,
|
|
104
130
|
allCards,
|
|
131
|
+
activeCards,
|
|
132
|
+
doneCards,
|
|
133
|
+
weekStart: weekStartStr,
|
|
105
134
|
people,
|
|
106
135
|
timeline,
|
|
107
136
|
summary,
|
package/lib/templates/board.js
CHANGED
|
@@ -31,6 +31,11 @@ function BoardCardEl({ card, showActivity }) {
|
|
|
31
31
|
: 'var(--muted)';
|
|
32
32
|
|
|
33
33
|
return h('div', {
|
|
34
|
+
className: 'eg-board-card',
|
|
35
|
+
'data-card-id': card.id,
|
|
36
|
+
'data-original-status': card.status,
|
|
37
|
+
'data-original-priority': card.priority,
|
|
38
|
+
'data-original-owners': (card.owners || []).join(','),
|
|
34
39
|
style: {
|
|
35
40
|
background: 'var(--surface)',
|
|
36
41
|
border: '1px solid var(--border)',
|
|
@@ -38,10 +43,21 @@ function BoardCardEl({ card, showActivity }) {
|
|
|
38
43
|
borderRadius: '8px',
|
|
39
44
|
padding: '12px 16px',
|
|
40
45
|
marginBottom: '8px',
|
|
46
|
+
position: 'relative',
|
|
41
47
|
},
|
|
42
48
|
},
|
|
49
|
+
// Edit toggle (top-right)
|
|
50
|
+
h('button', {
|
|
51
|
+
className: 'eg-card-edit-btn',
|
|
52
|
+
style: {
|
|
53
|
+
position: 'absolute', top: '8px', right: '8px',
|
|
54
|
+
background: 'none', border: 'none', cursor: 'pointer',
|
|
55
|
+
fontFamily: fonts.mono, fontSize: '11px', color: 'var(--muted)',
|
|
56
|
+
padding: '2px 6px', borderRadius: '3px',
|
|
57
|
+
},
|
|
58
|
+
}, 'edit'),
|
|
43
59
|
// Title row
|
|
44
|
-
h('div', { style: { display: 'flex', alignItems: 'baseline', gap: '8px', marginBottom: '6px' } },
|
|
60
|
+
h('div', { style: { display: 'flex', alignItems: 'baseline', gap: '8px', marginBottom: '6px', paddingRight: '40px' } },
|
|
45
61
|
h('span', {
|
|
46
62
|
style: { fontFamily: fonts.mono, fontSize: '11px', color: pColor, fontWeight: 600, flexShrink: 0 },
|
|
47
63
|
}, PRIORITY_LABELS[card.priority]),
|
|
@@ -85,6 +101,54 @@ function BoardCardEl({ card, showActivity }) {
|
|
|
85
101
|
card.description && h('p', {
|
|
86
102
|
style: { fontSize: '13px', color: 'var(--muted)', margin: '6px 0 0', lineHeight: 1.4 },
|
|
87
103
|
}, card.description),
|
|
104
|
+
// Inline editor (hidden by default)
|
|
105
|
+
h('div', {
|
|
106
|
+
className: 'eg-card-editor',
|
|
107
|
+
style: {
|
|
108
|
+
display: 'none', marginTop: '10px', padding: '10px',
|
|
109
|
+
background: 'var(--neutral-chip)', borderRadius: '6px',
|
|
110
|
+
fontFamily: fonts.mono, fontSize: '12px',
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
h('div', { style: { display: 'flex', gap: '12px', alignItems: 'center', flexWrap: 'wrap' } },
|
|
114
|
+
h('label', null,
|
|
115
|
+
h('span', { style: { color: 'var(--muted)', marginRight: '6px' } }, 'status'),
|
|
116
|
+
h('select', {
|
|
117
|
+
className: 'eg-edit-status', 'data-card-id': card.id,
|
|
118
|
+
defaultValue: card.status,
|
|
119
|
+
style: { fontFamily: fonts.mono, fontSize: '12px', padding: '2px 4px' },
|
|
120
|
+
},
|
|
121
|
+
h('option', { value: 'todo' }, 'todo'),
|
|
122
|
+
h('option', { value: 'in-progress' }, 'in-progress'),
|
|
123
|
+
h('option', { value: 'review' }, 'review'),
|
|
124
|
+
h('option', { value: 'done' }, 'done'),
|
|
125
|
+
),
|
|
126
|
+
),
|
|
127
|
+
h('label', null,
|
|
128
|
+
h('span', { style: { color: 'var(--muted)', marginRight: '6px' } }, 'priority'),
|
|
129
|
+
h('select', {
|
|
130
|
+
className: 'eg-edit-priority', 'data-card-id': card.id,
|
|
131
|
+
defaultValue: String(card.priority),
|
|
132
|
+
style: { fontFamily: fonts.mono, fontSize: '12px', padding: '2px 4px' },
|
|
133
|
+
},
|
|
134
|
+
h('option', { value: '0' }, 'P0'),
|
|
135
|
+
h('option', { value: '1' }, 'P1'),
|
|
136
|
+
h('option', { value: '2' }, 'P2'),
|
|
137
|
+
h('option', { value: '3' }, 'P3'),
|
|
138
|
+
),
|
|
139
|
+
),
|
|
140
|
+
h('label', null,
|
|
141
|
+
h('span', { style: { color: 'var(--muted)', marginRight: '6px' } }, 'owners'),
|
|
142
|
+
h('input', {
|
|
143
|
+
className: 'eg-edit-owners', 'data-card-id': card.id,
|
|
144
|
+
type: 'text',
|
|
145
|
+
defaultValue: (card.owners || []).join(', '),
|
|
146
|
+
placeholder: 'comma-separated',
|
|
147
|
+
style: { fontFamily: fonts.mono, fontSize: '12px', padding: '2px 6px', width: '180px' },
|
|
148
|
+
}),
|
|
149
|
+
),
|
|
150
|
+
),
|
|
151
|
+
),
|
|
88
152
|
);
|
|
89
153
|
}
|
|
90
154
|
|
|
@@ -96,7 +160,7 @@ function ActivityView({ activities }) {
|
|
|
96
160
|
for (const activity of activities) {
|
|
97
161
|
if (activity.subactivities) {
|
|
98
162
|
for (const sub of activity.subactivities) {
|
|
99
|
-
const cards = sub.cards || [];
|
|
163
|
+
const cards = (sub.cards || []).filter(c => c.status !== 'done');
|
|
100
164
|
if (cards.length === 0) continue;
|
|
101
165
|
sections.push(
|
|
102
166
|
h('div', { key: `${activity.id}-${sub.id}`, style: { marginBottom: '1.5rem' } },
|
|
@@ -114,7 +178,7 @@ function ActivityView({ activities }) {
|
|
|
114
178
|
);
|
|
115
179
|
}
|
|
116
180
|
}
|
|
117
|
-
const cards = activity.cards || [];
|
|
181
|
+
const cards = (activity.cards || []).filter(c => c.status !== 'done');
|
|
118
182
|
if (cards.length === 0) continue;
|
|
119
183
|
sections.push(
|
|
120
184
|
h('div', { key: activity.id, style: { marginBottom: '1.5rem' } },
|
|
@@ -137,6 +201,85 @@ function ActivityView({ activities }) {
|
|
|
137
201
|
|
|
138
202
|
// ── Person View ────────────────────────────────────────────────
|
|
139
203
|
|
|
204
|
+
// ── Priority View ──────────────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
function PriorityView({ activeCards }) {
|
|
207
|
+
const groups = [
|
|
208
|
+
{ level: 0, label: 'P0 — Breaking', sub: 'Drop everything' },
|
|
209
|
+
{ level: 1, label: 'P1 — This cycle', sub: 'The working set' },
|
|
210
|
+
{ level: 2, label: 'P2 — Next cycle', sub: 'Scoped, waiting' },
|
|
211
|
+
{ level: 3, label: 'P3 — Parked', sub: 'Captured for later' },
|
|
212
|
+
];
|
|
213
|
+
return h('div', null,
|
|
214
|
+
...groups.map(g => {
|
|
215
|
+
const cards = (activeCards || []).filter(c => c.priority === g.level);
|
|
216
|
+
if (cards.length === 0) return null;
|
|
217
|
+
const pColor = PRIORITY_COLORS[g.level];
|
|
218
|
+
return h('div', { key: g.level, style: { marginBottom: '1.5rem' } },
|
|
219
|
+
h('div', {
|
|
220
|
+
style: {
|
|
221
|
+
display: 'flex', alignItems: 'baseline', gap: '10px',
|
|
222
|
+
marginBottom: '8px', paddingBottom: '4px',
|
|
223
|
+
borderBottom: `2px solid ${pColor}`,
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
h('span', {
|
|
227
|
+
style: {
|
|
228
|
+
fontFamily: fonts.mono, fontSize: '13px', fontWeight: 700,
|
|
229
|
+
color: pColor, textTransform: 'uppercase', letterSpacing: '0.06em',
|
|
230
|
+
},
|
|
231
|
+
}, g.label),
|
|
232
|
+
h('span', {
|
|
233
|
+
style: { fontFamily: fonts.mono, fontSize: '11px', color: 'var(--muted)' },
|
|
234
|
+
}, `${cards.length} · ${g.sub}`),
|
|
235
|
+
),
|
|
236
|
+
...cards.map((card, i) => h(BoardCardEl, { key: i, card, showActivity: true })),
|
|
237
|
+
);
|
|
238
|
+
}).filter(Boolean),
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ── Done View ──────────────────────────────────────────────────
|
|
243
|
+
|
|
244
|
+
function DoneView({ doneCards, weekStart }) {
|
|
245
|
+
if (!doneCards || doneCards.length === 0) {
|
|
246
|
+
return h('div', { style: { padding: '2rem', textAlign: 'center', color: 'var(--muted)', fontStyle: 'italic' } },
|
|
247
|
+
`Nothing done yet this week. The Done tab clears every Monday.`
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const byActivity = {};
|
|
251
|
+
for (const card of doneCards) {
|
|
252
|
+
const key = card.subactivity ? `${card.activity} · ${card.subactivity}` : card.activity;
|
|
253
|
+
if (!byActivity[key]) byActivity[key] = [];
|
|
254
|
+
byActivity[key].push(card);
|
|
255
|
+
}
|
|
256
|
+
return h('div', null,
|
|
257
|
+
h('div', {
|
|
258
|
+
style: {
|
|
259
|
+
fontFamily: fonts.mono, fontSize: '11px', color: 'var(--muted)',
|
|
260
|
+
marginBottom: '1rem', padding: '8px 12px', background: 'var(--neutral-chip)',
|
|
261
|
+
borderRadius: '6px',
|
|
262
|
+
},
|
|
263
|
+
}, `Showing ${doneCards.length} done this week (since ${weekStart}). This tab auto-clears every Monday.`),
|
|
264
|
+
...Object.entries(byActivity).map(([activityLabel, cards], i) =>
|
|
265
|
+
h('div', { key: i, style: { marginBottom: '1.5rem' } },
|
|
266
|
+
h('div', {
|
|
267
|
+
style: {
|
|
268
|
+
fontFamily: fonts.mono, fontSize: '12px', textTransform: 'uppercase',
|
|
269
|
+
letterSpacing: '0.06em', color: 'var(--muted)', marginBottom: '8px', paddingBottom: '4px',
|
|
270
|
+
borderBottom: '1px solid var(--border)',
|
|
271
|
+
},
|
|
272
|
+
}, activityLabel),
|
|
273
|
+
...cards.map((card, j) =>
|
|
274
|
+
h(BoardCardEl, { key: j, card, showActivity: false })
|
|
275
|
+
),
|
|
276
|
+
)
|
|
277
|
+
),
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ── Person View ────────────────────────────────────────────────
|
|
282
|
+
|
|
140
283
|
function PersonView({ people }) {
|
|
141
284
|
return h('div', null,
|
|
142
285
|
...people.map((person, pi) =>
|
|
@@ -350,25 +493,31 @@ function SummaryBar({ summary }) {
|
|
|
350
493
|
|
|
351
494
|
// ── Tab Switcher (rendered as HTML with inline JS) ─────────────
|
|
352
495
|
|
|
353
|
-
function ViewTabs() {
|
|
354
|
-
|
|
496
|
+
function ViewTabs({ doneCount }) {
|
|
497
|
+
const tabs = [
|
|
498
|
+
{ key: 'activity', label: 'Activity' },
|
|
499
|
+
{ key: 'priority', label: 'Priority' },
|
|
500
|
+
{ key: 'person', label: 'Person' },
|
|
501
|
+
{ key: 'timeline', label: 'Timeline' },
|
|
502
|
+
{ key: 'done', label: `Done${doneCount > 0 ? ` (${doneCount})` : ''}` },
|
|
503
|
+
];
|
|
355
504
|
return h('div', {
|
|
356
505
|
style: {
|
|
357
506
|
display: 'flex', gap: '4px', marginBottom: '1.5rem',
|
|
358
507
|
borderBottom: '2px solid var(--border)', paddingBottom: '0',
|
|
359
508
|
},
|
|
360
509
|
},
|
|
361
|
-
|
|
510
|
+
...tabs.map(tab =>
|
|
362
511
|
h('button', {
|
|
363
|
-
key: tab,
|
|
512
|
+
key: tab.key,
|
|
364
513
|
className: 'eg-board-tab',
|
|
365
|
-
'data-view': tab.
|
|
514
|
+
'data-view': tab.key,
|
|
366
515
|
style: {
|
|
367
516
|
fontFamily: fonts.mono, fontSize: '13px', padding: '8px 16px',
|
|
368
517
|
background: 'none', border: 'none', borderBottom: '2px solid transparent',
|
|
369
518
|
marginBottom: '-2px', cursor: 'pointer', color: 'var(--muted)',
|
|
370
519
|
},
|
|
371
|
-
}, tab)
|
|
520
|
+
}, tab.label)
|
|
372
521
|
),
|
|
373
522
|
);
|
|
374
523
|
}
|
|
@@ -396,7 +545,7 @@ export function boardTemplate(data) {
|
|
|
396
545
|
sections.push(h(SummaryBar, { key: 'summary', summary: data.summary }));
|
|
397
546
|
|
|
398
547
|
// Tab switcher
|
|
399
|
-
sections.push(h(ViewTabs, { key: 'tabs' }));
|
|
548
|
+
sections.push(h(ViewTabs, { key: 'tabs', doneCount: (data.doneCards || []).length }));
|
|
400
549
|
|
|
401
550
|
// Activity view (default visible)
|
|
402
551
|
sections.push(
|
|
@@ -405,6 +554,13 @@ export function boardTemplate(data) {
|
|
|
405
554
|
)
|
|
406
555
|
);
|
|
407
556
|
|
|
557
|
+
// Priority view (hidden by default)
|
|
558
|
+
sections.push(
|
|
559
|
+
h('div', { key: 'view-priority', className: 'eg-board-view', 'data-view': 'priority', style: { display: 'none' } },
|
|
560
|
+
h(PriorityView, { activeCards: data.activeCards })
|
|
561
|
+
)
|
|
562
|
+
);
|
|
563
|
+
|
|
408
564
|
// Person view (hidden by default)
|
|
409
565
|
sections.push(
|
|
410
566
|
h('div', { key: 'view-person', className: 'eg-board-view', 'data-view': 'person', style: { display: 'none' } },
|
|
@@ -419,6 +575,13 @@ export function boardTemplate(data) {
|
|
|
419
575
|
)
|
|
420
576
|
);
|
|
421
577
|
|
|
578
|
+
// Done view (hidden by default)
|
|
579
|
+
sections.push(
|
|
580
|
+
h('div', { key: 'view-done', className: 'eg-board-view', 'data-view': 'done', style: { display: 'none' } },
|
|
581
|
+
h(DoneView, { doneCards: data.doneCards || [], weekStart: data.weekStart })
|
|
582
|
+
)
|
|
583
|
+
);
|
|
584
|
+
|
|
422
585
|
// Inline script for tab switching
|
|
423
586
|
sections.push(
|
|
424
587
|
h('script', {
|
|
@@ -446,6 +609,126 @@ export function boardTemplate(data) {
|
|
|
446
609
|
})
|
|
447
610
|
);
|
|
448
611
|
|
|
612
|
+
// Floating "Copy changes" button — live editor output
|
|
613
|
+
sections.push(
|
|
614
|
+
h('button', {
|
|
615
|
+
key: 'copy-changes-btn',
|
|
616
|
+
id: 'eg-copy-changes-btn',
|
|
617
|
+
style: {
|
|
618
|
+
position: 'fixed', bottom: '24px', right: '24px', zIndex: 1000,
|
|
619
|
+
background: 'var(--terracotta)', color: 'white', border: 'none',
|
|
620
|
+
borderRadius: '24px', padding: '12px 20px', fontFamily: fonts.mono,
|
|
621
|
+
fontSize: '13px', fontWeight: 600, cursor: 'pointer',
|
|
622
|
+
boxShadow: '0 4px 12px rgba(0,0,0,0.15)', display: 'none',
|
|
623
|
+
},
|
|
624
|
+
}, 'Copy 0 changes')
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
// Interactive editor script — toggle editors, sync across duplicate cards,
|
|
628
|
+
// track changes, produce paste-back command on click
|
|
629
|
+
sections.push(
|
|
630
|
+
h('script', {
|
|
631
|
+
key: 'editor-script',
|
|
632
|
+
dangerouslySetInnerHTML: {
|
|
633
|
+
__html: `
|
|
634
|
+
(function() {
|
|
635
|
+
var editBtns = document.querySelectorAll('.eg-card-edit-btn');
|
|
636
|
+
var copyBtn = document.getElementById('eg-copy-changes-btn');
|
|
637
|
+
|
|
638
|
+
// Toggle editor — scope to clicked card's parent (each card appears in
|
|
639
|
+
// multiple views, so global querySelector would target the wrong one)
|
|
640
|
+
editBtns.forEach(function(btn) {
|
|
641
|
+
btn.addEventListener('click', function() {
|
|
642
|
+
var cardEl = btn.closest('.eg-board-card');
|
|
643
|
+
if (!cardEl) return;
|
|
644
|
+
var editor = cardEl.querySelector('.eg-card-editor');
|
|
645
|
+
if (!editor) return;
|
|
646
|
+
var isOpen = editor.style.display === 'block';
|
|
647
|
+
editor.style.display = isOpen ? 'none' : 'block';
|
|
648
|
+
btn.textContent = isOpen ? 'edit' : 'close';
|
|
649
|
+
});
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
// Sync edits across duplicate card instances (same card in Activity + Priority + Person)
|
|
653
|
+
function syncEdit(event) {
|
|
654
|
+
var input = event.target;
|
|
655
|
+
if (!input.classList) return;
|
|
656
|
+
var cls = input.className || '';
|
|
657
|
+
if (!cls.includes('eg-edit-')) return;
|
|
658
|
+
var cardId = input.dataset.cardId;
|
|
659
|
+
if (!cardId) return;
|
|
660
|
+
var className = cls.split(' ').find(function(c){return c.startsWith('eg-edit-');});
|
|
661
|
+
var selector = '.' + className + '[data-card-id="' + cardId + '"]';
|
|
662
|
+
document.querySelectorAll(selector).forEach(function(el) {
|
|
663
|
+
if (el !== input) el.value = input.value;
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
document.addEventListener('change', syncEdit);
|
|
667
|
+
document.addEventListener('input', syncEdit);
|
|
668
|
+
|
|
669
|
+
function collectChanges() {
|
|
670
|
+
var changes = [];
|
|
671
|
+
var seen = {};
|
|
672
|
+
document.querySelectorAll('.eg-board-card').forEach(function(card) {
|
|
673
|
+
var id = card.dataset.cardId;
|
|
674
|
+
if (seen[id]) return;
|
|
675
|
+
seen[id] = true;
|
|
676
|
+
var origStatus = card.dataset.originalStatus;
|
|
677
|
+
var origPriority = card.dataset.originalPriority;
|
|
678
|
+
var origOwners = card.dataset.originalOwners;
|
|
679
|
+
var statusEl = card.querySelector('.eg-edit-status');
|
|
680
|
+
var priorityEl = card.querySelector('.eg-edit-priority');
|
|
681
|
+
var ownersEl = card.querySelector('.eg-edit-owners');
|
|
682
|
+
if (!statusEl) return;
|
|
683
|
+
var newStatus = statusEl.value;
|
|
684
|
+
var newPriority = priorityEl.value;
|
|
685
|
+
var newOwners = ownersEl.value.split(',').map(function(s){return s.trim();}).filter(Boolean).join(',');
|
|
686
|
+
var cardChange = { id: id, changes: [] };
|
|
687
|
+
if (newStatus !== origStatus) cardChange.changes.push('status → ' + newStatus);
|
|
688
|
+
if (newPriority !== origPriority) cardChange.changes.push('priority → P' + newPriority);
|
|
689
|
+
if (newOwners !== origOwners) cardChange.changes.push('owners → [' + newOwners + ']');
|
|
690
|
+
if (cardChange.changes.length > 0) changes.push(cardChange);
|
|
691
|
+
});
|
|
692
|
+
return changes;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function updateCopyBtn() {
|
|
696
|
+
var changes = collectChanges();
|
|
697
|
+
if (changes.length === 0) {
|
|
698
|
+
copyBtn.style.display = 'none';
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
copyBtn.style.display = 'block';
|
|
702
|
+
var total = changes.reduce(function(n, c) { return n + c.changes.length; }, 0);
|
|
703
|
+
copyBtn.textContent = 'Copy ' + total + ' change' + (total === 1 ? '' : 's');
|
|
704
|
+
}
|
|
705
|
+
document.addEventListener('change', updateCopyBtn);
|
|
706
|
+
document.addEventListener('input', updateCopyBtn);
|
|
707
|
+
|
|
708
|
+
copyBtn.addEventListener('click', function() {
|
|
709
|
+
var changes = collectChanges();
|
|
710
|
+
var lines = ['Apply these board changes:', ''];
|
|
711
|
+
changes.forEach(function(c) {
|
|
712
|
+
lines.push('- ' + c.id + ':');
|
|
713
|
+
c.changes.forEach(function(ch) { lines.push(' ' + ch); });
|
|
714
|
+
});
|
|
715
|
+
var text = lines.join('\\n');
|
|
716
|
+
navigator.clipboard.writeText(text).then(function() {
|
|
717
|
+
var original = copyBtn.textContent;
|
|
718
|
+
copyBtn.textContent = '✓ Copied — paste in Claude';
|
|
719
|
+
copyBtn.style.background = '#2e7d32';
|
|
720
|
+
setTimeout(function() {
|
|
721
|
+
copyBtn.textContent = original;
|
|
722
|
+
copyBtn.style.background = 'var(--terracotta)';
|
|
723
|
+
}, 2000);
|
|
724
|
+
});
|
|
725
|
+
});
|
|
726
|
+
})();
|
|
727
|
+
`,
|
|
728
|
+
},
|
|
729
|
+
})
|
|
730
|
+
);
|
|
731
|
+
|
|
449
732
|
// Footer
|
|
450
733
|
sections.push(
|
|
451
734
|
h(ArtifactFooter, {
|