amalgm 0.1.98 → 0.1.99
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/package.json
CHANGED
|
@@ -7,9 +7,37 @@
|
|
|
7
7
|
* even without blank-line separators.
|
|
8
8
|
*
|
|
9
9
|
* Output is inline-styled HTML suitable for email clients that strip <style>
|
|
10
|
-
* tags.
|
|
10
|
+
* tags. All values come from EMAIL_TOKENS below, which mirrors the shared
|
|
11
|
+
* rendering ruleset in amalgm-ui/lib/rendering/tokens.ts — keep both in sync:
|
|
12
|
+
* body 14px, h1 caps at 18px (the amalgm section-header size), Notion-style
|
|
13
|
+
* table grid, black chat-style code blocks, blue hyperlinks/citations.
|
|
11
14
|
*/
|
|
12
15
|
|
|
16
|
+
const EMAIL_TOKENS = {
|
|
17
|
+
font: {
|
|
18
|
+
text: "'SF Pro Text',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',sans-serif",
|
|
19
|
+
mono: "'JetBrains Mono','SF Mono',SFMono-Regular,Menlo,Consolas,monospace",
|
|
20
|
+
},
|
|
21
|
+
type: {
|
|
22
|
+
body: 14,
|
|
23
|
+
// h1 caps at the section-header size; h4-h6 share the body size.
|
|
24
|
+
headings: { 1: 18, 2: 16, 3: 15, 4: 14, 5: 14, 6: 14 },
|
|
25
|
+
code: 12,
|
|
26
|
+
table: 14,
|
|
27
|
+
},
|
|
28
|
+
color: {
|
|
29
|
+
text: '#ffffff',
|
|
30
|
+
textSecondary: '#d6d3d1',
|
|
31
|
+
border: '#1c1917', // stone-900 — container borders
|
|
32
|
+
tableBorder: '#292524', // stone-800 — table grid (Notion editor standard)
|
|
33
|
+
tableHeaderBg: '#141210', // stone-925 — Notion editor header row
|
|
34
|
+
bg: '#000000',
|
|
35
|
+
bgInset: '#0a0a0a', // inline-code fill
|
|
36
|
+
link: '#60a5fa', // blue-400 — hyperlinks + citations everywhere
|
|
37
|
+
},
|
|
38
|
+
radius: { inline: 6, block: 12 },
|
|
39
|
+
};
|
|
40
|
+
|
|
13
41
|
function escapeHtmlAttr(value) {
|
|
14
42
|
return String(value)
|
|
15
43
|
.replace(/&/g, '&')
|
|
@@ -25,27 +53,18 @@ function escapeHtmlText(value) {
|
|
|
25
53
|
.replace(/>/g, '>');
|
|
26
54
|
}
|
|
27
55
|
|
|
28
|
-
// Inline citation
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
folder: '\u{1F4C1}', // 📁
|
|
34
|
-
skill: '\u{2728}', // ✨
|
|
35
|
-
agent: '@',
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
function renderAmalgmCitation(label, kindAndPath, description) {
|
|
39
|
-
const slashIdx = kindAndPath.indexOf('/');
|
|
40
|
-
const kind = slashIdx === -1 ? kindAndPath : kindAndPath.slice(0, slashIdx);
|
|
41
|
-
const icon = AMALGM_KIND_ICONS[kind] || '\u{1F4CE}'; // 📎 fallback
|
|
56
|
+
// Inline citation for amalgm:// references (files, folders, skills, agents).
|
|
57
|
+
// Codex-style: plain blue text at the surrounding font size — no chip.
|
|
58
|
+
// Non-clickable in email (local resources can't open from a mail client);
|
|
59
|
+
// the optional description survives as a title tooltip.
|
|
60
|
+
function renderAmalgmCitation(label, _kindAndPath, description) {
|
|
42
61
|
const titleAttr = description ? ` title="${escapeHtmlAttr(description)}"` : '';
|
|
43
62
|
const safeLabel = escapeHtmlText(label);
|
|
44
|
-
return `<span style="
|
|
63
|
+
return `<span style="color:${EMAIL_TOKENS.color.link};white-space:nowrap"${titleAttr}>${safeLabel}</span>`;
|
|
45
64
|
}
|
|
46
65
|
|
|
47
66
|
/**
|
|
48
|
-
* Inline format: bold, italic, code, links, images, amalgm://
|
|
67
|
+
* Inline format: bold, italic, code, links, images, amalgm:// citations.
|
|
49
68
|
*/
|
|
50
69
|
function inlineFormat(text) {
|
|
51
70
|
return (
|
|
@@ -53,7 +72,7 @@ function inlineFormat(text) {
|
|
|
53
72
|
// Images: 
|
|
54
73
|
.replace(
|
|
55
74
|
/!\[([^\]]*)\]\(([^)]+)\)/g,
|
|
56
|
-
'<img src="$2" alt="$1" style="max-width:100%;height:auto;border-radius:
|
|
75
|
+
'<img src="$2" alt="$1" style="max-width:100%;height:auto;border-radius:16px;margin:8px 0">',
|
|
57
76
|
)
|
|
58
77
|
// amalgm:// citations (run before generic link regex so the optional
|
|
59
78
|
// title attribute doesn't get swallowed into the href). Format:
|
|
@@ -64,10 +83,10 @@ function inlineFormat(text) {
|
|
|
64
83
|
(_, label, kindAndPath, description) =>
|
|
65
84
|
renderAmalgmCitation(label, kindAndPath, description),
|
|
66
85
|
)
|
|
67
|
-
// Links: [text](url)
|
|
86
|
+
// Links: [text](url) — blue hyperlink per the rendering ruleset.
|
|
68
87
|
.replace(
|
|
69
88
|
/\[([^\]]+)\]\(([^)]+)\)/g,
|
|
70
|
-
|
|
89
|
+
`<a href="$2" style="color:${EMAIL_TOKENS.color.link};text-decoration:none" target="_blank">$1</a>`,
|
|
71
90
|
)
|
|
72
91
|
// Bold + italic: ***text***
|
|
73
92
|
.replace(
|
|
@@ -82,7 +101,7 @@ function inlineFormat(text) {
|
|
|
82
101
|
// Inline code: `text`
|
|
83
102
|
.replace(
|
|
84
103
|
/`([^`]+)`/g,
|
|
85
|
-
|
|
104
|
+
`<code style="background:${EMAIL_TOKENS.color.bgInset};border:1px solid ${EMAIL_TOKENS.color.border};padding:2px 6px;border-radius:${EMAIL_TOKENS.radius.inline}px;font-size:${EMAIL_TOKENS.type.code}px;font-family:${EMAIL_TOKENS.font.mono};color:#f5f5f4">$1</code>`,
|
|
86
105
|
)
|
|
87
106
|
);
|
|
88
107
|
}
|
|
@@ -98,7 +117,7 @@ function markdownToEmailHtml(md) {
|
|
|
98
117
|
const escaped = code.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
99
118
|
const placeholder = `%%CODEBLOCK_${codeBlocks.length}%%`;
|
|
100
119
|
codeBlocks.push(
|
|
101
|
-
`<pre style="background
|
|
120
|
+
`<pre style="background:${EMAIL_TOKENS.color.bg};border:1px solid ${EMAIL_TOKENS.color.border};border-radius:${EMAIL_TOKENS.radius.block}px;padding:16px;font-size:${EMAIL_TOKENS.type.code}px;font-family:${EMAIL_TOKENS.font.mono};color:#f5f5f4;overflow-x:auto;margin:16px 0;line-height:1.6"><code>${escaped.trimEnd()}</code></pre>`,
|
|
102
121
|
);
|
|
103
122
|
return placeholder;
|
|
104
123
|
});
|
|
@@ -136,29 +155,37 @@ function markdownToEmailHtml(md) {
|
|
|
136
155
|
tableRows = [];
|
|
137
156
|
return;
|
|
138
157
|
}
|
|
158
|
+
// Notion-style grid per the rendering ruleset: stone-800 borders,
|
|
159
|
+
// stone-925 header row, px-3 py-1.5 cells, 14px text, wrapped in the
|
|
160
|
+
// same rounded-xl (12px) chrome as code blocks. The wrapper draws the
|
|
161
|
+
// outer border, so cells only draw their inner right/bottom edges.
|
|
162
|
+
const cellBase = `padding:6px 12px;text-align:left;vertical-align:top;font-size:${EMAIL_TOKENS.type.table}px`;
|
|
163
|
+
const edge = `1px solid ${EMAIL_TOKENS.color.tableBorder}`;
|
|
164
|
+
const cellBorders = (isLastCol, isLastRow) =>
|
|
165
|
+
`${isLastCol ? '' : `border-right:${edge};`}${isLastRow ? '' : `border-bottom:${edge};`}`;
|
|
139
166
|
const headerCells = tableRows[0].cells;
|
|
140
167
|
const bodyRows = tableRows.slice(1);
|
|
141
168
|
const hdr = headerCells
|
|
142
169
|
.map(
|
|
143
|
-
(c) =>
|
|
144
|
-
`<th style="
|
|
170
|
+
(c, i) =>
|
|
171
|
+
`<th style="${cellBase};${cellBorders(i === headerCells.length - 1, false)}background:${EMAIL_TOKENS.color.tableHeaderBg};font-weight:500;color:#ffffff">${inlineFormat(c)}</th>`,
|
|
145
172
|
)
|
|
146
173
|
.join('');
|
|
147
174
|
const body = bodyRows
|
|
148
175
|
.map(
|
|
149
|
-
(r) =>
|
|
176
|
+
(r, rowIdx) =>
|
|
150
177
|
'<tr>' +
|
|
151
178
|
r.cells
|
|
152
179
|
.map(
|
|
153
|
-
(c) =>
|
|
154
|
-
`<td style="
|
|
180
|
+
(c, i) =>
|
|
181
|
+
`<td style="${cellBase};${cellBorders(i === r.cells.length - 1, rowIdx === bodyRows.length - 1)}color:#ffffff">${inlineFormat(c)}</td>`,
|
|
155
182
|
)
|
|
156
183
|
.join('') +
|
|
157
184
|
'</tr>',
|
|
158
185
|
)
|
|
159
186
|
.join('');
|
|
160
187
|
out.push(
|
|
161
|
-
`<
|
|
188
|
+
`<div style="border:${edge};border-radius:12px;overflow:hidden;margin:16px 0"><table style="width:100%;border-collapse:collapse"><thead><tr>${hdr}</tr></thead><tbody>${body}</tbody></table></div>`,
|
|
162
189
|
);
|
|
163
190
|
tableRows = [];
|
|
164
191
|
}
|
|
@@ -166,7 +193,7 @@ function markdownToEmailHtml(md) {
|
|
|
166
193
|
if (!quoteLines.length) return;
|
|
167
194
|
const content = inlineFormat(quoteLines.join('\n')).replace(/\n/g, '<br>');
|
|
168
195
|
out.push(
|
|
169
|
-
`<blockquote style="margin:16px 0;padding:8px 16px;border-left:3px solid
|
|
196
|
+
`<blockquote style="margin:16px 0;padding:8px 16px;border-left:3px solid ${EMAIL_TOKENS.color.tableBorder};color:${EMAIL_TOKENS.color.textSecondary};font-style:italic">${content}</blockquote>`,
|
|
170
197
|
);
|
|
171
198
|
quoteLines = [];
|
|
172
199
|
}
|
|
@@ -200,16 +227,16 @@ function markdownToEmailHtml(md) {
|
|
|
200
227
|
}
|
|
201
228
|
if (/^(-{3,}|_{3,}|\*{3,})$/.test(trimmed)) {
|
|
202
229
|
flushAll();
|
|
203
|
-
out.push(
|
|
230
|
+
out.push(`<hr style="border:none;border-top:1px solid ${EMAIL_TOKENS.color.border};margin:24px 0">`);
|
|
204
231
|
continue;
|
|
205
232
|
}
|
|
206
233
|
const headingMatch = trimmed.match(/^(#{1,6})\s+(.+)$/);
|
|
207
234
|
if (headingMatch) {
|
|
208
235
|
flushAll();
|
|
209
236
|
const level = headingMatch[1].length;
|
|
210
|
-
const
|
|
237
|
+
const size = EMAIL_TOKENS.type.headings[level];
|
|
211
238
|
out.push(
|
|
212
|
-
`<h${level} style="margin:20px 0 8px;font-size:${
|
|
239
|
+
`<h${level} style="margin:20px 0 8px;font-size:${size}px;font-weight:650;color:#ffffff;line-height:1.35">${inlineFormat(headingMatch[2])}</h${level}>`,
|
|
213
240
|
);
|
|
214
241
|
continue;
|
|
215
242
|
}
|
|
@@ -270,14 +297,14 @@ function markdownToEmailHtml(md) {
|
|
|
270
297
|
function formatNotificationEmail(message, link) {
|
|
271
298
|
const htmlMessage = markdownToEmailHtml(message);
|
|
272
299
|
const linkHtml = link
|
|
273
|
-
? `<p style="margin:24px 0 0"><a href="${link}" style="color
|
|
300
|
+
? `<p style="margin:24px 0 0"><a href="${link}" style="color:${EMAIL_TOKENS.color.link};text-decoration:none;font-size:14px">View details →</a></p>`
|
|
274
301
|
: '';
|
|
275
302
|
return `<!DOCTYPE html>
|
|
276
303
|
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head>
|
|
277
|
-
<body style="margin:0;padding:0;background:#000000;font-family
|
|
304
|
+
<body style="margin:0;padding:0;background:#000000;font-family:${EMAIL_TOKENS.font.text};-webkit-font-smoothing:antialiased">
|
|
278
305
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#000000"><tr><td align="center" style="padding:0">
|
|
279
306
|
<table role="presentation" width="520" cellpadding="0" cellspacing="0" style="max-width:520px;width:100%"><tr><td style="padding:40px 28px 48px">
|
|
280
|
-
<div style="font-size
|
|
307
|
+
<div style="font-size:${EMAIL_TOKENS.type.body}px;color:#ffffff">${htmlMessage}</div>
|
|
281
308
|
${linkHtml}
|
|
282
309
|
<p style="margin:48px 0 0;font-size:12px;color:#ffffff">amalgm.ai</p>
|
|
283
310
|
</td></tr></table>
|