@ideare/kit 0.0.0-stage → 0.1.0-next.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.
Files changed (83) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -2
  3. package/THIRD_PARTY.md +52 -0
  4. package/dist/brand/schema.d.ts +265 -0
  5. package/dist/brand/schema.js +271 -0
  6. package/dist/check/copy.d.ts +27 -0
  7. package/dist/check/copy.js +139 -0
  8. package/dist/check/index.d.ts +54 -0
  9. package/dist/check/index.js +492 -0
  10. package/dist/color/contrast.d.ts +41 -0
  11. package/dist/color/contrast.js +84 -0
  12. package/dist/color/palette.d.ts +27 -0
  13. package/dist/color/palette.js +114 -0
  14. package/dist/color/wcag.d.ts +22 -0
  15. package/dist/color/wcag.js +50 -0
  16. package/dist/draft/draft.d.ts +61 -0
  17. package/dist/draft/draft.js +383 -0
  18. package/dist/editor/history.d.ts +78 -0
  19. package/dist/editor/history.js +221 -0
  20. package/dist/editor/operations.d.ts +112 -0
  21. package/dist/editor/operations.js +621 -0
  22. package/dist/files/docs.d.ts +49 -0
  23. package/dist/files/docs.js +392 -0
  24. package/dist/files/generate.d.ts +47 -0
  25. package/dist/files/generate.js +254 -0
  26. package/dist/files/png.d.ts +40 -0
  27. package/dist/files/png.js +168 -0
  28. package/dist/files/raster.d.ts +14 -0
  29. package/dist/files/raster.js +0 -0
  30. package/dist/files/templates.d.ts +162 -0
  31. package/dist/files/templates.js +390 -0
  32. package/dist/files/tokens.d.ts +22 -0
  33. package/dist/files/tokens.js +204 -0
  34. package/dist/fonts/catalog.d.ts +3 -0
  35. package/dist/fonts/catalog.js +345 -0
  36. package/dist/fonts/ofl.d.ts +5 -0
  37. package/dist/fonts/ofl.js +5 -0
  38. package/dist/fonts/pick.d.ts +33 -0
  39. package/dist/fonts/pick.js +73 -0
  40. package/dist/imports/folder.d.ts +80 -0
  41. package/dist/imports/folder.js +124 -0
  42. package/dist/imports/unzip.d.ts +32 -0
  43. package/dist/imports/unzip.js +128 -0
  44. package/dist/index.d.ts +59 -0
  45. package/dist/index.js +34 -0
  46. package/dist/marks/draw.d.ts +48 -0
  47. package/dist/marks/draw.js +149 -0
  48. package/dist/marks/icons.data.d.ts +5 -0
  49. package/dist/marks/icons.data.js +600 -0
  50. package/dist/marks/svg.d.ts +46 -0
  51. package/dist/marks/svg.js +341 -0
  52. package/dist/node/index.d.ts +13 -0
  53. package/dist/node/index.js +41 -0
  54. package/dist/posts/pix.d.ts +19 -0
  55. package/dist/posts/pix.js +49 -0
  56. package/dist/posts/templates.d.ts +45 -0
  57. package/dist/posts/templates.js +868 -0
  58. package/dist/resvg/index.d.ts +2 -0
  59. package/dist/resvg/index.js +22 -0
  60. package/dist/scene/brand-art.d.ts +49 -0
  61. package/dist/scene/brand-art.js +154 -0
  62. package/dist/scene/frame.d.ts +89 -0
  63. package/dist/scene/frame.js +213 -0
  64. package/dist/scene/grain.d.ts +7 -0
  65. package/dist/scene/grain.js +7 -0
  66. package/dist/scene/qr.d.ts +10 -0
  67. package/dist/scene/qr.js +359 -0
  68. package/dist/scene/render.d.ts +76 -0
  69. package/dist/scene/render.js +557 -0
  70. package/dist/scene/schema.d.ts +271 -0
  71. package/dist/scene/schema.js +257 -0
  72. package/dist/sign.d.ts +47 -0
  73. package/dist/sign.js +88 -0
  74. package/dist/text/engine.d.ts +128 -0
  75. package/dist/text/engine.js +245 -0
  76. package/dist/text/fonts.d.ts +56 -0
  77. package/dist/text/fonts.js +65 -0
  78. package/dist/version.d.ts +2 -0
  79. package/dist/version.js +2 -0
  80. package/dist/wasm/index.d.ts +15 -0
  81. package/dist/wasm/index.js +46 -0
  82. package/package.json +78 -5
  83. package/schema/brand.v1.json +442 -0
@@ -0,0 +1,2 @@
1
+ import type { Rasterizer } from "../files/raster.ts";
2
+ export declare function resvgRasterizer(): Rasterizer;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The rasterizer for Node and Bun: @resvg/resvg-js 2.6.2 (resvg 0.34), with system fonts off.
3
+ * The kit only ever hands it glyph paths, so it never needs a font, and output does not depend
4
+ * on what is installed on the machine.
5
+ */
6
+ import { Resvg } from "@resvg/resvg-js";
7
+ export function resvgRasterizer() {
8
+ return async (svg, { width }) => {
9
+ const image = new Resvg(svg, {
10
+ fitTo: { mode: "width", value: Math.round(width) },
11
+ font: { loadSystemFonts: false },
12
+ }).render();
13
+ const pixels = image.pixels;
14
+ const png = image.asPng();
15
+ return {
16
+ png: new Uint8Array(png.buffer, png.byteOffset, png.byteLength),
17
+ pixels: new Uint8Array(pixels.buffer, pixels.byteOffset, pixels.byteLength),
18
+ width: image.width,
19
+ height: image.height,
20
+ };
21
+ };
22
+ }
@@ -0,0 +1,49 @@
1
+ import type { Brand } from "../brand/schema.ts";
2
+ import { type ParsedMark } from "../marks/svg.ts";
3
+ import type { LoadedFont, TextEngine } from "../text/engine.ts";
4
+ /** The versions an owner may bring as their own drawing (brand.mark.lockups). */
5
+ declare const LOCKUPS: readonly ["horizontal", "stacked", "wordmark"];
6
+ type Lockup = (typeof LOCKUPS)[number];
7
+ /**
8
+ * Everything the brand contributes to a drawing, prepared once per brand: the parsed marks, the
9
+ * fonts for both roles and the outlined wordmark. Lockups are composed from these, in natural
10
+ * units where the mark is 100 tall.
11
+ */
12
+ export type BrandArt = {
13
+ mark: ParsedMark;
14
+ small: ParsedMark;
15
+ dark: ParsedMark | undefined;
16
+ /** The owner's own lockups, when the brand has them. */
17
+ lockups: Partial<Record<Lockup, ParsedMark>>;
18
+ display: LoadedFont;
19
+ text: LoadedFont;
20
+ wordmark: {
21
+ d: string;
22
+ box: {
23
+ x: number;
24
+ y: number;
25
+ w: number;
26
+ h: number;
27
+ };
28
+ };
29
+ };
30
+ /** The font file a role draws with: the brand's family at the nearest weight it has. */
31
+ export declare function fontFor(brand: Brand, role: "display" | "text", weight?: number): {
32
+ id: string;
33
+ weight: number;
34
+ };
35
+ export declare function brandArt(brand: Brand, engine: TextEngine): Promise<BrandArt>;
36
+ export type LogoVariant = "mark" | "small" | "wordmark" | "horizontal" | "stacked";
37
+ export type LogoTone = "color" | "ink" | "white";
38
+ /** A drawn logo in its own units: `body` is placed at 0,0 and spans w by h. */
39
+ export type LogoFragment = {
40
+ body: string;
41
+ w: number;
42
+ h: number;
43
+ };
44
+ /**
45
+ * One logo version. `ground` is what it sits on (it picks the dark mark and the wordmark color
46
+ * for "color"); ink and white are one-color versions for stamps, embroidery and photos.
47
+ */
48
+ export declare function logoFragment(art: BrandArt, brand: Brand, variant: LogoVariant, tone: LogoTone, ground: string): LogoFragment;
49
+ export {};
@@ -0,0 +1,154 @@
1
+ import { contrast } from "../color/wcag.js";
2
+ import { isLight } from "../color/contrast.js";
3
+ import { markBody, parseMark } from "../marks/svg.js";
4
+ import { pathBounds } from "../marks/draw.js";
5
+ import { nearestWeight } from "../fonts/pick.js";
6
+ import { fmt } from "../text/engine.js";
7
+ /** The versions an owner may bring as their own drawing (brand.mark.lockups). */
8
+ const LOCKUPS = ["horizontal", "stacked", "wordmark"];
9
+ const cache = new WeakMap();
10
+ /** The font file a role draws with: the brand's family at the nearest weight it has. */
11
+ export function fontFor(brand, role, weight) {
12
+ const font = brand.fonts[role];
13
+ const wanted = weight ?? (role === "display" ? brand.wordmark.weight : 400);
14
+ return { id: font.id, weight: nearestWeight(font, wanted) };
15
+ }
16
+ export function brandArt(brand, engine) {
17
+ let perEngine = cache.get(brand);
18
+ if (!perEngine) {
19
+ perEngine = new WeakMap();
20
+ cache.set(brand, perEngine);
21
+ }
22
+ let pending = perEngine.get(engine);
23
+ if (!pending) {
24
+ pending = (async () => {
25
+ const [display, text, wordmarkFont] = await Promise.all([
26
+ engine.font(fontFor(brand, "display")),
27
+ engine.font(fontFor(brand, "text")),
28
+ engine.font(fontFor(brand, brand.wordmark.font, brand.wordmark.weight)),
29
+ ]);
30
+ const run = engine.shape(wordmarkFont, brand.wordmark.text, 100, brand.wordmark.tracking);
31
+ const d = engine.runPath(wordmarkFont, run, 100, 0, 0);
32
+ const mark = parseMark(brand.mark.svg);
33
+ const lockups = {};
34
+ for (const lockup of LOCKUPS) {
35
+ const svg = brand.mark.lockups?.[lockup];
36
+ if (svg)
37
+ lockups[lockup] = parseMark(svg);
38
+ }
39
+ return {
40
+ mark,
41
+ small: brand.mark.small ? parseMark(brand.mark.small) : mark,
42
+ dark: brand.mark.dark ? parseMark(brand.mark.dark) : undefined,
43
+ lockups,
44
+ display,
45
+ text,
46
+ wordmark: { d, box: pathBounds(d) },
47
+ };
48
+ })();
49
+ pending.catch(() => perEngine?.delete(engine));
50
+ perEngine.set(engine, pending);
51
+ }
52
+ return pending;
53
+ }
54
+ /**
55
+ * The mark as it should look on a ground. On a dark ground a mark without its own dark version
56
+ * swaps every color that would not reach 3:1 for the dark theme's ink (marks.md: a charcoal mark
57
+ * disappears on dark). A mark that already shows there keeps its colors: a white glyph on a
58
+ * brand-color badge, set on that same color, reads as the glyph alone, while swapping the badge
59
+ * for a light color would leave the glyph white on white.
60
+ */
61
+ function markOn(art, brand, ground, variant) {
62
+ const base = variant === "small" ? art.small : art.mark;
63
+ if (art.dark && variant === "mark" && !isLight(ground))
64
+ return { mark: art.dark, color: (hex) => hex };
65
+ return { mark: base, color: colorsOn(base, brand, ground) };
66
+ }
67
+ /** A drawing's colors on a ground: kept on a light one, and on a dark one when any of them
68
+ * shows; otherwise every color under 3:1 becomes the dark theme's ink. */
69
+ function colorsOn(mark, brand, ground) {
70
+ if (isLight(ground) || mark.colors.some((hex) => contrast(hex, ground) >= 3))
71
+ return (hex) => hex;
72
+ const light = brand.colors.dark.ink;
73
+ return (hex) => (contrast(hex, ground) < 3 ? light : hex);
74
+ }
75
+ /** A lockup's colors on a ground: kept on a light one; on a dark one, each color under 3:1
76
+ * becomes the dark theme's ink. Unlike a mark's inner details, a lockup's name sits on the
77
+ * ground itself, so a navy name turns light on black while an orange sun keeps its color. */
78
+ function lockupColorsOn(brand, ground) {
79
+ if (isLight(ground))
80
+ return (hex) => hex;
81
+ const light = brand.colors.dark.ink;
82
+ return (hex) => (contrast(hex, ground) < 3 ? light : hex);
83
+ }
84
+ function placeMark(mark, color, current, height, x, y) {
85
+ const { viewBox } = mark;
86
+ const scale = height / viewBox.h;
87
+ const body = markBody(mark, { color, current });
88
+ return {
89
+ body: `<g transform="translate(${fmt(x - viewBox.x * scale)} ${fmt(y - viewBox.y * scale)}) scale(${fmt(scale)})">${body}</g>`,
90
+ w: viewBox.w * scale,
91
+ };
92
+ }
93
+ function placeWordmark(art, fill, height, x, y) {
94
+ const { box, d } = art.wordmark;
95
+ const scale = box.h > 0 ? height / box.h : 1;
96
+ return {
97
+ body: `<path transform="translate(${fmt(x - box.x * scale)} ${fmt(y - box.y * scale)}) scale(${fmt(scale)})" d="${d}" fill="${fill}"/>`,
98
+ w: box.w * scale,
99
+ };
100
+ }
101
+ /**
102
+ * One logo version. `ground` is what it sits on (it picks the dark mark and the wordmark color
103
+ * for "color"); ink and white are one-color versions for stamps, embroidery and photos.
104
+ */
105
+ export function logoFragment(art, brand, variant, tone, ground) {
106
+ const palette = isLight(ground) ? brand.colors.light : brand.colors.dark;
107
+ const one = tone === "ink" ? brand.colors.light.ink : tone === "white" ? "#ffffff" : undefined;
108
+ const wordFill = one ?? palette.ink;
109
+ const mark = (which, height, x, y) => {
110
+ if (one) {
111
+ const base = which === "small" ? art.small : art.mark;
112
+ return placeMark(base, () => one, one, height, x, y);
113
+ }
114
+ const chosen = markOn(art, brand, ground, which);
115
+ return placeMark(chosen.mark, chosen.color, palette.ink, height, x, y);
116
+ };
117
+ const own = variant === "mark" || variant === "small" ? undefined : art.lockups[variant];
118
+ if (own) {
119
+ const placed = one
120
+ ? placeMark(own, () => one, one, 100, 0, 0)
121
+ : placeMark(own, lockupColorsOn(brand, ground), palette.ink, 100, 0, 0);
122
+ return { body: placed.body, w: placed.w, h: 100 };
123
+ }
124
+ switch (variant) {
125
+ case "mark":
126
+ case "small": {
127
+ const placed = mark(variant, 100, 0, 0);
128
+ return { body: placed.body, w: placed.w, h: 100 };
129
+ }
130
+ case "wordmark": {
131
+ const placed = placeWordmark(art, wordFill, 100, 0, 0);
132
+ return { body: placed.body, w: placed.w, h: 100 };
133
+ }
134
+ case "horizontal": {
135
+ const placedMark = mark("mark", 100, 0, 0);
136
+ const wordHeight = 44;
137
+ const placedWord = placeWordmark(art, wordFill, wordHeight, placedMark.w + 26, (100 - wordHeight) / 2);
138
+ return {
139
+ body: placedMark.body + placedWord.body,
140
+ w: placedMark.w + 26 + placedWord.w,
141
+ h: 100,
142
+ };
143
+ }
144
+ case "stacked": {
145
+ const wordHeight = 34;
146
+ const probe = placeWordmark(art, wordFill, wordHeight, 0, 0);
147
+ const markProbe = mark("mark", 100, 0, 0);
148
+ const w = Math.max(probe.w, markProbe.w);
149
+ const placedMark = mark("mark", 100, (w - markProbe.w) / 2, 0);
150
+ const placedWord = placeWordmark(art, wordFill, wordHeight, (w - probe.w) / 2, 100 + 18);
151
+ return { body: placedMark.body + placedWord.body, w, h: 100 + 18 + wordHeight };
152
+ }
153
+ }
154
+ }
@@ -0,0 +1,89 @@
1
+ import type { Frame } from "./schema.ts";
2
+ /**
3
+ * Frame math for the renderer and the editor (docs/spikes/editor.md, rule E1). A frame is
4
+ * `{x, y, w, h, rotate}`: `rotate` is in degrees, clockwise, about the frame's center, so turning
5
+ * a frame never moves its center. A group's layers sit in the group's own space, where (0, 0) is
6
+ * the group frame's top-left corner before it turns. Every length is in design units, the page's
7
+ * own px.
8
+ */
9
+ export type Point = {
10
+ x: number;
11
+ y: number;
12
+ };
13
+ export type Box = {
14
+ x: number;
15
+ y: number;
16
+ w: number;
17
+ h: number;
18
+ };
19
+ /** Corner and side handles, by compass point: nw is the top-left corner. */
20
+ export type Handle = "nw" | "n" | "ne" | "e" | "se" | "s" | "sw" | "w";
21
+ /** Lines a moving box can land on, as x positions and y positions. */
22
+ export type Guides = {
23
+ x: number[];
24
+ y: number[];
25
+ };
26
+ /** The smallest side a resize leaves, in design units. */
27
+ export declare const MIN_SIZE = 8;
28
+ /** How far a snap reaches, in screen px: 34 units on a phone at 0.35 scale (rule E5). */
29
+ export declare const SNAP_PX = 12;
30
+ export declare function rotatePoint(point: Point, degrees: number): Point;
31
+ /** Degrees in (-180, 180]. */
32
+ export declare function normalizeAngle(degrees: number): number;
33
+ /** A frame from its parts, with no `rotate` key when it does not turn. */
34
+ export declare function frameOf(x: number, y: number, w: number, h: number, rotate?: number): Frame;
35
+ export declare function centerOf(box: Box): Point;
36
+ /** The four corners, clockwise from the top left as drawn. */
37
+ export declare function cornersOf(frame: Frame): Point[];
38
+ /** The upright box a frame covers: what snapping, align and distribute use. */
39
+ export declare function boundsOf(frame: Frame): Box;
40
+ export declare function unionOf(boxes: Box[]): Box;
41
+ /** A frame in the page's space, from its own frame and its groups (outermost first). */
42
+ export declare function pageFrame(frame: Frame, groups: readonly Frame[]): Frame;
43
+ /** The inverse: a frame on the page, in the space of the innermost of `groups`. */
44
+ export declare function localFrame(frame: Frame, groups: readonly Frame[]): Frame;
45
+ /**
46
+ * A text box whose height changed (a line more as someone types) with its turned top-left
47
+ * corner held still (rule E2). Keeping x and y instead would move the pivot, and turned text
48
+ * would slide while someone types.
49
+ */
50
+ export declare function withHeight(frame: Frame, h: number): Frame;
51
+ export declare function isCorner(handle: Handle): boolean;
52
+ /** Where a handle sits on the page. */
53
+ export declare function handlePoint(frame: Frame, handle: Handle): Point;
54
+ /**
55
+ * The frame after a handle is pulled to `to`, with the point opposite the handle held still at
56
+ * any turn. A corner keeps the proportions by default (the pointer is projected on the diagonal),
57
+ * so the words in a text box keep their line breaks; a side changes one side only. Pass the
58
+ * handle's start point plus the pointer's travel as `to`, never the pointer itself, so nothing
59
+ * jumps by the finger's offset from the handle.
60
+ */
61
+ export declare function resizeFrame(start: Frame, handle: Handle, to: Point, options?: {
62
+ min?: number;
63
+ keepRatio?: boolean;
64
+ }): Frame;
65
+ /**
66
+ * The angle a turn handle gives, dragged about `pivot` from `from` to `to`, starting at `start`
67
+ * degrees. With no `step` it lands on a multiple of 45 within `snap` degrees (3 by default);
68
+ * with a `step` (15 for Shift on a desktop) it moves in steps.
69
+ */
70
+ export declare function turnAngle(start: number, pivot: Point, from: Point, to: Point, options?: {
71
+ step?: number;
72
+ snap?: number;
73
+ }): number;
74
+ /** The page's edges and middle, and the edges and middles of `boxes`. */
75
+ export declare function snapGuides(page: Box, boxes: Box[]): Guides;
76
+ /**
77
+ * The nudge that lands a moving box's nearest edge or middle on a guide, and the guide lines to
78
+ * draw. The reach is `px` screen pixels at the stage's `scale` (screen px per design unit), so it
79
+ * feels the same on a phone and on a desktop (rule E5).
80
+ */
81
+ export declare function snapBox(box: Box, guides: Guides, scale: number, px?: number): {
82
+ dx: number;
83
+ dy: number;
84
+ guides: Guides;
85
+ };
86
+ /** The SVG transform that places a frame: its position and turn live here only (rule E6). */
87
+ export declare function frameTransform(frame: Frame): string;
88
+ /** Two decimals, the precision every SVG number is written at. */
89
+ export declare function round2(value: number): number;
@@ -0,0 +1,213 @@
1
+ import { fmt } from "../text/engine.js";
2
+ /** The smallest side a resize leaves, in design units. */
3
+ export const MIN_SIZE = 8;
4
+ /** How far a snap reaches, in screen px: 34 units on a phone at 0.35 scale (rule E5). */
5
+ export const SNAP_PX = 12;
6
+ const radians = (degrees) => (degrees * Math.PI) / 180;
7
+ const add = (a, b) => ({ x: a.x + b.x, y: a.y + b.y });
8
+ const sub = (a, b) => ({ x: a.x - b.x, y: a.y - b.y });
9
+ export function rotatePoint(point, degrees) {
10
+ if (!degrees)
11
+ return point;
12
+ const cos = Math.cos(radians(degrees));
13
+ const sin = Math.sin(radians(degrees));
14
+ return { x: point.x * cos - point.y * sin, y: point.x * sin + point.y * cos };
15
+ }
16
+ /** Degrees in (-180, 180]. */
17
+ export function normalizeAngle(degrees) {
18
+ const turned = ((((degrees + 180) % 360) + 360) % 360) - 180;
19
+ if (turned === -180)
20
+ return 180;
21
+ // -0 becomes 0, so a frame that turned back to upright compares equal to one that never did.
22
+ return turned === 0 ? 0 : turned;
23
+ }
24
+ /** A frame from its parts, with no `rotate` key when it does not turn. */
25
+ export function frameOf(x, y, w, h, rotate = 0) {
26
+ const r = normalizeAngle(rotate);
27
+ return r ? { x, y, w, h, rotate: r } : { x, y, w, h };
28
+ }
29
+ export function centerOf(box) {
30
+ return { x: box.x + box.w / 2, y: box.y + box.h / 2 };
31
+ }
32
+ /** A frame of this size and turn whose center is `center`. */
33
+ function centeredAt(center, w, h, rotate = 0) {
34
+ return frameOf(center.x - w / 2, center.y - h / 2, w, h, rotate);
35
+ }
36
+ /** The four corners, clockwise from the top left as drawn. */
37
+ export function cornersOf(frame) {
38
+ const center = centerOf(frame);
39
+ const { w, h } = frame;
40
+ return [
41
+ { x: -w / 2, y: -h / 2 },
42
+ { x: w / 2, y: -h / 2 },
43
+ { x: w / 2, y: h / 2 },
44
+ { x: -w / 2, y: h / 2 },
45
+ ].map((corner) => add(center, rotatePoint(corner, frame.rotate ?? 0)));
46
+ }
47
+ /** The upright box a frame covers: what snapping, align and distribute use. */
48
+ export function boundsOf(frame) {
49
+ if (!frame.rotate)
50
+ return { x: frame.x, y: frame.y, w: frame.w, h: frame.h };
51
+ const corners = cornersOf(frame);
52
+ const xs = corners.map((corner) => corner.x);
53
+ const ys = corners.map((corner) => corner.y);
54
+ const x = Math.min(...xs);
55
+ const y = Math.min(...ys);
56
+ return { x, y, w: Math.max(...xs) - x, h: Math.max(...ys) - y };
57
+ }
58
+ export function unionOf(boxes) {
59
+ const x = Math.min(...boxes.map((box) => box.x));
60
+ const y = Math.min(...boxes.map((box) => box.y));
61
+ const right = Math.max(...boxes.map((box) => box.x + box.w));
62
+ const bottom = Math.max(...boxes.map((box) => box.y + box.h));
63
+ return { x, y, w: right - x, h: bottom - y };
64
+ }
65
+ /** A frame inside `group` (in the group's space), as it sits in the group's parent. */
66
+ function toParentSpace(group, child) {
67
+ const offset = {
68
+ x: child.x + child.w / 2 - group.w / 2,
69
+ y: child.y + child.h / 2 - group.h / 2,
70
+ };
71
+ const center = add(centerOf(group), rotatePoint(offset, group.rotate ?? 0));
72
+ return centeredAt(center, child.w, child.h, (group.rotate ?? 0) + (child.rotate ?? 0));
73
+ }
74
+ /** The inverse: a frame in the group's parent, in the group's own space. */
75
+ function toGroupSpace(group, frame) {
76
+ const offset = rotatePoint(sub(centerOf(frame), centerOf(group)), -(group.rotate ?? 0));
77
+ const center = { x: offset.x + group.w / 2, y: offset.y + group.h / 2 };
78
+ return centeredAt(center, frame.w, frame.h, (frame.rotate ?? 0) - (group.rotate ?? 0));
79
+ }
80
+ /** A frame in the page's space, from its own frame and its groups (outermost first). */
81
+ export function pageFrame(frame, groups) {
82
+ return groups.reduceRight((inner, group) => toParentSpace(group, inner), frame);
83
+ }
84
+ /** The inverse: a frame on the page, in the space of the innermost of `groups`. */
85
+ export function localFrame(frame, groups) {
86
+ return groups.reduce((outer, group) => toGroupSpace(group, outer), frame);
87
+ }
88
+ /**
89
+ * A text box whose height changed (a line more as someone types) with its turned top-left
90
+ * corner held still (rule E2). Keeping x and y instead would move the pivot, and turned text
91
+ * would slide while someone types.
92
+ */
93
+ export function withHeight(frame, h) {
94
+ if (!frame.rotate || h === frame.h)
95
+ return { ...frame, h };
96
+ const topLeft = cornersOf(frame)[0] ?? { x: frame.x, y: frame.y };
97
+ const center = add(topLeft, rotatePoint({ x: frame.w / 2, y: h / 2 }, frame.rotate));
98
+ return centeredAt(center, frame.w, h, frame.rotate);
99
+ }
100
+ const HANDLE_SIGN = {
101
+ nw: [-1, -1],
102
+ n: [0, -1],
103
+ ne: [1, -1],
104
+ e: [1, 0],
105
+ se: [1, 1],
106
+ s: [0, 1],
107
+ sw: [-1, 1],
108
+ w: [-1, 0],
109
+ };
110
+ export function isCorner(handle) {
111
+ return handle.length === 2;
112
+ }
113
+ /** Where a handle sits on the page. */
114
+ export function handlePoint(frame, handle) {
115
+ const [sx, sy] = HANDLE_SIGN[handle];
116
+ return add(centerOf(frame), rotatePoint({ x: (sx * frame.w) / 2, y: (sy * frame.h) / 2 }, frame.rotate ?? 0));
117
+ }
118
+ /**
119
+ * The frame after a handle is pulled to `to`, with the point opposite the handle held still at
120
+ * any turn. A corner keeps the proportions by default (the pointer is projected on the diagonal),
121
+ * so the words in a text box keep their line breaks; a side changes one side only. Pass the
122
+ * handle's start point plus the pointer's travel as `to`, never the pointer itself, so nothing
123
+ * jumps by the finger's offset from the handle.
124
+ */
125
+ export function resizeFrame(start, handle, to, options = {}) {
126
+ const min = options.min ?? MIN_SIZE;
127
+ const [sx, sy] = HANDLE_SIGN[handle];
128
+ const turn = start.rotate ?? 0;
129
+ // The point opposite the handle: a corner for a corner, the middle of a side for a side.
130
+ const anchor = add(centerOf(start), rotatePoint({ x: (-sx * start.w) / 2, y: (-sy * start.h) / 2 }, turn));
131
+ const pulled = rotatePoint(sub(to, anchor), -turn);
132
+ let w = start.w;
133
+ let h = start.h;
134
+ if (sy === 0)
135
+ w = Math.max(min, sx * pulled.x);
136
+ else if (sx === 0)
137
+ h = Math.max(min, sy * pulled.y);
138
+ else if (options.keepRatio === false) {
139
+ w = Math.max(min, sx * pulled.x);
140
+ h = Math.max(min, sy * pulled.y);
141
+ }
142
+ else {
143
+ const diagonal = { x: sx * start.w, y: sy * start.h };
144
+ const k = Math.max(min / Math.max(Math.min(start.w, start.h), Number.EPSILON), (pulled.x * diagonal.x + pulled.y * diagonal.y) /
145
+ Math.max(diagonal.x * diagonal.x + diagonal.y * diagonal.y, Number.EPSILON));
146
+ w = start.w * k;
147
+ h = start.h * k;
148
+ }
149
+ const center = add(anchor, rotatePoint({ x: (sx * w) / 2, y: (sy * h) / 2 }, turn));
150
+ return centeredAt(center, w, h, turn);
151
+ }
152
+ /**
153
+ * The angle a turn handle gives, dragged about `pivot` from `from` to `to`, starting at `start`
154
+ * degrees. With no `step` it lands on a multiple of 45 within `snap` degrees (3 by default);
155
+ * with a `step` (15 for Shift on a desktop) it moves in steps.
156
+ */
157
+ export function turnAngle(start, pivot, from, to, options = {}) {
158
+ const angle = (point) => (Math.atan2(point.y - pivot.y, point.x - pivot.x) * 180) / Math.PI;
159
+ let turned = start + angle(to) - angle(from);
160
+ if (options.step)
161
+ turned = Math.round(turned / options.step) * options.step;
162
+ else {
163
+ const nearest = Math.round(turned / 45) * 45;
164
+ if (Math.abs(turned - nearest) <= (options.snap ?? 3))
165
+ turned = nearest;
166
+ }
167
+ return round2(normalizeAngle(turned));
168
+ }
169
+ /** The page's edges and middle, and the edges and middles of `boxes`. */
170
+ export function snapGuides(page, boxes) {
171
+ const along = (start, length) => [start, start + length / 2, start + length];
172
+ return {
173
+ x: [page, ...boxes].flatMap((box) => along(box.x, box.w)),
174
+ y: [page, ...boxes].flatMap((box) => along(box.y, box.h)),
175
+ };
176
+ }
177
+ function nearest(points, targets, reach) {
178
+ let best;
179
+ for (const point of points)
180
+ for (const at of targets) {
181
+ const delta = at - point;
182
+ if (Math.abs(delta) <= reach && (!best || Math.abs(delta) < Math.abs(best.delta)))
183
+ best = { delta, at };
184
+ }
185
+ return best;
186
+ }
187
+ /**
188
+ * The nudge that lands a moving box's nearest edge or middle on a guide, and the guide lines to
189
+ * draw. The reach is `px` screen pixels at the stage's `scale` (screen px per design unit), so it
190
+ * feels the same on a phone and on a desktop (rule E5).
191
+ */
192
+ export function snapBox(box, guides, scale, px = SNAP_PX) {
193
+ const reach = px / scale;
194
+ const x = nearest([box.x, box.x + box.w / 2, box.x + box.w], guides.x, reach);
195
+ const y = nearest([box.y, box.y + box.h / 2, box.y + box.h], guides.y, reach);
196
+ return {
197
+ dx: x?.delta ?? 0,
198
+ dy: y?.delta ?? 0,
199
+ guides: { x: x ? [x.at] : [], y: y ? [y.at] : [] },
200
+ };
201
+ }
202
+ /** The SVG transform that places a frame: its position and turn live here only (rule E6). */
203
+ export function frameTransform(frame) {
204
+ const at = `translate(${fmt(frame.x)} ${fmt(frame.y)})`;
205
+ return frame.rotate
206
+ ? `${at} rotate(${fmt(frame.rotate)} ${fmt(frame.w / 2)} ${fmt(frame.h / 2)})`
207
+ : at;
208
+ }
209
+ /** Two decimals, the precision every SVG number is written at. */
210
+ export function round2(value) {
211
+ const rounded = Math.round(value * 100) / 100;
212
+ return Object.is(rounded, -0) ? 0 : rounded;
213
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A seeded 128 px noise tile (RGBA PNG, 26247 bytes) from the S1b render spike. Pages tile it with
3
+ * a userSpaceOnUse <pattern> for grain: 0% difference between resvg and Chrome, where
4
+ * feTurbulence differed on 10.8% of pixels (docs/spikes/render.md, rule 8).
5
+ */
6
+ export declare const GRAIN_TILE_SIZE = 128;
7
+ export declare const GRAIN_TILE_PNG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAgAElEQVR4nE3dCdim5fzG8bsQM6hkC/nHWFJxo0xUShnLZELWUvasCZVQMVT2XSjGviWZJoQhS7ZEhhbbhEQ0hbIXivI/Ptcx347e43jnfeZ57vu6fst5nr/fdT/3Mq1YsWI+5JBD5p///OfzypUr53vc4x7za1/72vkhD3nI/KEPfWj+1a9+NR922GHzbrvtNl944YXzaaedNj/iEY+Yt9tuu/nII4+cP/3pT8/vf//75w022GBs97Of/Wy+6U1vOr/73e+eb3SjG80PetCD5k033XS+5z3vOX/ve9+bX/e61803vOEN57vf/e7z2rVr55e//OXzWWedNT/qUY+aX/SiF80LFy4c+7/1rW+d3/Oe98wPeMAD5uOPP35evHjx/PCHP3z+6le/Ov/yl7+cjz766Pk+97nPvHTp0nnZsmXzzW9+8/mTn/zkfPLJJ8+f/exn5z/96U/zrW51q/lvf/vb/IIXvGD+61//Ot/pTneat9hii/n617/+/MQnPnF+2cteNmxft27dfItb3GL+4he/OP/5z3+eH/rQh86f//znZz+vec1r5h/96EfzlltuOV9zzTXz29/+9vmPf/zj/MIXvnB+17veNT/+8Y8fdj760Y+ev/zlLw9b+ME3dlx99dXz7373u2HDm970pvH5bW972/mUU06Z99prrxGTSy65ZN58883nww8/fMx1xzvecf73v/89cvGFL3xhbLvjjjvOz33uc+dtttlmPuGEE8a8YvLhD394/u1vfzs/5jGPmb///e/Pz3jGM+ZFixbN559//vzkJz953nDDDUfMPvrRj86//vWv59///vdjv/e+973z8uXL50libnKTmwyHTfawhz1sJN8EEnW/+91vOPGOd7xj/u9//zsMkLy3ve1tw5lnPvOZIxjPe97zBhie9axnjX0vuuiiYdQrXvGKEWTOH3fccfP973//8f6ee+45ggZ05th2221HwATlDne4w/h74xvfeH7nO985DJaspz3tafPq1avnP/zhD/PZZ589H3TQQfNWW201QAq0wGdeCbjLXe4yf/CDHxzgsv/+++8/bPjPf/4zHAeitWvXDkBvvPHG82abbTZsOOqoo+ZVq1bN3/rWt4bf55xz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";