egregore-artifacts 0.2.0 → 0.3.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/bin/cli.js CHANGED
@@ -4,7 +4,7 @@ import { execSync } from 'node:child_process';
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
6
6
 
7
- const KNOWN_TYPES = ['quest', 'handoff', 'activity', 'document'];
7
+ const KNOWN_TYPES = ['quest', 'handoff', 'activity', 'document', 'board', 'network'];
8
8
  const args = process.argv.slice(2);
9
9
 
10
10
  let type, filePath;
@@ -47,7 +47,7 @@ if (KNOWN_TYPES.includes(positional[0])) {
47
47
  type = inferType(filePath);
48
48
  }
49
49
 
50
- if (!filePath && type !== 'activity') {
50
+ if (!filePath && type !== 'activity' && type !== 'board' && type !== 'network') {
51
51
  console.error(`✗ Missing file path for type "${type}"`);
52
52
  process.exit(1);
53
53
  }
@@ -82,7 +82,7 @@ function resolveFile(fp) {
82
82
  }
83
83
 
84
84
  try {
85
- const input = type === 'activity' ? (filePath || 'live') : resolveFile(filePath);
85
+ const input = (type === 'activity' || type === 'board' || type === 'network') ? (filePath || 'live') : resolveFile(filePath);
86
86
  const html = await generateArtifact(type, input);
87
87
  const slug = filePath ? filePath.split('/').pop().replace('.md', '') : new Date().toISOString().split('T')[0];
88
88
 
package/lib/components.js CHANGED
@@ -74,7 +74,7 @@ export function ArtifactList({ artifacts }) {
74
74
  h('span', { className: 'eg-artifact-date' }, a.date),
75
75
  h('span', { className: 'eg-artifact-type' }, a.type),
76
76
  h('span', { className: 'eg-artifact-title' }, a.title),
77
- a.author && h('span', { style: { color: colors.muted, fontSize: '13px', fontFamily: fonts.mono } }, `(${a.author})`),
77
+ a.author && h('span', { style: { color: 'var(--muted)', fontSize: '13px', fontFamily: fonts.mono } }, `(${a.author})`),
78
78
  )
79
79
  ),
80
80
  );
@@ -92,7 +92,7 @@ export function ContributorRow({ contributors }) {
92
92
  (c.name || '?')[0].toUpperCase()
93
93
  ),
94
94
  h('span', null, c.name),
95
- c.role && h('span', { style: { color: colors.muted, fontSize: '12px' } }, c.role),
95
+ c.role && h('span', { style: { color: 'var(--muted)', fontSize: '12px' } }, c.role),
96
96
  )
97
97
  ),
98
98
  );
package/lib/index.js CHANGED
@@ -7,14 +7,18 @@ import { parseQuest } from './parsers/quest.js';
7
7
  import { parseHandoff } from './parsers/handoff.js';
8
8
  import { parseActivity } from './parsers/activity.js';
9
9
  import { parseDocument } from './parsers/document.js';
10
+ import { parseBoard } from './parsers/board.js';
11
+ import { parseNetwork } from './parsers/network.js';
10
12
  import { questTemplate } from './templates/quest.js';
11
13
  import { handoffTemplate } from './templates/handoff.js';
12
14
  import { activityTemplate } from './templates/activity.js';
13
15
  import { documentTemplate } from './templates/document.js';
16
+ import { boardTemplate } from './templates/board.js';
17
+ import { networkTemplate } from './templates/network.js';
14
18
  import { openInBrowser } from './open.js';
15
19
 
16
- const PARSERS = { quest: parseQuest, handoff: parseHandoff, activity: parseActivity, document: parseDocument };
17
- const TEMPLATES = { quest: questTemplate, handoff: handoffTemplate, activity: activityTemplate, document: documentTemplate };
20
+ const PARSERS = { quest: parseQuest, handoff: parseHandoff, activity: parseActivity, document: parseDocument, board: parseBoard, network: parseNetwork };
21
+ const TEMPLATES = { quest: questTemplate, handoff: handoffTemplate, activity: activityTemplate, document: documentTemplate, board: boardTemplate, network: networkTemplate };
18
22
 
19
23
  export async function generateArtifact(type, input) {
20
24
  const template = TEMPLATES[type];
package/lib/markdown.js CHANGED
@@ -26,6 +26,40 @@ export function renderMarkdown(text) {
26
26
  continue;
27
27
  }
28
28
 
29
+ // H1 heading (rare inside a document body; render as section-sized)
30
+ if (line.startsWith('# ')) {
31
+ elements.push(h('h2', {
32
+ key: elements.length,
33
+ style: {
34
+ fontFamily: fonts.serif,
35
+ fontSize: '28px',
36
+ fontWeight: 400,
37
+ lineHeight: 1.2,
38
+ margin: '1.75rem 0 0.75rem',
39
+ color: 'var(--black)',
40
+ },
41
+ }, inlineMarkdown(line.slice(2))));
42
+ i++;
43
+ continue;
44
+ }
45
+
46
+ // H2 heading
47
+ if (line.startsWith('## ')) {
48
+ elements.push(h('h3', {
49
+ key: elements.length,
50
+ style: {
51
+ fontFamily: fonts.serif,
52
+ fontSize: '22px',
53
+ fontWeight: 400,
54
+ lineHeight: 1.25,
55
+ margin: '1.5rem 0 0.5rem',
56
+ color: 'var(--black)',
57
+ },
58
+ }, inlineMarkdown(line.slice(3))));
59
+ i++;
60
+ continue;
61
+ }
62
+
29
63
  // H3 heading
30
64
  if (line.startsWith('### ')) {
31
65
  elements.push(h('h3', {
@@ -36,7 +70,7 @@ export function renderMarkdown(text) {
36
70
  fontWeight: 600,
37
71
  lineHeight: 1.3,
38
72
  margin: '1.5rem 0 0.5rem',
39
- color: colors.black,
73
+ color: 'var(--black)',
40
74
  },
41
75
  }, inlineMarkdown(line.slice(4))));
42
76
  i++;
@@ -52,7 +86,7 @@ export function renderMarkdown(text) {
52
86
  fontSize: '15px',
53
87
  fontWeight: 600,
54
88
  margin: '1rem 0 0.25rem',
55
- color: colors.black,
89
+ color: 'var(--black)',
56
90
  },
57
91
  }, inlineMarkdown(line.slice(5))));
58
92
  i++;
@@ -79,7 +113,7 @@ export function renderMarkdown(text) {
79
113
  marginBottom: '0.35rem',
80
114
  fontSize: '15px',
81
115
  lineHeight: 1.55,
82
- color: colors.dark,
116
+ color: 'var(--dark)',
83
117
  },
84
118
  },
85
119
  h('span', {
@@ -90,7 +124,7 @@ export function renderMarkdown(text) {
90
124
  width: '5px',
91
125
  height: '5px',
92
126
  borderRadius: '50%',
93
- background: colors.terracotta,
127
+ background: 'var(--terracotta)',
94
128
  },
95
129
  }),
96
130
  inlineMarkdown(item),
@@ -128,8 +162,8 @@ export function renderMarkdown(text) {
128
162
  width: '20px',
129
163
  height: '20px',
130
164
  borderRadius: '50%',
131
- background: colors.terracotta,
132
- color: colors.cream,
165
+ background: 'var(--terracotta)',
166
+ color: 'var(--cream)',
133
167
  display: 'flex',
134
168
  alignItems: 'center',
135
169
  justifyContent: 'center',
@@ -158,7 +192,7 @@ export function renderMarkdown(text) {
158
192
  elements.push(h('pre', {
159
193
  key: elements.length,
160
194
  style: {
161
- background: colors.terminalBg,
195
+ background: 'var(--terminal-bg)',
162
196
  color: 'rgba(255, 255, 255, 0.85)',
163
197
  fontFamily: fonts.mono,
164
198
  fontSize: '13px',
@@ -190,12 +224,15 @@ export function renderMarkdown(text) {
190
224
  if (paraLines.length > 0) {
191
225
  elements.push(h('p', {
192
226
  key: elements.length,
193
- style: { margin: '0.5rem 0', fontSize: '15px', lineHeight: 1.6, color: colors.dark },
227
+ style: { margin: '0.5rem 0', fontSize: '15px', lineHeight: 1.6, color: 'var(--dark)' },
194
228
  }, inlineMarkdown(paraLines.join(' '))));
229
+ } else {
230
+ // Safety: nothing consumed this line. Skip it to avoid an infinite loop.
231
+ i++;
195
232
  }
196
233
  }
197
234
 
198
- return h('div', null, ...elements);
235
+ return h('div', { style: { color: 'var(--dark)' } }, ...elements);
199
236
  }
200
237
 
201
238
  // Inline markdown: **bold**, *italic*, `code`, [link](url)
@@ -210,11 +247,11 @@ function inlineMarkdown(text) {
210
247
  const patterns = [
211
248
  { re: /`([^`]+)`/, render: (m) => h('code', {
212
249
  key: key++,
213
- style: { fontFamily: fonts.mono, fontSize: '0.88em', background: 'rgba(59, 45, 33, 0.06)', padding: '2px 5px', borderRadius: '3px' },
250
+ className: 'eg-code',
214
251
  }, m[1]) },
215
252
  { re: /\*\*(.+?)\*\*/, render: (m) => h('strong', { key: key++, style: { fontWeight: 600 } }, m[1]) },
216
253
  { re: /\[([^\]]+)\]\(([^)]+)\)/, render: (m) => h('a', {
217
- key: key++, href: m[2], style: { color: colors.terracotta, textDecoration: 'underline' },
254
+ key: key++, href: m[2], style: { color: 'var(--terracotta)', textDecoration: 'underline' },
218
255
  }, m[1]) },
219
256
  ];
220
257
 
@@ -285,11 +322,11 @@ function renderTable(lines, key) {
285
322
  style: {
286
323
  textAlign: 'left',
287
324
  padding: '8px 12px',
288
- borderBottom: `2px solid ${colors.border}`,
325
+ borderBottom: '2px solid var(--border)',
289
326
  fontFamily: fonts.mono,
290
327
  fontSize: '12px',
291
328
  fontWeight: 500,
292
- color: colors.muted,
329
+ color: 'var(--muted)',
293
330
  textTransform: 'uppercase',
294
331
  letterSpacing: '0.04em',
295
332
  whiteSpace: 'nowrap',
@@ -306,8 +343,8 @@ function renderTable(lines, key) {
306
343
  key: j,
307
344
  style: {
308
345
  padding: '6px 12px',
309
- borderBottom: `1px solid rgba(224, 216, 204, 0.5)`,
310
- color: colors.dark,
346
+ borderBottom: '1px solid var(--border)',
347
+ color: 'var(--dark)',
311
348
  },
312
349
  }, cell.trim())
313
350
  ),
@@ -347,7 +384,7 @@ export function renderMarkdownLite(text) {
347
384
 
348
385
  // H2/H3 heading
349
386
  if (line.startsWith('## ')) {
350
- elements.push(h('h3', { key: elements.length, style: { fontFamily: fonts.serif, fontSize: '18px', fontWeight: 600, margin: '1.5rem 0 0.5rem', color: colors.black } }, line.slice(3)));
387
+ elements.push(h('h3', { key: elements.length, style: { fontFamily: fonts.serif, fontSize: '18px', fontWeight: 600, margin: '1.5rem 0 0.5rem', color: 'var(--black)' } }, line.slice(3)));
351
388
  i++;
352
389
  continue;
353
390
  }
@@ -369,7 +406,7 @@ export function renderMarkdownLite(text) {
369
406
  if (line.trim() === '') { i++; continue; }
370
407
 
371
408
  // Paragraph
372
- elements.push(h('p', { key: elements.length, style: { margin: '0.5rem 0', fontSize: '14px', lineHeight: 1.6, color: colors.dark } }, line));
409
+ elements.push(h('p', { key: elements.length, style: { margin: '0.5rem 0', fontSize: '14px', lineHeight: 1.6, color: 'var(--dark)' } }, line));
373
410
  i++;
374
411
  }
375
412
 
@@ -0,0 +1,109 @@
1
+ // Parse board data (JSON from memory/board/board.json) into structured data
2
+ import fs from 'node:fs';
3
+ import { execSync } from 'node:child_process';
4
+
5
+ export function parseBoard(input) {
6
+ let data;
7
+
8
+ if (typeof input === 'string' && fs.existsSync(input)) {
9
+ data = JSON.parse(fs.readFileSync(input, 'utf-8'));
10
+ } else if (typeof input === 'string' && input.startsWith('{')) {
11
+ data = JSON.parse(input);
12
+ } else if (typeof input === 'object') {
13
+ data = input;
14
+ } else {
15
+ // Try to find board.json relative to git root
16
+ try {
17
+ const root = execSync('git rev-parse --show-toplevel 2>/dev/null', { encoding: 'utf-8' }).trim();
18
+ const boardPath = `${root}/memory/board/board.json`;
19
+ if (fs.existsSync(boardPath)) {
20
+ data = JSON.parse(fs.readFileSync(boardPath, 'utf-8'));
21
+ }
22
+ } catch {}
23
+ if (!data) throw new Error('No board data found. Expected memory/board/board.json');
24
+ }
25
+
26
+ const today = new Date().toISOString().split('T')[0];
27
+
28
+ // Flatten all cards from the hierarchy for person and timeline views
29
+ const allCards = [];
30
+ for (const activity of data.activities || []) {
31
+ if (activity.subactivities) {
32
+ for (const sub of activity.subactivities) {
33
+ for (const card of sub.cards || []) {
34
+ allCards.push({ ...card, activity: activity.label, subactivity: sub.label, activityId: activity.id, subactivityId: sub.id });
35
+ }
36
+ }
37
+ }
38
+ for (const card of activity.cards || []) {
39
+ allCards.push({ ...card, activity: activity.label, subactivity: null, activityId: activity.id, subactivityId: null });
40
+ }
41
+ }
42
+
43
+ // Group by person
44
+ const personMap = {};
45
+ for (const card of allCards) {
46
+ for (const owner of card.owners || []) {
47
+ if (!personMap[owner]) personMap[owner] = [];
48
+ personMap[owner].push(card);
49
+ }
50
+ }
51
+ const people = Object.entries(personMap)
52
+ .map(([name, cards]) => ({
53
+ name,
54
+ cards: cards.sort((a, b) => a.priority - b.priority),
55
+ stats: {
56
+ total: cards.length,
57
+ p0: cards.filter(c => c.priority === 0).length,
58
+ p1: cards.filter(c => c.priority === 1).length,
59
+ p2: cards.filter(c => c.priority === 2).length,
60
+ p3: cards.filter(c => c.priority === 3).length,
61
+ inProgress: cards.filter(c => c.status === 'in-progress').length,
62
+ todo: cards.filter(c => c.status === 'todo').length,
63
+ review: cards.filter(c => c.status === 'review').length,
64
+ done: cards.filter(c => c.status === 'done').length,
65
+ },
66
+ }))
67
+ .sort((a, b) => b.stats.p0 - a.stats.p0 || b.stats.total - a.stats.total);
68
+
69
+ // Timeline: cards with dates, sorted by startDate
70
+ const timeline = allCards
71
+ .filter(c => c.startDate || c.dueDate)
72
+ .sort((a, b) => (a.startDate || a.dueDate || '').localeCompare(b.startDate || b.dueDate || ''));
73
+
74
+ // Summary stats
75
+ const summary = {
76
+ totalCards: allCards.length,
77
+ byPriority: { P0: 0, P1: 0, P2: 0, P3: 0 },
78
+ byStatus: { todo: 0, 'in-progress': 0, review: 0, done: 0 },
79
+ };
80
+ for (const card of allCards) {
81
+ const pKey = `P${card.priority}`;
82
+ if (summary.byPriority[pKey] !== undefined) summary.byPriority[pKey]++;
83
+ if (summary.byStatus[card.status] !== undefined) summary.byStatus[card.status]++;
84
+ }
85
+
86
+ // Read org name
87
+ let org = 'Egregore';
88
+ try {
89
+ const root = execSync('git rev-parse --show-toplevel 2>/dev/null', { encoding: 'utf-8' }).trim();
90
+ const configPath = `${root}/egregore.json`;
91
+ if (fs.existsSync(configPath)) {
92
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
93
+ org = config.org_name || org;
94
+ }
95
+ } catch {}
96
+
97
+ return {
98
+ title: `Project Board — ${org}`,
99
+ date: today,
100
+ org,
101
+ updatedBy: data.updatedBy,
102
+ updatedAt: data.updated,
103
+ activities: data.activities,
104
+ allCards,
105
+ people,
106
+ timeline,
107
+ summary,
108
+ };
109
+ }
@@ -0,0 +1,73 @@
1
+ // Parse network/people data (JSON from memory/network/people.json)
2
+ import fs from 'node:fs';
3
+ import { execSync } from 'node:child_process';
4
+
5
+ export function parseNetwork(input) {
6
+ let data;
7
+
8
+ if (typeof input === 'string' && fs.existsSync(input)) {
9
+ data = JSON.parse(fs.readFileSync(input, 'utf-8'));
10
+ } else if (typeof input === 'string' && input.startsWith('{')) {
11
+ data = JSON.parse(input);
12
+ } else if (typeof input === 'object') {
13
+ data = input;
14
+ } else {
15
+ try {
16
+ const root = execSync('git rev-parse --show-toplevel 2>/dev/null', { encoding: 'utf-8' }).trim();
17
+ const peoplePath = `${root}/memory/network/people.json`;
18
+ if (fs.existsSync(peoplePath)) {
19
+ data = JSON.parse(fs.readFileSync(peoplePath, 'utf-8'));
20
+ }
21
+ } catch {}
22
+ if (!data) throw new Error('No network data found. Expected memory/network/people.json');
23
+ }
24
+
25
+ const people = data.people || [];
26
+
27
+ // Collect all unique roles
28
+ const allRoles = [...new Set(people.flatMap(p => p.roles || []))].sort();
29
+
30
+ // Group by role
31
+ const byRole = {};
32
+ for (const role of allRoles) {
33
+ byRole[role] = people.filter(p => (p.roles || []).includes(role));
34
+ }
35
+
36
+ // Group by organization
37
+ const byOrg = {};
38
+ for (const person of people) {
39
+ const org = person.organization || 'Independent';
40
+ if (!byOrg[org]) byOrg[org] = [];
41
+ byOrg[org].push(person);
42
+ }
43
+
44
+ // Summary
45
+ const summary = {
46
+ totalPeople: people.length,
47
+ totalOrgs: Object.keys(byOrg).length,
48
+ byRole: Object.fromEntries(allRoles.map(r => [r, byRole[r].length])),
49
+ };
50
+
51
+ // Read org name
52
+ let org = 'Egregore';
53
+ try {
54
+ const root = execSync('git rev-parse --show-toplevel 2>/dev/null', { encoding: 'utf-8' }).trim();
55
+ const configPath = `${root}/egregore.json`;
56
+ if (fs.existsSync(configPath)) {
57
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
58
+ org = config.org_name || org;
59
+ }
60
+ } catch {}
61
+
62
+ return {
63
+ title: `Network — ${org}`,
64
+ date: new Date().toISOString().split('T')[0],
65
+ org,
66
+ updatedBy: data.updatedBy,
67
+ people,
68
+ allRoles,
69
+ byRole,
70
+ byOrg,
71
+ summary,
72
+ };
73
+ }
package/lib/registry.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import React from 'react';
3
3
  import { defineRegistry } from '@json-render/react';
4
4
  import { catalog } from './catalog.js';
5
- import { colors, fonts } from './tokens.js';
5
+ import { fonts } from './tokens.js';
6
6
  import { renderMarkdown } from './markdown.js';
7
7
 
8
8
  const h = React.createElement;
@@ -70,12 +70,12 @@ export const { registry, handlers, executeAction } = defineRegistry(catalog, {
70
70
  h('div', {
71
71
  key: i,
72
72
  style: {
73
- background: 'white', border: `1px solid ${colors.border}`,
73
+ background: 'var(--surface)', border: '1px solid var(--border)',
74
74
  borderRadius: '12px', padding: '1.25rem', textAlign: 'center',
75
75
  },
76
76
  },
77
- h('div', { style: { fontSize: '28px', fontFamily: fonts.serif, fontWeight: 400, color: colors.black, lineHeight: 1.2 } }, m.value),
78
- h('div', { style: { fontSize: '12px', fontFamily: fonts.mono, color: colors.muted, marginTop: '4px', textTransform: 'uppercase', letterSpacing: '0.04em' } }, m.label),
77
+ h('div', { style: { fontSize: '28px', fontFamily: fonts.serif, fontWeight: 400, color: 'var(--black)', lineHeight: 1.2 } }, m.value),
78
+ h('div', { style: { fontSize: '12px', fontFamily: fonts.mono, color: 'var(--muted)', marginTop: '4px', textTransform: 'uppercase', letterSpacing: '0.04em' } }, m.label),
79
79
  )
80
80
  ),
81
81
  ),
@@ -97,7 +97,7 @@ export const { registry, handlers, executeAction } = defineRegistry(catalog, {
97
97
  h('span', { className: 'eg-artifact-date' }, a.date),
98
98
  h('span', { className: 'eg-artifact-type' }, a.type),
99
99
  h('span', { className: 'eg-artifact-title' }, a.title),
100
- a.author && h('span', { style: { color: colors.muted, fontSize: '13px', fontFamily: fonts.mono } }, `(${a.author})`),
100
+ a.author && h('span', { style: { color: 'var(--muted)', fontSize: '13px', fontFamily: fonts.mono } }, `(${a.author})`),
101
101
  )
102
102
  ),
103
103
  ),
@@ -108,7 +108,7 @@ export const { registry, handlers, executeAction } = defineRegistry(catalog, {
108
108
  h('div', { key: i, className: 'eg-contributor' },
109
109
  h('span', { className: 'eg-contributor-avatar' }, (p.name || '?')[0].toUpperCase()),
110
110
  h('span', null, p.name),
111
- p.role && h('span', { style: { color: colors.muted, fontSize: '12px' } }, p.role),
111
+ p.role && h('span', { style: { color: 'var(--muted)', fontSize: '12px' } }, p.role),
112
112
  )
113
113
  ),
114
114
  ),
@@ -121,8 +121,8 @@ export const { registry, handlers, executeAction } = defineRegistry(catalog, {
121
121
  NumberedSteps: ({ props }) =>
122
122
  h('ol', { style: { listStyle: 'none', padding: 0 } },
123
123
  ...props.steps.map((step, i) =>
124
- h('li', { key: i, style: { display: 'flex', gap: '12px', padding: '8px 0', borderBottom: i < props.steps.length - 1 ? '1px solid rgba(224, 216, 204, 0.5)' : 'none', fontSize: '15px', lineHeight: 1.5 } },
125
- h('span', { style: { flexShrink: 0, width: '24px', height: '24px', borderRadius: '50%', background: colors.terracotta, color: colors.cream, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '12px', fontFamily: fonts.mono, fontWeight: 600 } }, String(i + 1)),
124
+ h('li', { key: i, style: { display: 'flex', gap: '12px', padding: '8px 0', borderBottom: i < props.steps.length - 1 ? '1px solid var(--hairline)' : 'none', fontSize: '15px', lineHeight: 1.5 } },
125
+ h('span', { style: { flexShrink: 0, width: '24px', height: '24px', borderRadius: '50%', background: 'var(--terracotta)', color: 'var(--cream)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '12px', fontFamily: fonts.mono, fontWeight: 600 } }, String(i + 1)),
126
126
  h('span', null, step),
127
127
  )
128
128
  ),
@@ -131,11 +131,11 @@ export const { registry, handlers, executeAction } = defineRegistry(catalog, {
131
131
  SessionList: ({ props }) =>
132
132
  h('div', null,
133
133
  ...props.sessions.map((s, i) =>
134
- h('div', { key: i, style: { display: 'flex', alignItems: 'baseline', gap: '12px', padding: '6px 0', borderBottom: '1px solid rgba(224, 216, 204, 0.5)', fontSize: '14px' } },
135
- h('span', { style: { fontFamily: fonts.mono, fontSize: '12px', color: colors.muted, minWidth: '72px', flexShrink: 0 } }, s.date),
136
- s.by && h('span', { style: { fontFamily: fonts.mono, fontSize: '11px', color: colors.blueMuted, background: 'rgba(123, 157, 183, 0.1)', padding: '1px 6px', borderRadius: '3px', flexShrink: 0 } }, s.by),
137
- h('span', { style: { color: colors.black, flex: 1 } }, s.topic),
138
- s.handedTo && h('span', { style: { fontFamily: fonts.mono, fontSize: '11px', color: colors.terracotta } }, `→ ${s.handedTo}`),
134
+ h('div', { key: i, style: { display: 'flex', alignItems: 'baseline', gap: '12px', padding: '6px 0', borderBottom: '1px solid var(--hairline)', fontSize: '14px' } },
135
+ h('span', { style: { fontFamily: fonts.mono, fontSize: '12px', color: 'var(--muted)', minWidth: '72px', flexShrink: 0 } }, s.date),
136
+ s.by && h('span', { style: { fontFamily: fonts.mono, fontSize: '11px', color: 'var(--blue-muted)', background: 'var(--blue-chip)', padding: '1px 6px', borderRadius: '3px', flexShrink: 0 } }, s.by),
137
+ h('span', { style: { color: 'var(--black)', flex: 1 } }, s.topic),
138
+ s.handedTo && h('span', { style: { fontFamily: fonts.mono, fontSize: '11px', color: 'var(--terracotta)' } }, `→ ${s.handedTo}`),
139
139
  )
140
140
  ),
141
141
  ),
@@ -146,9 +146,9 @@ export const { registry, handlers, executeAction } = defineRegistry(catalog, {
146
146
  Callout: ({ props }) =>
147
147
  h('div', {
148
148
  style: {
149
- background: 'rgba(212, 135, 90, 0.06)', border: '1px solid rgba(212, 135, 90, 0.15)',
149
+ background: 'var(--terracotta-soft)', border: '1px solid var(--terracotta-hair)',
150
150
  borderRadius: '12px', padding: '1.25rem 1.5rem', marginBottom: '1.5rem',
151
- fontSize: '15px', lineHeight: 1.6, color: colors.dark,
151
+ fontSize: '15px', lineHeight: 1.6, color: 'var(--dark)',
152
152
  },
153
153
  }, renderMarkdown(props.text)),
154
154