@ideare/kit 0.0.0-stage → 0.1.0-next.1
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 +21 -0
- package/README.md +148 -2
- package/THIRD_PARTY.md +52 -0
- package/dist/brand/schema.d.ts +265 -0
- package/dist/brand/schema.js +271 -0
- package/dist/check/copy.d.ts +27 -0
- package/dist/check/copy.js +139 -0
- package/dist/check/index.d.ts +54 -0
- package/dist/check/index.js +568 -0
- package/dist/color/contrast.d.ts +41 -0
- package/dist/color/contrast.js +84 -0
- package/dist/color/palette.d.ts +27 -0
- package/dist/color/palette.js +114 -0
- package/dist/color/wcag.d.ts +22 -0
- package/dist/color/wcag.js +50 -0
- package/dist/draft/draft.d.ts +61 -0
- package/dist/draft/draft.js +383 -0
- package/dist/editor/history.d.ts +78 -0
- package/dist/editor/history.js +221 -0
- package/dist/editor/operations.d.ts +112 -0
- package/dist/editor/operations.js +621 -0
- package/dist/files/docs.d.ts +49 -0
- package/dist/files/docs.js +392 -0
- package/dist/files/generate.d.ts +47 -0
- package/dist/files/generate.js +254 -0
- package/dist/files/png.d.ts +40 -0
- package/dist/files/png.js +168 -0
- package/dist/files/raster.d.ts +14 -0
- package/dist/files/raster.js +0 -0
- package/dist/files/templates.d.ts +162 -0
- package/dist/files/templates.js +390 -0
- package/dist/files/tokens.d.ts +22 -0
- package/dist/files/tokens.js +204 -0
- package/dist/fonts/catalog.d.ts +3 -0
- package/dist/fonts/catalog.js +345 -0
- package/dist/fonts/ofl.d.ts +5 -0
- package/dist/fonts/ofl.js +5 -0
- package/dist/fonts/pick.d.ts +33 -0
- package/dist/fonts/pick.js +73 -0
- package/dist/imports/folder.d.ts +80 -0
- package/dist/imports/folder.js +124 -0
- package/dist/imports/unzip.d.ts +32 -0
- package/dist/imports/unzip.js +128 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +34 -0
- package/dist/marks/draw.d.ts +48 -0
- package/dist/marks/draw.js +149 -0
- package/dist/marks/icons.data.d.ts +5 -0
- package/dist/marks/icons.data.js +600 -0
- package/dist/marks/one-color.d.ts +45 -0
- package/dist/marks/one-color.js +304 -0
- package/dist/marks/svg.d.ts +53 -0
- package/dist/marks/svg.js +393 -0
- package/dist/node/index.d.ts +13 -0
- package/dist/node/index.js +41 -0
- package/dist/posts/pix.d.ts +19 -0
- package/dist/posts/pix.js +49 -0
- package/dist/posts/templates.d.ts +45 -0
- package/dist/posts/templates.js +868 -0
- package/dist/resvg/index.d.ts +2 -0
- package/dist/resvg/index.js +22 -0
- package/dist/scene/brand-art.d.ts +61 -0
- package/dist/scene/brand-art.js +217 -0
- package/dist/scene/frame.d.ts +89 -0
- package/dist/scene/frame.js +213 -0
- package/dist/scene/grain.d.ts +7 -0
- package/dist/scene/grain.js +7 -0
- package/dist/scene/qr.d.ts +10 -0
- package/dist/scene/qr.js +359 -0
- package/dist/scene/render.d.ts +76 -0
- package/dist/scene/render.js +557 -0
- package/dist/scene/schema.d.ts +271 -0
- package/dist/scene/schema.js +257 -0
- package/dist/sign.d.ts +47 -0
- package/dist/sign.js +88 -0
- package/dist/text/engine.d.ts +128 -0
- package/dist/text/engine.js +245 -0
- package/dist/text/fonts.d.ts +56 -0
- package/dist/text/fonts.js +65 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/dist/wasm/index.d.ts +15 -0
- package/dist/wasm/index.js +46 -0
- package/package.json +78 -5
- package/schema/brand.v1.json +442 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type MarkElement, type ParsedMark } from "./svg.ts";
|
|
2
|
+
/**
|
|
3
|
+
* One-color versions (stamps, embroidery, a logo over a photo) keep the symbol readable the way a
|
|
4
|
+
* printer would: a glyph on a tile is cut out of it. The tile prints in the one color and the
|
|
5
|
+
* glyph shows the ground through it, so the version works on any ground. Painting every part in
|
|
6
|
+
* the one color leaves a plain disc.
|
|
7
|
+
*/
|
|
8
|
+
/** A box in the mark's own units. */
|
|
9
|
+
type Box = {
|
|
10
|
+
x0: number;
|
|
11
|
+
y0: number;
|
|
12
|
+
x1: number;
|
|
13
|
+
y1: number;
|
|
14
|
+
};
|
|
15
|
+
/** A drawn shape with the paints it inherits ("none", "currentColor" or a hex) and its box. */
|
|
16
|
+
type MarkLeaf = {
|
|
17
|
+
element: MarkElement;
|
|
18
|
+
fill: string;
|
|
19
|
+
stroke: string;
|
|
20
|
+
box: Box;
|
|
21
|
+
};
|
|
22
|
+
/** A shape in a one-color version: whether its fill and its stroke are cut out. */
|
|
23
|
+
type OneColorPart = {
|
|
24
|
+
leaf: MarkLeaf;
|
|
25
|
+
fillHole: boolean;
|
|
26
|
+
strokeHole: boolean;
|
|
27
|
+
};
|
|
28
|
+
/** Every point a path reaches, control points included, in its own units. Relative commands,
|
|
29
|
+
* H and V, and arcs with packed flags (svgo writes "a1 1 0 011 1") all read. */
|
|
30
|
+
export declare function pathPoints(d: string): [number, number][];
|
|
31
|
+
/**
|
|
32
|
+
* Which paints become holes in a one-color version. Each part is read against what it sits on
|
|
33
|
+
* (the topmost filled part before it whose box holds it): in another color it takes the other
|
|
34
|
+
* state, so a glyph on a tile is cut out of it and a dot on that glyph prints again; in the same
|
|
35
|
+
* color it takes the same state. A part on the ground prints, unless it is white or close to it,
|
|
36
|
+
* which reads as the paper. A drawing that is all white (made for dark grounds) keeps every
|
|
37
|
+
* shape, since there is nothing to cut it from.
|
|
38
|
+
*/
|
|
39
|
+
export declare function oneColorParts(mark: ParsedMark): OneColorPart[];
|
|
40
|
+
/**
|
|
41
|
+
* The mark as a mask for its one-color version: white where the one color prints, black where a
|
|
42
|
+
* part is cut out. Undefined when nothing is cut out, so the mark can simply be recolored.
|
|
43
|
+
*/
|
|
44
|
+
export declare function knockoutMask(mark: ParsedMark): ParsedMark | undefined;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { contrast } from "../color/wcag.js";
|
|
2
|
+
import { IDENTITY, apply, withTransform, } from "./svg.js";
|
|
3
|
+
/** Under this contrast with white, a color reads as the paper: a hole or a highlight. */
|
|
4
|
+
const PAPER_CONTRAST = 1.3;
|
|
5
|
+
/** How far a part may reach past the tile's box and still sit on it, as a share of the mark. */
|
|
6
|
+
const SLACK = 0.02;
|
|
7
|
+
const NUMBER = /[-+]?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?/iy;
|
|
8
|
+
const COMMAND = /[MmLlHhVvCcSsQqTtAaZz]/;
|
|
9
|
+
/** Points along an arc (SVG's endpoint form), sampled from its center form. */
|
|
10
|
+
function arcPoints(x0, y0, radiusX, radiusY, degrees, large, sweep, x, y) {
|
|
11
|
+
let rx = Math.abs(radiusX);
|
|
12
|
+
let ry = Math.abs(radiusY);
|
|
13
|
+
if (!rx || !ry || (x0 === x && y0 === y))
|
|
14
|
+
return [[x, y]];
|
|
15
|
+
const phi = (degrees * Math.PI) / 180;
|
|
16
|
+
const cos = Math.cos(phi);
|
|
17
|
+
const sin = Math.sin(phi);
|
|
18
|
+
const dx = (x0 - x) / 2;
|
|
19
|
+
const dy = (y0 - y) / 2;
|
|
20
|
+
const x1 = cos * dx + sin * dy;
|
|
21
|
+
const y1 = -sin * dx + cos * dy;
|
|
22
|
+
const lambda = (x1 * x1) / (rx * rx) + (y1 * y1) / (ry * ry);
|
|
23
|
+
if (lambda > 1) {
|
|
24
|
+
rx *= Math.sqrt(lambda);
|
|
25
|
+
ry *= Math.sqrt(lambda);
|
|
26
|
+
}
|
|
27
|
+
const num = rx * rx * ry * ry - rx * rx * y1 * y1 - ry * ry * x1 * x1;
|
|
28
|
+
const den = rx * rx * y1 * y1 + ry * ry * x1 * x1;
|
|
29
|
+
const coef = (large === sweep ? -1 : 1) * Math.sqrt(Math.max(0, num / den));
|
|
30
|
+
const cxp = (coef * rx * y1) / ry;
|
|
31
|
+
const cyp = (-coef * ry * x1) / rx;
|
|
32
|
+
const cx = cos * cxp - sin * cyp + (x0 + x) / 2;
|
|
33
|
+
const cy = sin * cxp + cos * cyp + (y0 + y) / 2;
|
|
34
|
+
const start = Math.atan2((y1 - cyp) / ry, (x1 - cxp) / rx);
|
|
35
|
+
let delta = Math.atan2((-y1 - cyp) / ry, (-x1 - cxp) / rx) - start;
|
|
36
|
+
if (sweep && delta < 0)
|
|
37
|
+
delta += 2 * Math.PI;
|
|
38
|
+
else if (!sweep && delta > 0)
|
|
39
|
+
delta -= 2 * Math.PI;
|
|
40
|
+
const points = [];
|
|
41
|
+
for (let step = 1; step <= 16; step++) {
|
|
42
|
+
const t = start + (delta * step) / 16;
|
|
43
|
+
const ex = rx * Math.cos(t);
|
|
44
|
+
const ey = ry * Math.sin(t);
|
|
45
|
+
points.push([cos * ex - sin * ey + cx, sin * ex + cos * ey + cy]);
|
|
46
|
+
}
|
|
47
|
+
return points;
|
|
48
|
+
}
|
|
49
|
+
/** Every point a path reaches, control points included, in its own units. Relative commands,
|
|
50
|
+
* H and V, and arcs with packed flags (svgo writes "a1 1 0 011 1") all read. */
|
|
51
|
+
export function pathPoints(d) {
|
|
52
|
+
const points = [];
|
|
53
|
+
let i = 0;
|
|
54
|
+
const skip = () => {
|
|
55
|
+
while (i < d.length && /[\s,]/.test(d[i] ?? ""))
|
|
56
|
+
i++;
|
|
57
|
+
};
|
|
58
|
+
const number = () => {
|
|
59
|
+
skip();
|
|
60
|
+
NUMBER.lastIndex = i;
|
|
61
|
+
const match = NUMBER.exec(d);
|
|
62
|
+
if (!match)
|
|
63
|
+
return undefined;
|
|
64
|
+
i += match[0].length;
|
|
65
|
+
return Number(match[0]);
|
|
66
|
+
};
|
|
67
|
+
const flag = () => {
|
|
68
|
+
skip();
|
|
69
|
+
const char = d[i];
|
|
70
|
+
if (char !== "0" && char !== "1")
|
|
71
|
+
return undefined;
|
|
72
|
+
i++;
|
|
73
|
+
return Number(char);
|
|
74
|
+
};
|
|
75
|
+
let x = 0;
|
|
76
|
+
let y = 0;
|
|
77
|
+
let startX = 0;
|
|
78
|
+
let startY = 0;
|
|
79
|
+
let command = "";
|
|
80
|
+
for (;;) {
|
|
81
|
+
skip();
|
|
82
|
+
if (i >= d.length)
|
|
83
|
+
return points;
|
|
84
|
+
const char = d[i] ?? "";
|
|
85
|
+
if (COMMAND.test(char)) {
|
|
86
|
+
command = char;
|
|
87
|
+
i++;
|
|
88
|
+
if (command === "Z" || command === "z") {
|
|
89
|
+
x = startX;
|
|
90
|
+
y = startY;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const relative = command === command.toLowerCase();
|
|
95
|
+
const ox = relative ? x : 0;
|
|
96
|
+
const oy = relative ? y : 0;
|
|
97
|
+
const pairs = (count) => {
|
|
98
|
+
const out = [];
|
|
99
|
+
for (let n = 0; n < count; n++) {
|
|
100
|
+
const px = number();
|
|
101
|
+
const py = number();
|
|
102
|
+
if (px === undefined || py === undefined)
|
|
103
|
+
return undefined;
|
|
104
|
+
out.push([ox + px, oy + py]);
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
};
|
|
108
|
+
let drawn;
|
|
109
|
+
switch (command.toLowerCase()) {
|
|
110
|
+
case "m":
|
|
111
|
+
case "l":
|
|
112
|
+
case "t":
|
|
113
|
+
drawn = pairs(1);
|
|
114
|
+
break;
|
|
115
|
+
case "s":
|
|
116
|
+
case "q":
|
|
117
|
+
drawn = pairs(2);
|
|
118
|
+
break;
|
|
119
|
+
case "c":
|
|
120
|
+
drawn = pairs(3);
|
|
121
|
+
break;
|
|
122
|
+
case "h": {
|
|
123
|
+
const value = number();
|
|
124
|
+
drawn = value === undefined ? undefined : [[ox + value, y]];
|
|
125
|
+
break;
|
|
126
|
+
}
|
|
127
|
+
case "v": {
|
|
128
|
+
const value = number();
|
|
129
|
+
drawn = value === undefined ? undefined : [[x, oy + value]];
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case "a": {
|
|
133
|
+
const [rx, ry, rotation] = [number(), number(), number()];
|
|
134
|
+
const [large, sweep] = [flag(), flag()];
|
|
135
|
+
const end = pairs(1)?.[0];
|
|
136
|
+
drawn =
|
|
137
|
+
rx === undefined ||
|
|
138
|
+
ry === undefined ||
|
|
139
|
+
rotation === undefined ||
|
|
140
|
+
large === undefined ||
|
|
141
|
+
sweep === undefined ||
|
|
142
|
+
!end
|
|
143
|
+
? undefined
|
|
144
|
+
: arcPoints(x, y, rx, ry, rotation, large, sweep, end[0], end[1]);
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
default:
|
|
148
|
+
// A number with no command before it, or after a close: nothing more reads.
|
|
149
|
+
return points;
|
|
150
|
+
}
|
|
151
|
+
const last = drawn?.[drawn.length - 1];
|
|
152
|
+
if (!drawn || !last)
|
|
153
|
+
return points;
|
|
154
|
+
points.push(...drawn);
|
|
155
|
+
if (command === "M" || command === "m") {
|
|
156
|
+
startX = last[0];
|
|
157
|
+
startY = last[1];
|
|
158
|
+
// More pairs after a moveto are lines.
|
|
159
|
+
command = command === "M" ? "L" : "l";
|
|
160
|
+
}
|
|
161
|
+
[x, y] = last;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function corners(x0, y0, x1, y1) {
|
|
165
|
+
return [
|
|
166
|
+
[x0, y0],
|
|
167
|
+
[x1, y0],
|
|
168
|
+
[x0, y1],
|
|
169
|
+
[x1, y1],
|
|
170
|
+
];
|
|
171
|
+
}
|
|
172
|
+
function shapePoints(element) {
|
|
173
|
+
const attrs = element.attrs;
|
|
174
|
+
const n = (name) => Number(attrs[name] ?? 0) || 0;
|
|
175
|
+
switch (element.tag) {
|
|
176
|
+
case "circle":
|
|
177
|
+
return corners(n("cx") - n("r"), n("cy") - n("r"), n("cx") + n("r"), n("cy") + n("r"));
|
|
178
|
+
case "ellipse":
|
|
179
|
+
return corners(n("cx") - n("rx"), n("cy") - n("ry"), n("cx") + n("rx"), n("cy") + n("ry"));
|
|
180
|
+
case "rect":
|
|
181
|
+
return corners(n("x"), n("y"), n("x") + n("width"), n("y") + n("height"));
|
|
182
|
+
case "line":
|
|
183
|
+
return [
|
|
184
|
+
[n("x1"), n("y1")],
|
|
185
|
+
[n("x2"), n("y2")],
|
|
186
|
+
];
|
|
187
|
+
case "polyline":
|
|
188
|
+
case "polygon": {
|
|
189
|
+
const values = (attrs.points ?? "")
|
|
190
|
+
.trim()
|
|
191
|
+
.split(/[\s,]+/)
|
|
192
|
+
.map(Number);
|
|
193
|
+
const points = [];
|
|
194
|
+
for (let k = 0; k + 1 < values.length; k += 2)
|
|
195
|
+
points.push([values[k] ?? 0, values[k + 1] ?? 0]);
|
|
196
|
+
return points;
|
|
197
|
+
}
|
|
198
|
+
case "path":
|
|
199
|
+
return pathPoints(attrs.d ?? "");
|
|
200
|
+
case "g":
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/** Every drawn shape of a mark, bottom first, with its paints and its box in the mark's units. */
|
|
205
|
+
function markLeaves(mark) {
|
|
206
|
+
const leaves = [];
|
|
207
|
+
const walk = (element, fill, stroke, matrix) => {
|
|
208
|
+
const nextFill = element.attrs.fill ?? fill;
|
|
209
|
+
const nextStroke = element.attrs.stroke ?? stroke;
|
|
210
|
+
const nextMatrix = withTransform(matrix, element.attrs.transform);
|
|
211
|
+
for (const child of element.children)
|
|
212
|
+
walk(child, nextFill, nextStroke, nextMatrix);
|
|
213
|
+
if (element.tag === "g")
|
|
214
|
+
return;
|
|
215
|
+
const points = shapePoints(element).map(([x, y]) => apply(nextMatrix, x, y));
|
|
216
|
+
if (!points.length)
|
|
217
|
+
return;
|
|
218
|
+
const xs = points.map(([x]) => x);
|
|
219
|
+
const ys = points.map(([, y]) => y);
|
|
220
|
+
leaves.push({
|
|
221
|
+
element,
|
|
222
|
+
fill: nextFill,
|
|
223
|
+
stroke: nextStroke,
|
|
224
|
+
box: {
|
|
225
|
+
x0: Math.min(...xs),
|
|
226
|
+
y0: Math.min(...ys),
|
|
227
|
+
x1: Math.max(...xs),
|
|
228
|
+
y1: Math.max(...ys),
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
};
|
|
232
|
+
// An unset fill on the root paints in currentColor (markBody writes it so).
|
|
233
|
+
walk(mark.root, "currentColor", "none", IDENTITY);
|
|
234
|
+
return leaves;
|
|
235
|
+
}
|
|
236
|
+
function inside(inner, outer, slack) {
|
|
237
|
+
return (inner.x0 >= outer.x0 - slack &&
|
|
238
|
+
inner.y0 >= outer.y0 - slack &&
|
|
239
|
+
inner.x1 <= outer.x1 + slack &&
|
|
240
|
+
inner.y1 <= outer.y1 + slack);
|
|
241
|
+
}
|
|
242
|
+
/** Whether the paint reads as the paper: white, or close to it. */
|
|
243
|
+
function isPaper(paint) {
|
|
244
|
+
return paint.startsWith("#") && contrast(paint, "#ffffff") < PAPER_CONTRAST;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Which paints become holes in a one-color version. Each part is read against what it sits on
|
|
248
|
+
* (the topmost filled part before it whose box holds it): in another color it takes the other
|
|
249
|
+
* state, so a glyph on a tile is cut out of it and a dot on that glyph prints again; in the same
|
|
250
|
+
* color it takes the same state. A part on the ground prints, unless it is white or close to it,
|
|
251
|
+
* which reads as the paper. A drawing that is all white (made for dark grounds) keeps every
|
|
252
|
+
* shape, since there is nothing to cut it from.
|
|
253
|
+
*/
|
|
254
|
+
export function oneColorParts(mark) {
|
|
255
|
+
const slack = Math.max(mark.viewBox.w, mark.viewBox.h) * SLACK;
|
|
256
|
+
const parts = [];
|
|
257
|
+
for (const leaf of markLeaves(mark)) {
|
|
258
|
+
let below;
|
|
259
|
+
for (let k = parts.length - 1; k >= 0 && !below; k--) {
|
|
260
|
+
const part = parts[k];
|
|
261
|
+
if (part && part.leaf.fill !== "none" && inside(leaf.box, part.leaf.box, slack))
|
|
262
|
+
below = part;
|
|
263
|
+
}
|
|
264
|
+
const isHole = (paint) => paint !== "none" &&
|
|
265
|
+
(below
|
|
266
|
+
? paint === below.leaf.fill
|
|
267
|
+
? below.fillHole
|
|
268
|
+
: !below.fillHole
|
|
269
|
+
: isPaper(paint));
|
|
270
|
+
parts.push({ leaf, fillHole: isHole(leaf.fill), strokeHole: isHole(leaf.stroke) });
|
|
271
|
+
}
|
|
272
|
+
const keepsInk = parts.some((part) => (part.leaf.fill !== "none" && !part.fillHole) ||
|
|
273
|
+
(part.leaf.stroke !== "none" && !part.strokeHole));
|
|
274
|
+
return keepsInk
|
|
275
|
+
? parts
|
|
276
|
+
: parts.map((part) => ({ ...part, fillHole: false, strokeHole: false }));
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* The mark as a mask for its one-color version: white where the one color prints, black where a
|
|
280
|
+
* part is cut out. Undefined when nothing is cut out, so the mark can simply be recolored.
|
|
281
|
+
*/
|
|
282
|
+
export function knockoutMask(mark) {
|
|
283
|
+
const parts = oneColorParts(mark);
|
|
284
|
+
if (!parts.some((part) => part.fillHole || part.strokeHole))
|
|
285
|
+
return undefined;
|
|
286
|
+
const byElement = new Map(parts.map((part) => [part.leaf.element, part]));
|
|
287
|
+
const paint = (value, hole) => value === "none" ? "none" : hole ? "#000000" : "#ffffff";
|
|
288
|
+
const copy = (element) => {
|
|
289
|
+
const { fill: _fill, stroke: _stroke, ...attrs } = element.attrs;
|
|
290
|
+
const part = byElement.get(element);
|
|
291
|
+
return {
|
|
292
|
+
...element,
|
|
293
|
+
attrs: part
|
|
294
|
+
? {
|
|
295
|
+
...attrs,
|
|
296
|
+
fill: paint(part.leaf.fill, part.fillHole),
|
|
297
|
+
stroke: paint(part.leaf.stroke, part.strokeHole),
|
|
298
|
+
}
|
|
299
|
+
: attrs,
|
|
300
|
+
children: element.children.map(copy),
|
|
301
|
+
};
|
|
302
|
+
};
|
|
303
|
+
return { ...mark, root: copy(mark.root), colors: ["#000000", "#ffffff"] };
|
|
304
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marks as data. A mark master is SVG, but the scene engine never passes through SVG it did not
|
|
3
|
+
* write (docs/spikes/render.md, rule 1). So a mark is read into a small, fixed subset (shapes,
|
|
4
|
+
* groups, solid fills and strokes), then written back out by the kit, recolored as each version
|
|
5
|
+
* needs. Anything outside the subset is listed, so the studio can say what to fix.
|
|
6
|
+
*/
|
|
7
|
+
export type MarkElement = {
|
|
8
|
+
tag: "g" | "path" | "circle" | "ellipse" | "rect" | "line" | "polyline" | "polygon";
|
|
9
|
+
attrs: Record<string, string>;
|
|
10
|
+
children: MarkElement[];
|
|
11
|
+
};
|
|
12
|
+
export type ParsedMark = {
|
|
13
|
+
viewBox: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
w: number;
|
|
17
|
+
h: number;
|
|
18
|
+
};
|
|
19
|
+
/** The root's own paint (Lucide-style fill="none" stroke="currentColor"), kept as a group. */
|
|
20
|
+
root: MarkElement;
|
|
21
|
+
/** Every hex color used, lowercase. */
|
|
22
|
+
colors: string[];
|
|
23
|
+
/** The thinnest stroke, in viewBox units (Infinity when nothing is stroked). */
|
|
24
|
+
thinnestStroke: number;
|
|
25
|
+
/** What was left out, in plain words. Empty for a clean mark. */
|
|
26
|
+
unsupported: string[];
|
|
27
|
+
};
|
|
28
|
+
/** An affine transform as SVG's matrix(a b c d e f). */
|
|
29
|
+
export type Matrix = readonly [number, number, number, number, number, number];
|
|
30
|
+
export declare const IDENTITY: Matrix;
|
|
31
|
+
/** `matrix` followed by a transform attribute parseMark kept (so it is well formed). */
|
|
32
|
+
export declare function withTransform(matrix: Matrix, transform: string | undefined): Matrix;
|
|
33
|
+
/** A point through the matrix. */
|
|
34
|
+
export declare function apply([a, b, c, d, e, f]: Matrix, x: number, y: number): [number, number];
|
|
35
|
+
/** Read a mark master into the kit's subset. Throws only when there is no usable <svg> root. */
|
|
36
|
+
export declare function parseMark(svg: string): ParsedMark;
|
|
37
|
+
export type MarkPaint = {
|
|
38
|
+
/** Map each hex the mark uses to the hex to draw. Default: as it is. */
|
|
39
|
+
color?: (hex: string) => string;
|
|
40
|
+
/** What currentColor and an unset fill mean here. Default: #000000, as in SVG. */
|
|
41
|
+
current?: string;
|
|
42
|
+
};
|
|
43
|
+
/** Write the mark's elements back out (no <svg> wrapper), recolored. */
|
|
44
|
+
export declare function markBody(mark: ParsedMark, paint?: MarkPaint): string;
|
|
45
|
+
/** A standalone SVG of the mark (recolored), with its own viewBox. */
|
|
46
|
+
export declare function markSvg(mark: ParsedMark, paint?: MarkPaint): string;
|
|
47
|
+
/**
|
|
48
|
+
* A mark master as the kit would draw it, written by the kit: only the subset parseMark reads, so
|
|
49
|
+
* no script, event handler, link or foreign content survives. currentColor stays. Running it on
|
|
50
|
+
* its own output gives the same bytes, so saving a clean mark again changes nothing. Throws like
|
|
51
|
+
* parseMark when there is no usable <svg> root.
|
|
52
|
+
*/
|
|
53
|
+
export declare function cleanMarkSvg(svg: string): string;
|