@sgummalla-works/sketchon 0.1.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/LICENSE +34 -0
- package/README.md +197 -0
- package/assets/emoji/1f4a1.svg +1 -0
- package/assets/emoji/1f4c8.svg +1 -0
- package/assets/emoji/1f4ca.svg +1 -0
- package/assets/emoji/1f4cb.svg +1 -0
- package/assets/emoji/1f4da.svg +1 -0
- package/assets/emoji/1f4dd.svg +1 -0
- package/assets/emoji/1f4e5.svg +1 -0
- package/assets/emoji/1f501.svg +1 -0
- package/assets/emoji/1f50d.svg +1 -0
- package/assets/emoji/1f517.svg +1 -0
- package/assets/emoji/1f525.svg +1 -0
- package/assets/emoji/1f5c4.svg +1 -0
- package/assets/emoji/1f9e0.svg +1 -0
- package/assets/emoji/1f9ee.svg +1 -0
- package/assets/emoji/23f8.svg +1 -0
- package/assets/emoji/2699.svg +1 -0
- package/assets/emoji/26a1.svg +1 -0
- package/assets/emoji/2705.svg +1 -0
- package/assets/emoji/2753.svg +1 -0
- package/assets/inter-400.ttf +0 -0
- package/assets/inter-600.ttf +0 -0
- package/assets/inter-700.ttf +0 -0
- package/dist/adapters/baseline.d.ts +3 -0
- package/dist/adapters/baseline.d.ts.map +1 -0
- package/dist/adapters/baseline.js +196 -0
- package/dist/adapters/baseline.js.map +1 -0
- package/dist/adapters/elk.d.ts +3 -0
- package/dist/adapters/elk.d.ts.map +1 -0
- package/dist/adapters/elk.js +230 -0
- package/dist/adapters/elk.js.map +1 -0
- package/dist/adapters/emoji.d.ts +10 -0
- package/dist/adapters/emoji.d.ts.map +1 -0
- package/dist/adapters/emoji.js +80 -0
- package/dist/adapters/emoji.js.map +1 -0
- package/dist/adapters/fonts.d.ts +13 -0
- package/dist/adapters/fonts.d.ts.map +1 -0
- package/dist/adapters/fonts.js +27 -0
- package/dist/adapters/fonts.js.map +1 -0
- package/dist/adapters/satori-helpers.d.ts +48 -0
- package/dist/adapters/satori-helpers.d.ts.map +1 -0
- package/dist/adapters/satori-helpers.js +43 -0
- package/dist/adapters/satori-helpers.js.map +1 -0
- package/dist/adapters/satori.d.ts +3 -0
- package/dist/adapters/satori.d.ts.map +1 -0
- package/dist/adapters/satori.js +269 -0
- package/dist/adapters/satori.js.map +1 -0
- package/dist/adapters/sequence.d.ts +3 -0
- package/dist/adapters/sequence.d.ts.map +1 -0
- package/dist/adapters/sequence.js +136 -0
- package/dist/adapters/sequence.js.map +1 -0
- package/dist/adapters/svg.d.ts +43 -0
- package/dist/adapters/svg.d.ts.map +1 -0
- package/dist/adapters/svg.js +92 -0
- package/dist/adapters/svg.js.map +1 -0
- package/dist/application/registry.d.ts +43 -0
- package/dist/application/registry.d.ts.map +1 -0
- package/dist/application/registry.js +45 -0
- package/dist/application/registry.js.map +1 -0
- package/dist/application/router.d.ts +56 -0
- package/dist/application/router.d.ts.map +1 -0
- package/dist/application/router.js +59 -0
- package/dist/application/router.js.map +1 -0
- package/dist/constants.d.ts +25 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +35 -0
- package/dist/constants.js.map +1 -0
- package/dist/domain/spec.d.ts +163 -0
- package/dist/domain/spec.d.ts.map +1 -0
- package/dist/domain/spec.js +17 -0
- package/dist/domain/spec.js.map +1 -0
- package/dist/domain/theme.d.ts +68 -0
- package/dist/domain/theme.d.ts.map +1 -0
- package/dist/domain/theme.js +60 -0
- package/dist/domain/theme.js.map +1 -0
- package/dist/domain/validate.d.ts +19 -0
- package/dist/domain/validate.d.ts.map +1 -0
- package/dist/domain/validate.js +69 -0
- package/dist/domain/validate.js.map +1 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +83 -0
- package/dist/index.js.map +1 -0
- package/dist/ports/Technique.d.ts +69 -0
- package/dist/ports/Technique.d.ts.map +1 -0
- package/dist/ports/Technique.js +2 -0
- package/dist/ports/Technique.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* elkjs technique — the engine for GRAPH-shaped diagrams (flow, architecture,
|
|
3
|
+
* state, ER): cases where edges must be routed between freely-placed nodes, not
|
|
4
|
+
* stacked in a flexbox column. Satori owns the panel/concept family; ELK owns
|
|
5
|
+
* this one.
|
|
6
|
+
*
|
|
7
|
+
* Same thesis: the model declares a coordinate-free spec; ELK's layered layout
|
|
8
|
+
* computes every node coordinate AND routes every edge (with bend points). We
|
|
9
|
+
* never hand-place anything. Because ELK returns real coordinates, this
|
|
10
|
+
* technique CAN report boxes + edge segments back to the geometry eval — so its
|
|
11
|
+
* overlap/off-canvas/crossing scores are genuinely measured, unlike Satori's.
|
|
12
|
+
*
|
|
13
|
+
* Groups are rendered as ELK hierarchy: a grouped node becomes a child of a
|
|
14
|
+
* container node, so ELK keeps cluster members together and we draw the group
|
|
15
|
+
* box behind them.
|
|
16
|
+
*/
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
import ELKConstructor from 'elkjs/lib/elk.bundled.js';
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
20
|
+
const ELK = ELKConstructor;
|
|
21
|
+
import { CANVAS_PADDING, EDGE_LABEL_FONT_SIZE, NODE_LABEL_FONT_SIZE, NODE_MAX_WIDTH, NODE_MIN_HEIGHT, NODE_MIN_WIDTH, NODE_PADDING_X, NODE_PADDING_Y, NODE_SUBLABEL_FONT_SIZE, } from '../constants.js';
|
|
22
|
+
import { esc, estimateTextWidth, palette, svgDocument, wrapText } from './svg.js';
|
|
23
|
+
const LABEL_LINE_HEIGHT = 18; // px per wrapped label line
|
|
24
|
+
const elk = new ELK();
|
|
25
|
+
/** Map our Direction to ELK's. */
|
|
26
|
+
function elkDirection(dir, kind) {
|
|
27
|
+
const d = dir ?? (kind === 'sequence' || kind === 'er' || kind === 'state' ? 'LR' : 'TB');
|
|
28
|
+
switch (d) {
|
|
29
|
+
case 'LR':
|
|
30
|
+
return 'RIGHT';
|
|
31
|
+
case 'RL':
|
|
32
|
+
return 'LEFT';
|
|
33
|
+
case 'BT':
|
|
34
|
+
return 'UP';
|
|
35
|
+
default:
|
|
36
|
+
return 'DOWN';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function clamp(v, lo, hi) {
|
|
40
|
+
return Math.max(lo, Math.min(hi, v));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Size a node box from its (wrapped) label + sublabel. Long labels wrap to
|
|
44
|
+
* NODE_MAX_WIDTH and the box grows TALLER to fit the lines, instead of letting
|
|
45
|
+
* a single line overflow. Diamonds get extra slack since their usable interior
|
|
46
|
+
* is smaller than their bounding box.
|
|
47
|
+
*
|
|
48
|
+
* @param node the node to size
|
|
49
|
+
* @returns box width/height, the wrapped lines, and the widest line's px width
|
|
50
|
+
*/
|
|
51
|
+
function sizeNode(node) {
|
|
52
|
+
const innerMax = NODE_MAX_WIDTH - NODE_PADDING_X * 2;
|
|
53
|
+
const lines = wrapText(node.label, innerMax, NODE_LABEL_FONT_SIZE);
|
|
54
|
+
const contentWidth = Math.max(...lines.map((l) => estimateTextWidth(l, NODE_LABEL_FONT_SIZE)));
|
|
55
|
+
const subW = node.sublabel ? estimateTextWidth(node.sublabel, NODE_SUBLABEL_FONT_SIZE) : 0;
|
|
56
|
+
const width = clamp(Math.max(contentWidth, subW) + NODE_PADDING_X * 2, NODE_MIN_WIDTH, NODE_MAX_WIDTH);
|
|
57
|
+
const labelBlock = lines.length * LABEL_LINE_HEIGHT;
|
|
58
|
+
const subBlock = node.sublabel ? NODE_SUBLABEL_FONT_SIZE + 4 : 0;
|
|
59
|
+
const height = Math.max(NODE_MIN_HEIGHT, labelBlock + subBlock + NODE_PADDING_Y * 2);
|
|
60
|
+
return { width, height, lines, contentWidth };
|
|
61
|
+
}
|
|
62
|
+
/** Build the ELK graph (with group hierarchy) from the spec. */
|
|
63
|
+
function buildElkGraph(spec) {
|
|
64
|
+
const nodeById = new Map(spec.nodes.map((n) => [n.id, n]));
|
|
65
|
+
const groupOf = new Map();
|
|
66
|
+
for (const n of spec.nodes)
|
|
67
|
+
if (n.group)
|
|
68
|
+
groupOf.set(n.id, n.group);
|
|
69
|
+
const makeNode = (n) => {
|
|
70
|
+
const { width, height } = sizeNode(n);
|
|
71
|
+
return { id: n.id, width, height };
|
|
72
|
+
};
|
|
73
|
+
const children = [];
|
|
74
|
+
if (spec.groups?.length) {
|
|
75
|
+
const grouped = new Set();
|
|
76
|
+
for (const g of spec.groups) {
|
|
77
|
+
const members = g.members.filter((m) => nodeById.has(m));
|
|
78
|
+
members.forEach((m) => grouped.add(m));
|
|
79
|
+
children.push({
|
|
80
|
+
id: `group:${g.id}`,
|
|
81
|
+
layoutOptions: { 'elk.padding': '[top=28,left=16,bottom=16,right=16]' },
|
|
82
|
+
labels: g.label ? [{ text: g.label, width: estimateTextWidth(g.label, 12), height: 16 }] : [],
|
|
83
|
+
children: members.map((m) => makeNode(nodeById.get(m))),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// Ungrouped nodes sit at the top level alongside the group containers.
|
|
87
|
+
for (const n of spec.nodes)
|
|
88
|
+
if (!grouped.has(n.id))
|
|
89
|
+
children.push(makeNode(n));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
for (const n of spec.nodes)
|
|
93
|
+
children.push(makeNode(n));
|
|
94
|
+
}
|
|
95
|
+
const edges = spec.edges.map((e, i) => ({
|
|
96
|
+
id: `e${i}`,
|
|
97
|
+
sources: [e.from],
|
|
98
|
+
targets: [e.to],
|
|
99
|
+
labels: e.label
|
|
100
|
+
? [{ text: e.label, width: estimateTextWidth(e.label, EDGE_LABEL_FONT_SIZE), height: 14 }]
|
|
101
|
+
: [],
|
|
102
|
+
}));
|
|
103
|
+
return {
|
|
104
|
+
id: 'root',
|
|
105
|
+
layoutOptions: {
|
|
106
|
+
'elk.algorithm': 'layered',
|
|
107
|
+
'elk.direction': elkDirection(spec.direction, spec.kind),
|
|
108
|
+
'elk.spacing.nodeNode': '36',
|
|
109
|
+
'elk.layered.spacing.nodeNodeBetweenLayers': '64',
|
|
110
|
+
'elk.spacing.edgeNode': '20',
|
|
111
|
+
'elk.layered.spacing.edgeNodeBetweenLayers': '20',
|
|
112
|
+
'elk.edgeRouting': 'ORTHOGONAL',
|
|
113
|
+
'elk.hierarchyHandling': 'INCLUDE_CHILDREN',
|
|
114
|
+
},
|
|
115
|
+
children,
|
|
116
|
+
edges,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** Recursively collect absolute-positioned boxes (ELK child coords are parent-relative). */
|
|
120
|
+
function collectBoxes(node, offsetX, offsetY, spec, boxes, svg) {
|
|
121
|
+
for (const child of node.children ?? []) {
|
|
122
|
+
const ax = offsetX + (child.x ?? 0);
|
|
123
|
+
const ay = offsetY + (child.y ?? 0);
|
|
124
|
+
if (child.id.startsWith('group:')) {
|
|
125
|
+
// Group container box behind its members.
|
|
126
|
+
const gId = child.id.slice('group:'.length);
|
|
127
|
+
const g = spec.groups?.find((gr) => gr.id === gId);
|
|
128
|
+
const p = palette(g?.emphasis ?? 'muted');
|
|
129
|
+
svg.push(`<rect x="${ax.toFixed(1)}" y="${ay.toFixed(1)}" width="${(child.width ?? 0).toFixed(1)}" height="${(child.height ?? 0).toFixed(1)}" rx="10" fill="${p.fill}" fill-opacity="0.35" stroke="${p.stroke}" stroke-width="1.5" stroke-dasharray="5 4"/>`);
|
|
130
|
+
if (child.labels?.[0]) {
|
|
131
|
+
svg.push(`<text x="${(ax + 12).toFixed(1)}" y="${(ay + 18).toFixed(1)}" font-size="12" font-weight="600" fill="${p.text}">${esc(child.labels[0].text)}</text>`);
|
|
132
|
+
}
|
|
133
|
+
boxes.push({ id: child.id, x: ax, y: ay, w: child.width ?? 0, h: child.height ?? 0, label: '', fontSize: 12, role: 'group' });
|
|
134
|
+
collectBoxes(child, ax, ay, spec, boxes, svg);
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
const node2 = spec.nodes.find((n) => n.id === child.id);
|
|
138
|
+
const p = palette(node2.emphasis);
|
|
139
|
+
const w = child.width ?? 0;
|
|
140
|
+
const h = child.height ?? 0;
|
|
141
|
+
const isDiamond = node2.shape === 'diamond';
|
|
142
|
+
const isRound = node2.shape === 'pill' || node2.shape === 'rounded' || node2.shape === 'circle';
|
|
143
|
+
const cx = ax + w / 2;
|
|
144
|
+
if (isDiamond) {
|
|
145
|
+
svg.push(`<polygon points="${cx.toFixed(1)},${ay.toFixed(1)} ${(ax + w).toFixed(1)},${(ay + h / 2).toFixed(1)} ${cx.toFixed(1)},${(ay + h).toFixed(1)} ${ax.toFixed(1)},${(ay + h / 2).toFixed(1)}" fill="${p.fill}" stroke="${p.stroke}" stroke-width="1.5"/>`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
svg.push(`<rect x="${ax.toFixed(1)}" y="${ay.toFixed(1)}" width="${w.toFixed(1)}" height="${h.toFixed(1)}" rx="${isRound ? 14 : 4}" fill="${p.fill}" stroke="${p.stroke}" stroke-width="1.5"/>`);
|
|
149
|
+
}
|
|
150
|
+
// Re-wrap to the box's actual inner width so multi-line labels fit.
|
|
151
|
+
const lines = wrapText(node2.label, w - NODE_PADDING_X * 2, NODE_LABEL_FONT_SIZE);
|
|
152
|
+
const contentWidth = Math.max(...lines.map((l) => estimateTextWidth(l, NODE_LABEL_FONT_SIZE)));
|
|
153
|
+
const subBlock = node2.sublabel ? NODE_SUBLABEL_FONT_SIZE + 4 : 0;
|
|
154
|
+
const labelBlock = lines.length * LABEL_LINE_HEIGHT;
|
|
155
|
+
// Vertically center the (label block + sublabel) within the box.
|
|
156
|
+
const blockTop = ay + (h - labelBlock - subBlock) / 2;
|
|
157
|
+
const tspans = lines
|
|
158
|
+
.map((ln, i) => `<tspan x="${cx.toFixed(1)}" y="${(blockTop + i * LABEL_LINE_HEIGHT + LABEL_LINE_HEIGHT / 2).toFixed(1)}">${esc(ln)}</tspan>`)
|
|
159
|
+
.join('');
|
|
160
|
+
svg.push(`<text font-size="${NODE_LABEL_FONT_SIZE}" fill="${p.text}" text-anchor="middle" dominant-baseline="middle" font-weight="500">${tspans}</text>`);
|
|
161
|
+
if (node2.sublabel) {
|
|
162
|
+
svg.push(`<text x="${cx.toFixed(1)}" y="${(blockTop + labelBlock + subBlock / 2).toFixed(1)}" font-size="${NODE_SUBLABEL_FONT_SIZE}" fill="${p.text}" text-anchor="middle" dominant-baseline="middle" opacity="0.75">${esc(node2.sublabel)}</text>`);
|
|
163
|
+
}
|
|
164
|
+
boxes.push({ id: child.id, x: ax, y: ay, w, h, label: node2.label, fontSize: NODE_LABEL_FONT_SIZE, contentWidth, role: 'node' });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export const elkTechnique = {
|
|
169
|
+
key: 'elk',
|
|
170
|
+
name: 'elkjs (layered graph)',
|
|
171
|
+
blurb: 'ELK computes node coords + routes edges (orthogonal). Engine for flow/architecture/state/ER graphs.',
|
|
172
|
+
async render(spec) {
|
|
173
|
+
const graph = buildElkGraph(spec);
|
|
174
|
+
const laid = (await elk.layout(graph));
|
|
175
|
+
const boxes = [];
|
|
176
|
+
const nodeSvg = [];
|
|
177
|
+
collectBoxes(laid, CANVAS_PADDING, CANVAS_PADDING, spec, boxes, nodeSvg);
|
|
178
|
+
// Edges (top-level; ELK routes them with absolute coords at root level).
|
|
179
|
+
const placedEdges = [];
|
|
180
|
+
const edgeSvg = [];
|
|
181
|
+
const byId = new Map((laid.edges ?? []).map((e) => [e.id, e]));
|
|
182
|
+
spec.edges.forEach((e, i) => {
|
|
183
|
+
const le = byId.get(`e${i}`);
|
|
184
|
+
const sec = le?.sections?.[0];
|
|
185
|
+
if (!sec)
|
|
186
|
+
return;
|
|
187
|
+
const ox = CANVAS_PADDING;
|
|
188
|
+
const oy = CANVAS_PADDING;
|
|
189
|
+
const pts = [sec.startPoint, ...(sec.bendPoints ?? []), sec.endPoint].map((p) => ({
|
|
190
|
+
x: p.x + ox,
|
|
191
|
+
y: p.y + oy,
|
|
192
|
+
}));
|
|
193
|
+
const dash = e.style === 'dashed' ? ' stroke-dasharray="6 4"' : e.style === 'dotted' ? ' stroke-dasharray="2 4"' : '';
|
|
194
|
+
const d = pts.map((p, idx) => `${idx === 0 ? 'M' : 'L'}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(' ');
|
|
195
|
+
const marker = e.arrow === 'none' ? '' : ' marker-end="url(#arrow-filled)"';
|
|
196
|
+
edgeSvg.push(`<path d="${d}" fill="none" stroke="#64748b" stroke-width="1.5"${dash}${marker}/>`);
|
|
197
|
+
// Record the last segment for crossing detection.
|
|
198
|
+
const a = pts[pts.length - 2] ?? pts[0];
|
|
199
|
+
const b = pts[pts.length - 1];
|
|
200
|
+
placedEdges.push({ x1: a.x, y1: a.y, x2: b.x, y2: b.y, fromId: e.from, toId: e.to });
|
|
201
|
+
if (e.label && le?.labels?.[0]) {
|
|
202
|
+
const mid = pts[Math.floor(pts.length / 2)];
|
|
203
|
+
edgeSvg.push(`<text x="${mid.x.toFixed(1)}" y="${(mid.y - 4).toFixed(1)}" font-size="${EDGE_LABEL_FONT_SIZE}" fill="#475569" text-anchor="middle">${esc(e.label)}</text>`);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
const width = Math.ceil((laid.width ?? 0) + CANVAS_PADDING * 2);
|
|
207
|
+
const height = Math.ceil((laid.height ?? 0) + CANVAS_PADDING * 2);
|
|
208
|
+
const title = spec.title
|
|
209
|
+
? `<text x="${CANVAS_PADDING}" y="24" font-size="16" font-weight="600" fill="#0f172a">${esc(spec.title)}</text>`
|
|
210
|
+
: '';
|
|
211
|
+
const body = [title, ...edgeSvg, ...nodeSvg].join('\n');
|
|
212
|
+
const svg = svgDocument(width, height, body);
|
|
213
|
+
return {
|
|
214
|
+
svg,
|
|
215
|
+
width,
|
|
216
|
+
height,
|
|
217
|
+
boxes,
|
|
218
|
+
edges: placedEdges,
|
|
219
|
+
features: {
|
|
220
|
+
// ELK draws plain graph nodes — not the rich panel features.
|
|
221
|
+
cards: false,
|
|
222
|
+
checkpoints: false,
|
|
223
|
+
twoColumn: false,
|
|
224
|
+
legend: false,
|
|
225
|
+
categoryColors: false,
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
},
|
|
229
|
+
};
|
|
230
|
+
//# sourceMappingURL=elk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elk.js","sourceRoot":"","sources":["../../src/adapters/elk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,8DAA8D;AAC9D,OAAO,cAAc,MAAM,0BAA0B,CAAC;AACtD,8DAA8D;AAC9D,MAAM,GAAG,GAAG,cAAqB,CAAC;AAClC,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,cAAc,EACd,cAAc,EACd,cAAc,EACd,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,GAAG,EAAE,iBAAiB,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAElF,MAAM,iBAAiB,GAAG,EAAE,CAAC,CAAC,4BAA4B;AAG1D,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;AAEtB,kCAAkC;AAClC,SAAS,YAAY,CAAC,GAA0B,EAAE,IAAY;IAC5D,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC1F,QAAQ,CAAC,EAAE,CAAC;QACV,KAAK,IAAI;YACP,OAAO,OAAO,CAAC;QACjB,KAAK,IAAI;YACP,OAAO,MAAM,CAAC;QAChB,KAAK,IAAI;YACP,OAAO,IAAI,CAAC;QACd;YACE,OAAO,MAAM,CAAC;IAClB,CAAC;AACH,CAAC;AAED,SAAS,KAAK,CAAC,CAAS,EAAE,EAAU,EAAE,EAAU;IAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;GAQG;AACH,SAAS,QAAQ,CAAC,IAAiB;IAMjC,MAAM,QAAQ,GAAG,cAAc,GAAG,cAAc,GAAG,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,oBAAoB,CAAC,CAAC;IACnE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC;IAC/F,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,EAAE,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3F,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,CAAC,GAAG,cAAc,GAAG,CAAC,EAAE,cAAc,EAAE,cAAc,CAAC,CAAC;IACvG,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,GAAG,iBAAiB,CAAC;IACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,uBAAuB,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,GAAG,QAAQ,GAAG,cAAc,GAAG,CAAC,CAAC,CAAC;IACrF,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;AAChD,CAAC;AAwBD,gEAAgE;AAChE,SAAS,aAAa,CAAC,IAAiB;IACtC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK;QAAE,IAAI,CAAC,CAAC,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC;IAEpE,MAAM,QAAQ,GAAG,CAAC,CAAc,EAAW,EAAE;QAC3C,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACtC,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IACrC,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACzD,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACvC,QAAQ,CAAC,IAAI,CAAC;gBACZ,EAAE,EAAE,SAAS,CAAC,CAAC,EAAE,EAAE;gBACnB,aAAa,EAAE,EAAE,aAAa,EAAE,qCAAqC,EAAE;gBACvE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;gBAC7F,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAE,CAAC,CAAC;aACzD,CAAC,CAAC;QACL,CAAC;QACD,uEAAuE;QACvE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACjF,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK;YAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;IAED,MAAM,KAAK,GAAc,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACjD,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACjB,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACf,MAAM,EAAE,CAAC,CAAC,KAAK;YACb,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,EAAE,oBAAoB,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;YAC1F,CAAC,CAAC,EAAE;KACP,CAAC,CAAC,CAAC;IAEJ,OAAO;QACL,EAAE,EAAE,MAAM;QACV,aAAa,EAAE;YACb,eAAe,EAAE,SAAS;YAC1B,eAAe,EAAE,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC;YACxD,sBAAsB,EAAE,IAAI;YAC5B,2CAA2C,EAAE,IAAI;YACjD,sBAAsB,EAAE,IAAI;YAC5B,2CAA2C,EAAE,IAAI;YACjD,iBAAiB,EAAE,YAAY;YAC/B,uBAAuB,EAAE,kBAAkB;SAC5C;QACD,QAAQ;QACR,KAAK;KACN,CAAC;AACJ,CAAC;AAED,4FAA4F;AAC5F,SAAS,YAAY,CACnB,IAAa,EACb,OAAe,EACf,OAAe,EACf,IAAiB,EACjB,KAAkB,EAClB,GAAa;IAEb,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QACxC,MAAM,EAAE,GAAG,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,MAAM,EAAE,GAAG,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpC,IAAI,KAAK,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAClC,0CAA0C;YAC1C,MAAM,GAAG,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC5C,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC;YACnD,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,QAAQ,IAAI,OAAO,CAAC,CAAC;YAC1C,GAAG,CAAC,IAAI,CACN,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,IAAI,iCAAiC,CAAC,CAAC,MAAM,+CAA+C,CACpP,CAAC;YACF,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACtB,GAAG,CAAC,IAAI,CACN,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,4CAA4C,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CACtJ,CAAC;YACJ,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YAC9H,YAAY,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,EAAE,CAAE,CAAC;YACzD,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAClC,MAAM,CAAC,GAAG,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;YAC3B,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;YAC5B,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC;YAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,KAAK,MAAM,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,CAAC;YAChG,MAAM,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;YACtB,IAAI,SAAS,EAAE,CAAC;gBACd,GAAG,CAAC,IAAI,CACN,oBAAoB,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,MAAM,wBAAwB,CACvP,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,IAAI,CACN,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,MAAM,wBAAwB,CACvL,CAAC;YACJ,CAAC;YACD,oEAAoE;YACpE,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,cAAc,GAAG,CAAC,EAAE,oBAAoB,CAAC,CAAC;YAClF,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,CAAC,CAAC;YAC/F,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,uBAAuB,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAClE,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,GAAG,iBAAiB,CAAC;YACpD,iEAAiE;YACjE,MAAM,QAAQ,GAAG,EAAE,GAAG,CAAC,CAAC,GAAG,UAAU,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;YACtD,MAAM,MAAM,GAAG,KAAK;iBACjB,GAAG,CACF,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CACR,aAAa,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,GAAG,CAAC,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,UAAU,CAChI;iBACA,IAAI,CAAC,EAAE,CAAC,CAAC;YACZ,GAAG,CAAC,IAAI,CACN,oBAAoB,oBAAoB,WAAW,CAAC,CAAC,IAAI,uEAAuE,MAAM,SAAS,CAChJ,CAAC;YACF,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnB,GAAG,CAAC,IAAI,CACN,YAAY,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,uBAAuB,WAAW,CAAC,CAAC,IAAI,oEAAoE,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAC3O,CAAC;YACJ,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,oBAAoB,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACnI,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAc;IACrC,GAAG,EAAE,KAAK;IACV,IAAI,EAAE,uBAAuB;IAC7B,KAAK,EAAE,qGAAqG;IAC5G,KAAK,CAAC,MAAM,CAAC,IAAiB;QAC5B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,KAAc,CAAC,CAA+E,CAAC;QAE9H,MAAM,KAAK,GAAgB,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,YAAY,CAAC,IAAI,EAAE,cAAc,EAAE,cAAc,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QAEzE,yEAAyE;QACzE,MAAM,WAAW,GAAiB,EAAE,CAAC;QACrC,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,GAAG,CAAkB,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAChF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC1B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7B,MAAM,GAAG,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;YAC9B,IAAI,CAAC,GAAG;gBAAE,OAAO;YACjB,MAAM,EAAE,GAAG,cAAc,CAAC;YAC1B,MAAM,EAAE,GAAG,cAAc,CAAC;YAC1B,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,GAAG,CAAC,UAAU,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBAChF,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE;gBACX,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE;aACZ,CAAC,CAAC,CAAC;YACJ,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,EAAE,CAAC;YACtH,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACvG,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kCAAkC,CAAC;YAC5E,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,oDAAoD,IAAI,GAAG,MAAM,IAAI,CAAC,CAAC;YACjG,kDAAkD;YAClD,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;YACxC,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAC9B,WAAW,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACrF,IAAI,CAAC,CAAC,KAAK,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/B,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC5C,OAAO,CAAC,IAAI,CACV,YAAY,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB,oBAAoB,yCAAyC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAC7J,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,cAAc,GAAG,CAAC,CAAC,CAAC;QAChE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,cAAc,GAAG,CAAC,CAAC,CAAC;QAClE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK;YACtB,CAAC,CAAC,YAAY,cAAc,4DAA4D,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS;YAChH,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxD,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QAE7C,OAAO;YACL,GAAG;YACH,KAAK;YACL,MAAM;YACN,KAAK;YACL,KAAK,EAAE,WAAW;YAClB,QAAQ,EAAE;gBACR,6DAA6D;gBAC7D,KAAK,EAAE,KAAK;gBACZ,WAAW,EAAE,KAAK;gBAClB,SAAS,EAAE,KAAK;gBAChB,MAAM,EAAE,KAAK;gBACb,cAAc,EAAE,KAAK;aACtB;SACF,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Twemoji asset code for an emoji grapheme (FE0F stripped unless ZWJ-joined). */
|
|
2
|
+
export declare function emojiCode(emoji: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve an emoji to a base64 SVG data-URI for Satori, via disk + memory cache.
|
|
5
|
+
*
|
|
6
|
+
* @param emoji a single emoji grapheme
|
|
7
|
+
* @returns a data-URI for the Twemoji SVG, or a transparent pixel if unresolved
|
|
8
|
+
*/
|
|
9
|
+
export declare function emojiToDataUri(emoji: string): Promise<string>;
|
|
10
|
+
//# sourceMappingURL=emoji.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emoji.d.ts","sourceRoot":"","sources":["../../src/adapters/emoji.ts"],"names":[],"mappings":"AA6CA,kFAAkF;AAClF,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG/C;AAID;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BnE"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Emoji rendering for Satori. Satori has no emoji font, so it calls
|
|
3
|
+
* `loadAdditionalAsset('emoji', segment)` for each emoji grapheme and expects an
|
|
4
|
+
* image data-URI back. We map the emoji to a Twemoji SVG (jdecked's maintained
|
|
5
|
+
* fork), cache it to `assets/emoji/` so the lab runs offline after the first
|
|
6
|
+
* fetch, and return it base64-encoded.
|
|
7
|
+
*
|
|
8
|
+
* Codepoint derivation follows Twemoji's own filename rule: strip the FE0F
|
|
9
|
+
* variation selector UNLESS the sequence is a ZWJ join — otherwise the asset
|
|
10
|
+
* name won't match (e.g. ⚙️ U+2699 U+FE0F → "2699.svg").
|
|
11
|
+
*/
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
13
|
+
import { dirname, resolve } from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const EMOJI_DIR = resolve(__dirname, '../../assets/emoji');
|
|
17
|
+
const TWEMOJI_BASE = 'https://cdn.jsdelivr.net/gh/jdecked/twemoji@latest/assets/svg';
|
|
18
|
+
const U200D = String.fromCharCode(0x200d); // zero-width joiner
|
|
19
|
+
const UFE0F = /️/g; // variation selector-16
|
|
20
|
+
/** A 1×1 transparent SVG, returned when an emoji can't be resolved (offline / 404). */
|
|
21
|
+
const TRANSPARENT = `data:image/svg+xml;base64,${Buffer.from('<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"/>').toString('base64')}`;
|
|
22
|
+
/** Convert a (possibly surrogate-pair) string to dash-joined hex codepoints. */
|
|
23
|
+
function toCodePoint(s) {
|
|
24
|
+
const out = [];
|
|
25
|
+
let prev = 0;
|
|
26
|
+
for (let i = 0; i < s.length; i++) {
|
|
27
|
+
const c = s.charCodeAt(i);
|
|
28
|
+
if (prev) {
|
|
29
|
+
out.push((0x10000 + ((prev - 0xd800) << 10) + (c - 0xdc00)).toString(16));
|
|
30
|
+
prev = 0;
|
|
31
|
+
}
|
|
32
|
+
else if (c >= 0xd800 && c <= 0xdbff) {
|
|
33
|
+
prev = c;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
out.push(c.toString(16));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return out.join('-');
|
|
40
|
+
}
|
|
41
|
+
/** Twemoji asset code for an emoji grapheme (FE0F stripped unless ZWJ-joined). */
|
|
42
|
+
export function emojiCode(emoji) {
|
|
43
|
+
const grapheme = emoji.indexOf(U200D) < 0 ? emoji.replace(UFE0F, '') : emoji;
|
|
44
|
+
return toCodePoint(grapheme);
|
|
45
|
+
}
|
|
46
|
+
const memCache = new Map(); // code -> data URI
|
|
47
|
+
/**
|
|
48
|
+
* Resolve an emoji to a base64 SVG data-URI for Satori, via disk + memory cache.
|
|
49
|
+
*
|
|
50
|
+
* @param emoji a single emoji grapheme
|
|
51
|
+
* @returns a data-URI for the Twemoji SVG, or a transparent pixel if unresolved
|
|
52
|
+
*/
|
|
53
|
+
export async function emojiToDataUri(emoji) {
|
|
54
|
+
const code = emojiCode(emoji);
|
|
55
|
+
const cached = memCache.get(code);
|
|
56
|
+
if (cached)
|
|
57
|
+
return cached;
|
|
58
|
+
mkdirSync(EMOJI_DIR, { recursive: true });
|
|
59
|
+
const file = resolve(EMOJI_DIR, `${code}.svg`);
|
|
60
|
+
let svg;
|
|
61
|
+
if (existsSync(file)) {
|
|
62
|
+
svg = readFileSync(file, 'utf8');
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
try {
|
|
66
|
+
const res = await fetch(`${TWEMOJI_BASE}/${code}.svg`);
|
|
67
|
+
if (res.ok) {
|
|
68
|
+
svg = await res.text();
|
|
69
|
+
writeFileSync(file, svg, 'utf8');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// offline or fetch error — fall through to the transparent placeholder
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const uri = svg ? `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}` : TRANSPARENT;
|
|
77
|
+
memCache.set(code, uri);
|
|
78
|
+
return uri;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=emoji.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emoji.js","sourceRoot":"","sources":["../../src/adapters/emoji.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,EAAE,oBAAoB,CAAC,CAAC;AAC3D,MAAM,YAAY,GAAG,+DAA+D,CAAC;AAErF,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,oBAAoB;AAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,wBAAwB;AAE5C,uFAAuF;AACvF,MAAM,WAAW,GAAG,6BAA6B,MAAM,CAAC,IAAI,CAC1D,gEAAgE,CACjE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAEvB,gFAAgF;AAChF,SAAS,WAAW,CAAC,CAAS;IAC5B,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC1B,IAAI,IAAI,EAAE,CAAC;YACT,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;YAC1E,IAAI,GAAG,CAAC,CAAC;QACX,CAAC;aAAM,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,IAAI,MAAM,EAAE,CAAC;YACtC,IAAI,GAAG,CAAC,CAAC;QACX,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,SAAS,CAAC,KAAa;IACrC,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC7E,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,mBAAmB;AAE/D;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAa;IAChD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,EAAE,GAAG,IAAI,MAAM,CAAC,CAAC;IAC/C,IAAI,GAAuB,CAAC;IAE5B,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,YAAY,IAAI,IAAI,MAAM,CAAC,CAAC;YACvD,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;gBACX,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBACvB,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,uEAAuE;QACzE,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,6BAA6B,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC;IACnG,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACxB,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface SatoriFont {
|
|
2
|
+
name: string;
|
|
3
|
+
data: Buffer;
|
|
4
|
+
weight: 400 | 600 | 700;
|
|
5
|
+
style: 'normal';
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Load the Inter font set (regular / semibold / bold) for Satori.
|
|
9
|
+
*
|
|
10
|
+
* @returns the font descriptors Satori expects in its `fonts` option
|
|
11
|
+
*/
|
|
12
|
+
export declare function loadFonts(): SatoriFont[];
|
|
13
|
+
//# sourceMappingURL=fonts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fonts.d.ts","sourceRoot":"","sources":["../../src/adapters/fonts.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACxB,KAAK,EAAE,QAAQ,CAAC;CACjB;AAID;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,UAAU,EAAE,CAQxC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Font loading for Satori. Satori needs at least one font buffer (it has no
|
|
3
|
+
* system font access). We bundle Inter TTFs (OFL-licensed) under assets/ so the
|
|
4
|
+
* lab is self-contained and runs offline after clone. Loaded once and cached.
|
|
5
|
+
*/
|
|
6
|
+
import { readFileSync } from 'node:fs';
|
|
7
|
+
import { dirname, resolve } from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const ASSETS = resolve(__dirname, '../../assets');
|
|
11
|
+
let cached = null;
|
|
12
|
+
/**
|
|
13
|
+
* Load the Inter font set (regular / semibold / bold) for Satori.
|
|
14
|
+
*
|
|
15
|
+
* @returns the font descriptors Satori expects in its `fonts` option
|
|
16
|
+
*/
|
|
17
|
+
export function loadFonts() {
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
cached = [
|
|
21
|
+
{ name: 'Inter', data: readFileSync(resolve(ASSETS, 'inter-400.ttf')), weight: 400, style: 'normal' },
|
|
22
|
+
{ name: 'Inter', data: readFileSync(resolve(ASSETS, 'inter-600.ttf')), weight: 600, style: 'normal' },
|
|
23
|
+
{ name: 'Inter', data: readFileSync(resolve(ASSETS, 'inter-700.ttf')), weight: 700, style: 'normal' },
|
|
24
|
+
];
|
|
25
|
+
return cached;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=fonts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fonts.js","sourceRoot":"","sources":["../../src/adapters/fonts.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;AASlD,IAAI,MAAM,GAAwB,IAAI,CAAC;AAEvC;;;;GAIG;AACH,MAAM,UAAU,SAAS;IACvB,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAC1B,MAAM,GAAG;QACP,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE;QACrG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE;QACrG,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE;KACtG,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for building Satori element trees + the card colour palette.
|
|
3
|
+
*
|
|
4
|
+
* Two things live here so the technique file stays about LAYOUT, not plumbing:
|
|
5
|
+
* - `el()` — a tiny factory for the React-element-shaped objects Satori reads
|
|
6
|
+
* (we avoid JSX to skip the react/jsx toolchain; Satori only needs
|
|
7
|
+
* `{ type, props }`).
|
|
8
|
+
* - `CARD_PALETTE` / category resolution + `stripEmoji` + `monogram`.
|
|
9
|
+
*
|
|
10
|
+
* Why strip emoji: Satori throws when a glyph isn't in any supplied font, and
|
|
11
|
+
* Inter has no emoji. v1 renders a category-tinted monogram chip instead; real
|
|
12
|
+
* emoji (Twemoji via loadAdditionalAsset) is a tracked follow-up.
|
|
13
|
+
*/
|
|
14
|
+
import type { Emphasis } from '../domain/spec.js';
|
|
15
|
+
/** Loose element type matching what Satori accepts as a node. */
|
|
16
|
+
export interface SatoriEl {
|
|
17
|
+
type: string;
|
|
18
|
+
props: {
|
|
19
|
+
style?: Record<string, unknown>;
|
|
20
|
+
children?: unknown;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Build a Satori element.
|
|
25
|
+
*
|
|
26
|
+
* @param type HTML-ish tag (only `div` / `span` are needed)
|
|
27
|
+
* @param style flat CSS style object (Satori supports a flexbox subset)
|
|
28
|
+
* @param children child element(s) or text; nullish children are dropped
|
|
29
|
+
* @returns an element object Satori can render
|
|
30
|
+
*/
|
|
31
|
+
export declare function el(type: string, style: Record<string, unknown>, children?: unknown): SatoriEl;
|
|
32
|
+
/**
|
|
33
|
+
* Saturated card palette: white text on a strong fill, matching the reference
|
|
34
|
+
* image's category colours. Distinct from the light `PALETTE` in svg.ts (used by
|
|
35
|
+
* the baseline's outlined boxes) — cards need bold fills.
|
|
36
|
+
*/
|
|
37
|
+
export declare const CARD_PALETTE: Record<Emphasis, string>;
|
|
38
|
+
/** Gold checkpoint-pill colours (fill / border / text). */
|
|
39
|
+
export declare const CHECKPOINT_COLORS: {
|
|
40
|
+
fill: string;
|
|
41
|
+
border: string;
|
|
42
|
+
text: string;
|
|
43
|
+
};
|
|
44
|
+
/** Strip emoji / pictographic glyphs so Satori never hits a missing-glyph throw. */
|
|
45
|
+
export declare function stripEmoji(s: string): string;
|
|
46
|
+
/** First letters of the first two words, for the monogram icon chip. */
|
|
47
|
+
export declare function monogram(label: string): string;
|
|
48
|
+
//# sourceMappingURL=satori-helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"satori-helpers.d.ts","sourceRoot":"","sources":["../../src/adapters/satori-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAElD,iEAAiE;AACjE,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAChE;AAED;;;;;;;GAOG;AACH,wBAAgB,EAAE,CAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,QAAQ,CAAC,EAAE,OAAO,GACjB,QAAQ,CAKV;AAED;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAOjD,CAAC;AAEF,2DAA2D;AAC3D,eAAO,MAAM,iBAAiB;;;;CAA0D,CAAC;AAEzF,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE5C;AAED,wEAAwE;AACxE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK9C"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build a Satori element.
|
|
3
|
+
*
|
|
4
|
+
* @param type HTML-ish tag (only `div` / `span` are needed)
|
|
5
|
+
* @param style flat CSS style object (Satori supports a flexbox subset)
|
|
6
|
+
* @param children child element(s) or text; nullish children are dropped
|
|
7
|
+
* @returns an element object Satori can render
|
|
8
|
+
*/
|
|
9
|
+
export function el(type, style, children) {
|
|
10
|
+
const kids = Array.isArray(children)
|
|
11
|
+
? children.filter((c) => c !== null && c !== undefined && c !== false)
|
|
12
|
+
: children;
|
|
13
|
+
return { type, props: { style, children: kids } };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Saturated card palette: white text on a strong fill, matching the reference
|
|
17
|
+
* image's category colours. Distinct from the light `PALETTE` in svg.ts (used by
|
|
18
|
+
* the baseline's outlined boxes) — cards need bold fills.
|
|
19
|
+
*/
|
|
20
|
+
export const CARD_PALETTE = {
|
|
21
|
+
primary: '#1e4e79', // retrieval — blue
|
|
22
|
+
accent: '#4b3f9e', // reasoning — purple
|
|
23
|
+
danger: '#8a3a2c', // hitl — rust
|
|
24
|
+
success: '#3f6e26', // execution — green
|
|
25
|
+
normal: '#3b4250', // observability — slate
|
|
26
|
+
muted: '#64748b',
|
|
27
|
+
};
|
|
28
|
+
/** Gold checkpoint-pill colours (fill / border / text). */
|
|
29
|
+
export const CHECKPOINT_COLORS = { fill: '#6b5212', border: '#b8923a', text: '#ffd98a' };
|
|
30
|
+
/** Strip emoji / pictographic glyphs so Satori never hits a missing-glyph throw. */
|
|
31
|
+
export function stripEmoji(s) {
|
|
32
|
+
return s.replace(/[\p{Extended_Pictographic}️⏸]/gu, '').trim();
|
|
33
|
+
}
|
|
34
|
+
/** First letters of the first two words, for the monogram icon chip. */
|
|
35
|
+
export function monogram(label) {
|
|
36
|
+
const words = stripEmoji(label).split(/\s+/).filter(Boolean);
|
|
37
|
+
if (words.length === 0)
|
|
38
|
+
return '?';
|
|
39
|
+
if (words.length === 1)
|
|
40
|
+
return words[0].slice(0, 1).toUpperCase();
|
|
41
|
+
return (words[0][0] + words[1][0]).toUpperCase();
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=satori-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"satori-helpers.js","sourceRoot":"","sources":["../../src/adapters/satori-helpers.ts"],"names":[],"mappings":"AAqBA;;;;;;;GAOG;AACH,MAAM,UAAU,EAAE,CAChB,IAAY,EACZ,KAA8B,EAC9B,QAAkB;IAElB,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;QAClC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,KAAK,CAAC;QACtE,CAAC,CAAC,QAAQ,CAAC;IACb,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAA6B;IACpD,OAAO,EAAE,SAAS,EAAE,mBAAmB;IACvC,MAAM,EAAE,SAAS,EAAE,qBAAqB;IACxC,MAAM,EAAE,SAAS,EAAE,cAAc;IACjC,OAAO,EAAE,SAAS,EAAE,oBAAoB;IACxC,MAAM,EAAE,SAAS,EAAE,wBAAwB;IAC3C,KAAK,EAAE,SAAS;CACjB,CAAC;AAEF,2DAA2D;AAC3D,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAEzF,oFAAoF;AACpF,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,OAAO,CAAC,CAAC,OAAO,CAAC,kCAAkC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAClE,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IACnC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAClE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"satori.d.ts","sourceRoot":"","sources":["../../src/adapters/satori.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAmRrE,eAAO,MAAM,eAAe,EAAE,SAkC7B,CAAC"}
|