@weasel-js/cursor 1.5.0 → 1.5.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/dist/index.d.ts +15 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -131,6 +131,21 @@ declare const GLYPHS: {
|
|
|
131
131
|
readonly width: 0.9;
|
|
132
132
|
}];
|
|
133
133
|
};
|
|
134
|
+
readonly penPlus: {
|
|
135
|
+
readonly box: 24;
|
|
136
|
+
readonly hotspot: readonly [3, 17];
|
|
137
|
+
readonly paths: readonly [{
|
|
138
|
+
readonly role: "ink";
|
|
139
|
+
readonly d: "M 3 17 L 6.5 7.5 L 11 3 L 16 8 L 11.5 12.5 Z";
|
|
140
|
+
}, {
|
|
141
|
+
readonly role: "detail";
|
|
142
|
+
readonly d: "M 6.4 11.9 L 10.2 8.1";
|
|
143
|
+
readonly width: 0.9;
|
|
144
|
+
}, {
|
|
145
|
+
readonly role: "ink";
|
|
146
|
+
readonly d: "M 16.1 13.5 L 18.9 13.5 L 18.9 16.1 L 21.5 16.1 L 21.5 18.9 L 18.9 18.9 L 18.9 21.5 L 16.1 21.5 L 16.1 18.9 L 13.5 18.9 L 13.5 16.1 L 16.1 16.1 Z";
|
|
147
|
+
}];
|
|
148
|
+
};
|
|
134
149
|
readonly eyedropper: {
|
|
135
150
|
readonly box: 24;
|
|
136
151
|
readonly hotspot: readonly [5, 19];
|
package/dist/index.js
CHANGED
|
@@ -188,6 +188,28 @@ var GLYPHS = {
|
|
|
188
188
|
}
|
|
189
189
|
]
|
|
190
190
|
},
|
|
191
|
+
"penPlus": {
|
|
192
|
+
"box": 24,
|
|
193
|
+
"hotspot": [
|
|
194
|
+
3,
|
|
195
|
+
17
|
|
196
|
+
],
|
|
197
|
+
"paths": [
|
|
198
|
+
{
|
|
199
|
+
"role": "ink",
|
|
200
|
+
"d": "M 3 17 L 6.5 7.5 L 11 3 L 16 8 L 11.5 12.5 Z"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"role": "detail",
|
|
204
|
+
"d": "M 6.4 11.9 L 10.2 8.1",
|
|
205
|
+
"width": 0.9
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"role": "ink",
|
|
209
|
+
"d": "M 16.1 13.5 L 18.9 13.5 L 18.9 16.1 L 21.5 16.1 L 21.5 18.9 L 18.9 18.9 L 18.9 21.5 L 16.1 21.5 L 16.1 18.9 L 13.5 18.9 L 13.5 16.1 L 16.1 16.1 Z"
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
},
|
|
191
213
|
"eyedropper": {
|
|
192
214
|
"box": 24,
|
|
193
215
|
"hotspot": [
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts","../src/paint.ts","../src/bake.ts","../src/glyphs.ts","../src/registry.ts","../src/resolve.ts","../src/paintedCursorState.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * A cursor glyph: SVG path `d` strings tagged with a paint role, plus the\n * hotspot in glyph units. The same record feeds both the data-URI baker and\n * the Path2D painter, so `d` is the one geometry form neither has to translate.\n */\nexport interface CursorGlyph {\n /** Side of the square viewBox the paths are authored in. */\n readonly box: number;\n /** Hotspot in glyph units, scaled to integer CSS px at bake time. */\n readonly hotspot: readonly [number, number];\n /**\n * The glyph-unit radius a `worldRadius` spec means, for glyphs that measure\n * something — a brush ring reads as a lie about the brush size unless the\n * painter knows which circle in the drawing is the one being sized. Defaults\n * to the box's inscribed radius.\n */\n readonly radius?: number;\n readonly paths: readonly CursorPath[];\n}\n\nexport type CursorPath =\n /** A filled part of the silhouette. */\n | { readonly role: 'ink'; readonly d: string }\n /** An unfilled part of the silhouette — a handle, a wire, an arc. */\n | { readonly role: 'stroke'; readonly d: string; readonly width: number }\n /** A division inside the silhouette, drawn in the halo color. */\n | { readonly role: 'detail'; readonly d: string; readonly width: number }\n /** A literal color, for glyphs that carry a swatch. */\n | { readonly role: 'accent'; readonly d: string; readonly fill: string };\n\n/**\n * Ink and halo are constants of the register rather than parameters: a\n * self-contrasting glyph reads on white paper, dark chrome and mid-tone\n * artwork alike precisely because it does not track the theme.\n */\nexport const CURSOR_INK = '#141418';\nexport const CURSOR_HALO = '#ffffff';\nexport const CURSOR_HALO_WIDTH = 2.6;\n\n/**\n * Rotation steps a baked angle is quantized to. Below 16 the cursor visibly\n * snaps against a smoothly rotating selection; above it the cache grows for no\n * perceptible gain.\n */\nexport const CURSOR_ANGLE_STEPS = 16;\n\n/**\n * Chrome silently drops a cursor image above this size and falls back to the\n * keyword after the comma, with no error anywhere. Measured on Chrome 152 /\n * macOS 26.5; see the spec's \"Measured browser behavior\".\n */\nexport const CURSOR_MAX_CSS_PX = 128;\n\n/**\n * Visits the points that bound an authored `d` string, for the fit guards below.\n *\n * Command-aware on purpose. Scraping every number out of the string instead\n * reads an arc's radii and its three flags as coordinates, which reports a\n * bogus `0` for every `A` ever written.\n *\n * An arc is bounded by the circle it lies on: its centre is recovered from the\n * endpoints and radius, then the whole circle is admitted. Expanding the arc's\n * *endpoint* by the radii instead over-reports whenever an endpoint sits at an\n * extreme of the circle, which is exactly where the half-circle idiom these\n * glyphs use puts it.\n *\n * The authored dialect is absolute `M`/`L`/`A`/`Z` with circular arcs. A\n * relative command would be measured as if absolute and silently under-report,\n * so it throws.\n */\nfunction walk(d: string, see: (x: number, y: number) => void): void {\n let cx = 0;\n let cy = 0;\n for (const [, cmd, args] of d.matchAll(/([A-Za-z])([^A-Za-z]*)/g)) {\n if (cmd !== cmd.toUpperCase()) {\n throw new Error(`cursor glyph path uses a relative command '${cmd}': ${d}`);\n }\n const nums = (args.match(/-?\\d+(?:\\.\\d+)?/g) ?? []).map(Number);\n if (cmd === 'Z') continue;\n if (cmd === 'A') {\n for (let i = 0; i + 7 <= nums.length; i += 7) {\n const [rx, ry, , largeArc, sweep, x, y] = nums.slice(i, i + 7);\n const r = Math.max(rx, ry);\n const dx = x - cx;\n const dy = y - cy;\n const half = Math.hypot(dx, dy) / 2;\n // Centre sits off the chord midpoint by this much, perpendicular to it.\n const off = Math.sqrt(Math.max(0, r * r - half * half));\n const sign = largeArc === sweep ? -1 : 1;\n const ux = half > 0 ? -dy / (half * 2) : 0;\n const uy = half > 0 ? dx / (half * 2) : 0;\n const ox = (cx + x) / 2 + sign * off * ux;\n const oy = (cy + y) / 2 + sign * off * uy;\n see(ox - r, oy - r);\n see(ox + r, oy - r);\n see(ox - r, oy + r);\n see(ox + r, oy + r);\n cx = x;\n cy = y;\n }\n continue;\n }\n // M and L: plain coordinate pairs.\n for (let i = 0; i + 2 <= nums.length; i += 2) {\n cx = nums[i];\n cy = nums[i + 1];\n see(cx, cy);\n }\n }\n}\n\n/**\n * True when every authored path sits at least half a halo stroke inside the\n * viewBox. A clipped halo is invisible at proof size and flattens the glyph's\n * outline at cursor size, so it is worth failing loudly at authoring time\n * rather than discovering it on a dark background.\n */\nexport function haloFitsInBox(glyph: CursorGlyph): boolean {\n const margin = CURSOR_HALO_WIDTH / 2;\n let ok = true;\n for (const p of glyph.paths) {\n walk(p.d, (x, y) => {\n for (const v of [x, y]) {\n if (v < margin || v > glyph.box - margin) ok = false;\n }\n });\n }\n return ok;\n}\n\n/**\n * True when the glyph still fits its viewBox at every rotation — i.e. every\n * authored point is inside the box's inscribed circle, halo included.\n *\n * Bake rotates about the box centre in a viewBox that does not grow, so a\n * glyph that merely satisfies {@link haloFitsInBox} can have its corners\n * sheared off partway round. The loss is a few pixels at cursor size and\n * invisible until someone looks at exactly the wrong angle, which is why it\n * is worth failing at authoring time.\n */\nexport function rotationFitsInBox(glyph: CursorGlyph): boolean {\n const c = glyph.box / 2;\n const limit = c - CURSOR_HALO_WIDTH / 2;\n let ok = true;\n for (const p of glyph.paths) {\n walk(p.d, (x, y) => {\n if (Math.hypot(x - c, y - c) > limit) ok = false;\n });\n }\n return ok;\n}\n","import { CURSOR_HALO, CURSOR_HALO_WIDTH, CURSOR_INK } from './types';\nimport type { CursorGlyph, CursorPath } from './types';\n\n/**\n * One path of a glyph, with the paint the register gives it.\n *\n * Deliberately not a renderer's own type: `@weasel-js/cursor` decides *what*\n * to paint and in what order, and leaves *how* to whoever is drawing — the\n * baker turns each op into an SVG element, the painted-tier layer turns it\n * into a draw command. Both get the same list, which is what makes the two\n * tiers show the same glyph rather than two drawings that resemble each other.\n *\n * Stroked ops are round-capped and round-joined; nothing in the register\n * wants a mitre.\n */\nexport interface CursorPaintOp {\n readonly d: string;\n readonly fill?: string;\n readonly stroke?: { readonly color: string; readonly width: number };\n}\n\nexport interface PaintOptions {\n /**\n * Multiplies every stroke width, leaving geometry alone. Default 1.\n *\n * A world-sized glyph passes the reciprocal of its scale here so line weight\n * stays chrome-weight in CSS px: a brush ring at a 400px radius otherwise\n * carries a 40px-thick stroke and a halo to match.\n */\n readonly lineWidthScale?: number;\n}\n\n/**\n * The halo pass — every silhouette member drawn once, wide, in halo colour\n * before any ink lands.\n *\n * One pass under everything rather than a per-path `paint-order`: with each\n * path haloing itself, a later path's halo cuts a white trench through an\n * earlier path's fill wherever the two overlap.\n */\nfunction halo(p: CursorPath, k: number): CursorPaintOp | null {\n switch (p.role) {\n case 'ink':\n case 'accent':\n return { d: p.d, fill: CURSOR_HALO, stroke: { color: CURSOR_HALO, width: CURSOR_HALO_WIDTH * k } };\n case 'stroke':\n return { d: p.d, stroke: { color: CURSOR_HALO, width: (p.width + CURSOR_HALO_WIDTH) * k } };\n // A detail IS halo-coloured and sits on top of the ink; it has no halo.\n case 'detail':\n return null;\n }\n}\n\nfunction ink(p: CursorPath, k: number): CursorPaintOp {\n switch (p.role) {\n case 'ink':\n return { d: p.d, fill: CURSOR_INK };\n case 'stroke':\n return { d: p.d, stroke: { color: CURSOR_INK, width: p.width * k } };\n case 'detail':\n return { d: p.d, stroke: { color: CURSOR_HALO, width: p.width * k } };\n case 'accent':\n return { d: p.d, fill: p.fill };\n }\n}\n\n/** Every paint a glyph needs, in draw order: all halos, then all ink. */\nexport function cursorPaintOps(glyph: CursorGlyph, opts: PaintOptions = {}): CursorPaintOp[] {\n const k = opts.lineWidthScale ?? 1;\n const out: CursorPaintOp[] = [];\n for (const p of glyph.paths) {\n const h = halo(p, k);\n if (h) out.push(h);\n }\n for (const p of glyph.paths) out.push(ink(p, k));\n return out;\n}\n\n/** An affine `[a, b, c, d, e, f]`: `x' = ax + cy + e`, `y' = bx + dy + f`. */\nexport type CursorMatrix = readonly [number, number, number, number, number, number];\n\nexport interface PaintPlacement {\n /** Rendered size in CSS px, as `bakeCursor` means it. */\n readonly size: number;\n /** Clockwise rotation in radians. Default 0. */\n readonly angle?: number;\n /** Where the hotspot goes — the pointer, in the layer's own coords. */\n readonly at: { readonly x: number; readonly y: number };\n}\n\n/**\n * Where a glyph's own units land, to place it under the pointer at a size and\n * an angle.\n *\n * Rotation is about the hotspot, not the box centre. That is the same\n * placement the baker produces — it turns the glyph about the centre and then\n * carries the hotspot around with it, and the centre cancels — but it is worth\n * stating, because a painter that rotates about the centre and forgets to move\n * the hotspot swings the glyph around a point the user never sees.\n *\n * Unquantized on purpose: the 22.5° step exists to bound the bake cache, and\n * the painter has no cache to bound. A painted cursor turns smoothly.\n */\nexport function cursorPaintMatrix(glyph: CursorGlyph, placement: PaintPlacement): CursorMatrix {\n const s = placement.size / glyph.box;\n const theta = placement.angle ?? 0;\n // y is down, so a positive angle carries +x toward +y — clockwise on screen.\n const a = s * Math.cos(theta);\n const b = s * Math.sin(theta);\n const [hx, hy] = glyph.hotspot;\n return [a, b, -b, a, placement.at.x - (a * hx - b * hy), placement.at.y - (b * hx + a * hy)];\n}\n\n/**\n * The CSS-px size a world-sized glyph is drawn at, so the circle its `radius`\n * names measures `worldRadius` world units on screen.\n *\n * A glyph with no `radius` is measured by its inscribed circle. Getting this\n * wrong scales every brush cursor by a constant factor, which reads as a\n * slightly-off cursor rather than as a bug.\n */\nexport function cursorWorldSize(\n glyph: CursorGlyph,\n worldRadius: number,\n scale: number,\n): number {\n const r = glyph.radius ?? glyph.box / 2;\n return (worldRadius * scale * glyph.box) / r;\n}\n\n/**\n * The `lineWidthScale` that keeps a glyph's line weight at chrome weight while\n * its geometry scales — what a world-sized glyph wants, and what a fixed-size\n * one must not have (there, weight scaling is what makes the painted tier\n * match the baked one).\n */\nexport function chromeLineWidthScale(glyph: CursorGlyph, size: number): number {\n return glyph.box / size;\n}\n","import { cursorPaintOps } from './paint';\nimport type { CursorPaintOp } from './paint';\nimport { CURSOR_ANGLE_STEPS, CURSOR_MAX_CSS_PX } from './types';\nimport type { CursorGlyph } from './types';\n\nexport interface BakeOptions {\n /** Rendered size in CSS px. Default 24. */\n readonly size?: number;\n /** Clockwise rotation in radians about the box centre, quantized to\n * {@link CURSOR_ANGLE_STEPS} steps. Default 0. */\n readonly angle?: number;\n /** Keyword drawn if the browser rejects the image. Default 'default'. */\n readonly fallback?: string;\n}\n\n/**\n * Index of the quantization step an angle falls in, in `[0, CURSOR_ANGLE_STEPS)`.\n *\n * Exported because the bake cache keys on this rather than on the raw angle:\n * the hover pump feeds a continuously varying selection rotation, and two\n * angles in the same step must be one cache entry, not two.\n */\nexport function quantizeCursorAngle(angle: number): number {\n const i = Math.round((angle / (Math.PI * 2)) * CURSOR_ANGLE_STEPS);\n return ((i % CURSOR_ANGLE_STEPS) + CURSOR_ANGLE_STEPS) % CURSOR_ANGLE_STEPS;\n}\n\n/** One paint op as an SVG element. */\nfunction renderOp(op: CursorPaintOp): string {\n const fill = op.fill === undefined ? 'none' : op.fill;\n const stroke = op.stroke\n ? ` stroke=\"${op.stroke.color}\" stroke-width=\"${op.stroke.width}\"` +\n ` stroke-linecap=\"round\" stroke-linejoin=\"round\"`\n : '';\n return `<path d=\"${op.d}\" fill=\"${fill}\"${stroke}/>`;\n}\n\n/**\n * Render a glyph to a CSS cursor value.\n *\n * Ships SVG rather than a bitmap because Chrome rasterizes an SVG data-URI\n * cursor at device scale — it is already crisp on a retina display, so there\n * is no PNG pipeline and no `image-set()` here. See the spec.\n */\nexport function bakeCursor(glyph: CursorGlyph, opts: BakeOptions = {}): string {\n const size = opts.size ?? 24;\n if (size > CURSOR_MAX_CSS_PX) {\n throw new RangeError(\n `cursor size ${size} exceeds the ${CURSOR_MAX_CSS_PX}px cap: the browser ` +\n `would drop the image and silently fall back. Use the painted tier.`,\n );\n }\n // Halos first, then ink; within each pass, source order is z-order. The\n // pass structure is `paint.ts`'s, so the painted tier draws the same glyph\n // rather than a second drawing that resembles it.\n const paths = cursorPaintOps(glyph).map(renderOp).join('');\n\n const c = glyph.box / 2;\n const step = quantizeCursorAngle(opts.angle ?? 0);\n const theta = (step / CURSOR_ANGLE_STEPS) * Math.PI * 2;\n // A whole-glyph transform rather than rotated path data: the same record\n // has to reach `paint.ts` unrotated, and a `d` string is not re-authorable.\n const body = step === 0\n ? paths\n : `<g transform=\"rotate(${(step * 360) / CURSOR_ANGLE_STEPS} ${c} ${c})\">${paths}</g>`;\n const svg =\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size}\" height=\"${size}\"` +\n ` viewBox=\"0 0 ${glyph.box} ${glyph.box}\">${body}</svg>`;\n // The hotspot travels with the glyph. Left behind, a rotated arrow would\n // point from its tail.\n const dx = glyph.hotspot[0] - c;\n const dy = glyph.hotspot[1] - c;\n const cos = Math.cos(theta);\n const sin = Math.sin(theta);\n const hx = Math.round(((c + cos * dx - sin * dy) / glyph.box) * size);\n const hy = Math.round(((c + sin * dx + cos * dy) / glyph.box) * size);\n const uri = `data:image/svg+xml,${encodeURIComponent(svg)}`;\n return `url(\"${uri}\") ${hx} ${hy}, ${opts.fallback ?? 'default'}`;\n}\n","// GENERATED by scripts/gen-cursors.mjs — do not edit by hand.\n// Filled silhouettes on a 24-unit box; the halo is applied at bake time.\n\nimport type { CursorGlyph } from './types';\n\nexport const GLYPHS = {\n \"pencil\": {\n \"box\": 24,\n \"hotspot\": [\n 5,\n 19\n ],\n \"paths\": [\n {\n \"role\": \"ink\",\n \"d\": \"M 5 19 L 7.5 16.5 L 16 8 L 19 11 L 10.5 19.5 L 8 19 Z\"\n },\n {\n \"role\": \"detail\",\n \"d\": \"M 14 6 L 19 11\",\n \"width\": 1.2\n }\n ]\n },\n \"pen\": {\n \"box\": 24,\n \"hotspot\": [\n 5,\n 19\n ],\n \"paths\": [\n {\n \"role\": \"ink\",\n \"d\": \"M 5 19 L 8.5 9.5 L 13 5 L 18 10 L 13.5 14.5 Z\"\n },\n {\n \"role\": \"detail\",\n \"d\": \"M 8.4 13.9 L 12.2 10.1\",\n \"width\": 0.9\n }\n ]\n },\n \"eyedropper\": {\n \"box\": 24,\n \"hotspot\": [\n 5,\n 19\n ],\n \"paths\": [\n {\n \"role\": \"ink\",\n \"d\": \"M 5 19 L 6.8 14.8 L 14.4 7.2 L 16.8 9.6 L 9.2 17.2 Z\"\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 18.2 2.6 A 3.2 3.2 0 1 0 18.2 9 A 3.2 3.2 0 1 0 18.2 2.6 Z\"\n }\n ]\n },\n \"brush\": {\n \"box\": 24,\n \"hotspot\": [\n 12,\n 12\n ],\n \"radius\": 9.9,\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 12 2.1 A 9.9 9.9 0 1 0 12 21.9 A 9.9 9.9 0 1 0 12 2.1 Z\",\n \"width\": 1.6\n }\n ]\n },\n \"crosshairRect\": {\n \"box\": 24,\n \"hotspot\": [\n 9,\n 9\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 1.6 9 L 6.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 11.6 9 L 16.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 1.6 L 9 6.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 11.6 L 9 16.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 14.2 14.2 L 21 14.2 L 21 21 L 14.2 21 Z\"\n }\n ]\n },\n \"crosshairEllipse\": {\n \"box\": 24,\n \"hotspot\": [\n 9,\n 9\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 1.6 9 L 6.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 11.6 9 L 16.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 1.6 L 9 6.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 11.6 L 9 16.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 17.6 13.9 A 3.7 3.7 0 1 0 17.6 21.3 A 3.7 3.7 0 1 0 17.6 13.9 Z\"\n }\n ]\n },\n \"crosshairLine\": {\n \"box\": 24,\n \"hotspot\": [\n 9,\n 9\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 1.6 9 L 6.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 11.6 9 L 16.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 1.6 L 9 6.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 11.6 L 9 16.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 14.2 21 L 21 14.2\",\n \"width\": 2.2\n }\n ]\n },\n \"crosshairStar\": {\n \"box\": 24,\n \"hotspot\": [\n 9,\n 9\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 1.6 9 L 6.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 11.6 9 L 16.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 1.6 L 9 6.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 11.6 L 9 16.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 17.6 13.4 L 18.72 16.06 L 21.59 16.3 L 19.41 18.19 L 20.07 21 L 17.6 19.5 L 15.13 21 L 15.79 18.19 L 13.61 16.3 L 16.48 16.06 Z\"\n }\n ]\n },\n \"crosshairPolygon\": {\n \"box\": 24,\n \"hotspot\": [\n 9,\n 9\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 1.6 9 L 6.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 11.6 9 L 16.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 1.6 L 9 6.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 11.6 L 9 16.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 17.6 13.5 L 21.5 16.33 L 20.01 20.92 L 15.19 20.92 L 13.7 16.33 Z\"\n }\n ]\n },\n \"resize\": {\n \"box\": 24,\n \"hotspot\": [\n 12,\n 12\n ],\n \"paths\": [\n {\n \"role\": \"ink\",\n \"d\": \"M 2.5 12 L 7 8.8 L 7 10.7 L 17 10.7 L 17 8.8 L 21.5 12 L 17 15.2 L 17 13.3 L 7 13.3 L 7 15.2 Z\"\n }\n ]\n },\n \"rotate\": {\n \"box\": 24,\n \"hotspot\": [\n 12,\n 12\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 5.8 9.74 A 6.6 6.6 0 0 1 18.2 9.74\",\n \"width\": 2.8\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 4.22 14.07 L 2.98 8.72 L 8.62 10.77 Z\"\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 19.78 14.07 L 15.38 10.77 L 21.02 8.72 Z\"\n }\n ]\n }\n} as const satisfies Record<string, CursorGlyph>;\n\n/** Every glyph name in the set. */\nexport type CursorGlyphName = keyof typeof GLYPHS;\n","import { bakeCursor, quantizeCursorAngle } from './bake';\nimport type { BakeOptions } from './bake';\nimport { GLYPHS } from './glyphs';\nimport type { CursorGlyphName } from './glyphs';\nimport type { CursorGlyph } from './types';\n\nconst cache = new Map<string, string>();\n\n/**\n * The baked cursor string for a named glyph, memoized.\n *\n * The key space is bounded by the glyph set times 16 angles times the handful\n * of sizes and fallbacks in use, so the cache needs no eviction.\n */\nexport function cursorFor(name: CursorGlyphName, opts: BakeOptions = {}): string {\n const size = opts.size ?? 24;\n const fallback = opts.fallback ?? 'default';\n // The step index, not the raw angle: the hover pump feeds a continuously\n // varying selection rotation, so a raw-angle key would never hit.\n const step = quantizeCursorAngle(opts.angle ?? 0);\n const key = `${name}|${size}|${step}|${fallback}`;\n const hit = cache.get(key);\n if (hit !== undefined) return hit;\n\n // Widened deliberately: the key type says this is always defined, but the\n // guard is what makes a bad name from untyped JS throw instead of baking\n // `undefined` into a cursor string that silently does nothing.\n const glyph = (GLYPHS as Record<string, CursorGlyph | undefined>)[name];\n if (glyph === undefined) {\n throw new Error(`unknown cursor glyph: ${String(name)}`);\n }\n const css = bakeCursor(glyph, { size, fallback, angle: opts.angle ?? 0 });\n cache.set(key, css);\n return css;\n}\n","import { cursorFor } from './registry';\nimport type { CursorGlyphName } from './glyphs';\nimport { CURSOR_MAX_CSS_PX } from './types';\n\n/**\n * What a tool, action or affordance declares as its cursor.\n *\n * A bare string is a CSS cursor value and passes through untouched, so every\n * declaration written before this type existed keeps working.\n */\nexport type CursorSpec = string | CursorGlyphSpec;\n\n/** The glyph form of a {@link CursorSpec}. */\nexport interface CursorGlyphSpec {\n readonly glyph: CursorGlyphName;\n /** Rendered size in CSS px. Default 24. */\n readonly size?: number;\n /**\n * Size in world units instead of `size`, so the glyph tracks zoom — a brush\n * radius ring. Forces the painted tier at every zoom level, because a baked\n * image would have to be rebuilt on every wheel tick.\n */\n readonly worldRadius?: number;\n /** Clockwise rotation in radians, quantized to 22.5° at bake. */\n readonly angle?: number;\n /** Keyword drawn if the browser rejects the image. Default 'default'. */\n readonly fallback?: string;\n}\n\n/**\n * Which tier a spec reaches the screen through.\n *\n * Tools never choose this — they declare what they want and the resolver\n * picks, so a brush cursor stays a brush cursor across the radius where it\n * stops being expressible as a CSS cursor at all.\n */\nexport type ResolvedCursor =\n | { readonly kind: 'css'; readonly css: string }\n | {\n readonly kind: 'painted';\n readonly glyph: CursorGlyphName;\n readonly angle: number;\n /** CSS px, when the spec fixed one. */\n readonly size?: number;\n /** World units; the painter scales this by the live view. */\n readonly worldRadius?: number;\n };\n\n/**\n * Resolve a spec to the tier that can actually draw it.\n *\n * Escalates to painted when the glyph is sized in world units, or when its\n * fixed size is past {@link CURSOR_MAX_CSS_PX} — above which the browser drops\n * the image and silently falls back to the keyword.\n */\nexport function resolveCursorTier(spec: CursorSpec): ResolvedCursor {\n if (typeof spec === 'string') return { kind: 'css', css: spec };\n const { glyph, size, worldRadius, angle = 0, fallback } = spec;\n if (worldRadius !== undefined) {\n return { kind: 'painted', glyph, angle, worldRadius };\n }\n if (size !== undefined && size > CURSOR_MAX_CSS_PX) {\n return { kind: 'painted', glyph, angle, size };\n }\n return {\n kind: 'css',\n css: cursorFor(glyph, {\n ...(size !== undefined ? { size } : {}),\n ...(fallback !== undefined ? { fallback } : {}),\n angle,\n }),\n };\n}\n\n/**\n * A {@link CursorSpec} as a CSS cursor value.\n *\n * Every consumer of a widened cursor field calls this, so a declaration never\n * has to know which form it holds. A painted cursor answers `'none'` — the\n * native cursor gets out of the way and the painted-tier layer draws it. A\n * caller that writes that string without the layer installed leaves the user\n * with no cursor, so reach for {@link resolveCursorTier} if you need to know.\n */\nexport function resolveCursor(spec: CursorSpec | undefined): string | undefined {\n if (spec === undefined) return undefined;\n const r = resolveCursorTier(spec);\n return r.kind === 'css' ? r.css : 'none';\n}\n","/**\n * The painted cursor a surface is currently showing, shared between whoever\n * decides it and the layer that draws it.\n *\n * Two slots rather than one, mirroring the precedence the cursor pipeline\n * already has: `<Canvas>` writes the active tool's cursor to `base` from\n * render, and the hover pump writes an affordance's or an action's cursor to\n * `override` imperatively. The override wins while it is set, and clearing it\n * restores the base without a React commit — the same reason the pump uses an\n * inline style for the CSS tier.\n */\n\nimport type { ResolvedCursor } from './resolve';\n\n/** The painted arm of `ResolvedCursor` — what this module stores. */\nexport type PaintedCursor = Extract<ResolvedCursor, { kind: 'painted' }>;\n\nexport interface PaintedCursorFrame {\n readonly cursor: PaintedCursor;\n /** Pointer position in the layer's own space: canvas-local CSS px. */\n readonly at: { readonly x: number; readonly y: number };\n}\n\nexport interface PaintedCursorState {\n setBase(cursor: PaintedCursor | null): void;\n setOverride(cursor: PaintedCursor | null): void;\n /** Canvas-local CSS px. */\n setPointer(x: number, y: number): void;\n /** Pointer left the surface: nothing to draw until it returns. */\n clearPointer(): void;\n /**\n * Whether a painted cursor is set at all, regardless of where the pointer is.\n *\n * Lets the pointer path skip measuring the canvas rect on every move in the\n * overwhelmingly common case where the compositor is drawing the cursor and\n * nothing here needs its position.\n */\n active(): boolean;\n /** What the layer should draw this frame, or null for none. */\n current(): PaintedCursorFrame | null;\n /** Run `fn` whenever the answer to `current()` may have changed. */\n subscribe(fn: () => void): () => void;\n}\n\n/**\n * Structural, not `Object.is`. Both writers rebuild their cursor object from a\n * spec every time they run — the pump on every pointermove, `<Canvas>` on\n * every render — so identity comparison would report a change constantly and\n * drive a repaint per pointer event.\n */\nfunction same(a: PaintedCursor | null, b: PaintedCursor | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n return (\n a.glyph === b.glyph &&\n a.angle === b.angle &&\n a.size === b.size &&\n a.worldRadius === b.worldRadius\n );\n}\n\nexport function createPaintedCursorState(): PaintedCursorState {\n let base: PaintedCursor | null = null;\n let override: PaintedCursor | null = null;\n let at: { x: number; y: number } | null = null;\n const subs = new Set<() => void>();\n\n const notify = (): void => {\n for (const fn of subs) fn();\n };\n\n return {\n setBase(cursor) {\n if (same(base, cursor)) return;\n base = cursor;\n notify();\n },\n setOverride(cursor) {\n if (same(override, cursor)) return;\n override = cursor;\n notify();\n },\n setPointer(x, y) {\n // A moved pointer only matters while something is being painted; a\n // notify per pointermove otherwise would request a redraw per event\n // for a cursor the compositor is drawing itself.\n if (at && at.x === x && at.y === y) return;\n at = { x, y };\n if (override ?? base) notify();\n },\n clearPointer() {\n if (at === null) return;\n at = null;\n notify();\n },\n active() {\n return (override ?? base) !== null;\n },\n current() {\n const cursor = override ?? base;\n if (!cursor || !at) return null;\n return { cursor, at };\n },\n subscribe(fn) {\n subs.add(fn);\n return () => { subs.delete(fn); };\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/paint.ts","../src/bake.ts","../src/glyphs.ts","../src/registry.ts","../src/resolve.ts","../src/paintedCursorState.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * A cursor glyph: SVG path `d` strings tagged with a paint role, plus the\n * hotspot in glyph units. The same record feeds both the data-URI baker and\n * the Path2D painter, so `d` is the one geometry form neither has to translate.\n */\nexport interface CursorGlyph {\n /** Side of the square viewBox the paths are authored in. */\n readonly box: number;\n /** Hotspot in glyph units, scaled to integer CSS px at bake time. */\n readonly hotspot: readonly [number, number];\n /**\n * The glyph-unit radius a `worldRadius` spec means, for glyphs that measure\n * something — a brush ring reads as a lie about the brush size unless the\n * painter knows which circle in the drawing is the one being sized. Defaults\n * to the box's inscribed radius.\n */\n readonly radius?: number;\n readonly paths: readonly CursorPath[];\n}\n\nexport type CursorPath =\n /** A filled part of the silhouette. */\n | { readonly role: 'ink'; readonly d: string }\n /** An unfilled part of the silhouette — a handle, a wire, an arc. */\n | { readonly role: 'stroke'; readonly d: string; readonly width: number }\n /** A division inside the silhouette, drawn in the halo color. */\n | { readonly role: 'detail'; readonly d: string; readonly width: number }\n /** A literal color, for glyphs that carry a swatch. */\n | { readonly role: 'accent'; readonly d: string; readonly fill: string };\n\n/**\n * Ink and halo are constants of the register rather than parameters: a\n * self-contrasting glyph reads on white paper, dark chrome and mid-tone\n * artwork alike precisely because it does not track the theme.\n */\nexport const CURSOR_INK = '#141418';\nexport const CURSOR_HALO = '#ffffff';\nexport const CURSOR_HALO_WIDTH = 2.6;\n\n/**\n * Rotation steps a baked angle is quantized to. Below 16 the cursor visibly\n * snaps against a smoothly rotating selection; above it the cache grows for no\n * perceptible gain.\n */\nexport const CURSOR_ANGLE_STEPS = 16;\n\n/**\n * Chrome silently drops a cursor image above this size and falls back to the\n * keyword after the comma, with no error anywhere. Measured on Chrome 152 /\n * macOS 26.5; see the spec's \"Measured browser behavior\".\n */\nexport const CURSOR_MAX_CSS_PX = 128;\n\n/**\n * Visits the points that bound an authored `d` string, for the fit guards below.\n *\n * Command-aware on purpose. Scraping every number out of the string instead\n * reads an arc's radii and its three flags as coordinates, which reports a\n * bogus `0` for every `A` ever written.\n *\n * An arc is bounded by the circle it lies on: its centre is recovered from the\n * endpoints and radius, then the whole circle is admitted. Expanding the arc's\n * *endpoint* by the radii instead over-reports whenever an endpoint sits at an\n * extreme of the circle, which is exactly where the half-circle idiom these\n * glyphs use puts it.\n *\n * The authored dialect is absolute `M`/`L`/`A`/`Z` with circular arcs. A\n * relative command would be measured as if absolute and silently under-report,\n * so it throws.\n */\nfunction walk(d: string, see: (x: number, y: number) => void): void {\n let cx = 0;\n let cy = 0;\n for (const [, cmd, args] of d.matchAll(/([A-Za-z])([^A-Za-z]*)/g)) {\n if (cmd !== cmd.toUpperCase()) {\n throw new Error(`cursor glyph path uses a relative command '${cmd}': ${d}`);\n }\n const nums = (args.match(/-?\\d+(?:\\.\\d+)?/g) ?? []).map(Number);\n if (cmd === 'Z') continue;\n if (cmd === 'A') {\n for (let i = 0; i + 7 <= nums.length; i += 7) {\n const [rx, ry, , largeArc, sweep, x, y] = nums.slice(i, i + 7);\n const r = Math.max(rx, ry);\n const dx = x - cx;\n const dy = y - cy;\n const half = Math.hypot(dx, dy) / 2;\n // Centre sits off the chord midpoint by this much, perpendicular to it.\n const off = Math.sqrt(Math.max(0, r * r - half * half));\n const sign = largeArc === sweep ? -1 : 1;\n const ux = half > 0 ? -dy / (half * 2) : 0;\n const uy = half > 0 ? dx / (half * 2) : 0;\n const ox = (cx + x) / 2 + sign * off * ux;\n const oy = (cy + y) / 2 + sign * off * uy;\n see(ox - r, oy - r);\n see(ox + r, oy - r);\n see(ox - r, oy + r);\n see(ox + r, oy + r);\n cx = x;\n cy = y;\n }\n continue;\n }\n // M and L: plain coordinate pairs.\n for (let i = 0; i + 2 <= nums.length; i += 2) {\n cx = nums[i];\n cy = nums[i + 1];\n see(cx, cy);\n }\n }\n}\n\n/**\n * True when every authored path sits at least half a halo stroke inside the\n * viewBox. A clipped halo is invisible at proof size and flattens the glyph's\n * outline at cursor size, so it is worth failing loudly at authoring time\n * rather than discovering it on a dark background.\n */\nexport function haloFitsInBox(glyph: CursorGlyph): boolean {\n const margin = CURSOR_HALO_WIDTH / 2;\n let ok = true;\n for (const p of glyph.paths) {\n walk(p.d, (x, y) => {\n for (const v of [x, y]) {\n if (v < margin || v > glyph.box - margin) ok = false;\n }\n });\n }\n return ok;\n}\n\n/**\n * True when the glyph still fits its viewBox at every rotation — i.e. every\n * authored point is inside the box's inscribed circle, halo included.\n *\n * Bake rotates about the box centre in a viewBox that does not grow, so a\n * glyph that merely satisfies {@link haloFitsInBox} can have its corners\n * sheared off partway round. The loss is a few pixels at cursor size and\n * invisible until someone looks at exactly the wrong angle, which is why it\n * is worth failing at authoring time.\n */\nexport function rotationFitsInBox(glyph: CursorGlyph): boolean {\n const c = glyph.box / 2;\n const limit = c - CURSOR_HALO_WIDTH / 2;\n let ok = true;\n for (const p of glyph.paths) {\n walk(p.d, (x, y) => {\n if (Math.hypot(x - c, y - c) > limit) ok = false;\n });\n }\n return ok;\n}\n","import { CURSOR_HALO, CURSOR_HALO_WIDTH, CURSOR_INK } from './types';\nimport type { CursorGlyph, CursorPath } from './types';\n\n/**\n * One path of a glyph, with the paint the register gives it.\n *\n * Deliberately not a renderer's own type: `@weasel-js/cursor` decides *what*\n * to paint and in what order, and leaves *how* to whoever is drawing — the\n * baker turns each op into an SVG element, the painted-tier layer turns it\n * into a draw command. Both get the same list, which is what makes the two\n * tiers show the same glyph rather than two drawings that resemble each other.\n *\n * Stroked ops are round-capped and round-joined; nothing in the register\n * wants a mitre.\n */\nexport interface CursorPaintOp {\n readonly d: string;\n readonly fill?: string;\n readonly stroke?: { readonly color: string; readonly width: number };\n}\n\nexport interface PaintOptions {\n /**\n * Multiplies every stroke width, leaving geometry alone. Default 1.\n *\n * A world-sized glyph passes the reciprocal of its scale here so line weight\n * stays chrome-weight in CSS px: a brush ring at a 400px radius otherwise\n * carries a 40px-thick stroke and a halo to match.\n */\n readonly lineWidthScale?: number;\n}\n\n/**\n * The halo pass — every silhouette member drawn once, wide, in halo colour\n * before any ink lands.\n *\n * One pass under everything rather than a per-path `paint-order`: with each\n * path haloing itself, a later path's halo cuts a white trench through an\n * earlier path's fill wherever the two overlap.\n */\nfunction halo(p: CursorPath, k: number): CursorPaintOp | null {\n switch (p.role) {\n case 'ink':\n case 'accent':\n return { d: p.d, fill: CURSOR_HALO, stroke: { color: CURSOR_HALO, width: CURSOR_HALO_WIDTH * k } };\n case 'stroke':\n return { d: p.d, stroke: { color: CURSOR_HALO, width: (p.width + CURSOR_HALO_WIDTH) * k } };\n // A detail IS halo-coloured and sits on top of the ink; it has no halo.\n case 'detail':\n return null;\n }\n}\n\nfunction ink(p: CursorPath, k: number): CursorPaintOp {\n switch (p.role) {\n case 'ink':\n return { d: p.d, fill: CURSOR_INK };\n case 'stroke':\n return { d: p.d, stroke: { color: CURSOR_INK, width: p.width * k } };\n case 'detail':\n return { d: p.d, stroke: { color: CURSOR_HALO, width: p.width * k } };\n case 'accent':\n return { d: p.d, fill: p.fill };\n }\n}\n\n/** Every paint a glyph needs, in draw order: all halos, then all ink. */\nexport function cursorPaintOps(glyph: CursorGlyph, opts: PaintOptions = {}): CursorPaintOp[] {\n const k = opts.lineWidthScale ?? 1;\n const out: CursorPaintOp[] = [];\n for (const p of glyph.paths) {\n const h = halo(p, k);\n if (h) out.push(h);\n }\n for (const p of glyph.paths) out.push(ink(p, k));\n return out;\n}\n\n/** An affine `[a, b, c, d, e, f]`: `x' = ax + cy + e`, `y' = bx + dy + f`. */\nexport type CursorMatrix = readonly [number, number, number, number, number, number];\n\nexport interface PaintPlacement {\n /** Rendered size in CSS px, as `bakeCursor` means it. */\n readonly size: number;\n /** Clockwise rotation in radians. Default 0. */\n readonly angle?: number;\n /** Where the hotspot goes — the pointer, in the layer's own coords. */\n readonly at: { readonly x: number; readonly y: number };\n}\n\n/**\n * Where a glyph's own units land, to place it under the pointer at a size and\n * an angle.\n *\n * Rotation is about the hotspot, not the box centre. That is the same\n * placement the baker produces — it turns the glyph about the centre and then\n * carries the hotspot around with it, and the centre cancels — but it is worth\n * stating, because a painter that rotates about the centre and forgets to move\n * the hotspot swings the glyph around a point the user never sees.\n *\n * Unquantized on purpose: the 22.5° step exists to bound the bake cache, and\n * the painter has no cache to bound. A painted cursor turns smoothly.\n */\nexport function cursorPaintMatrix(glyph: CursorGlyph, placement: PaintPlacement): CursorMatrix {\n const s = placement.size / glyph.box;\n const theta = placement.angle ?? 0;\n // y is down, so a positive angle carries +x toward +y — clockwise on screen.\n const a = s * Math.cos(theta);\n const b = s * Math.sin(theta);\n const [hx, hy] = glyph.hotspot;\n return [a, b, -b, a, placement.at.x - (a * hx - b * hy), placement.at.y - (b * hx + a * hy)];\n}\n\n/**\n * The CSS-px size a world-sized glyph is drawn at, so the circle its `radius`\n * names measures `worldRadius` world units on screen.\n *\n * A glyph with no `radius` is measured by its inscribed circle. Getting this\n * wrong scales every brush cursor by a constant factor, which reads as a\n * slightly-off cursor rather than as a bug.\n */\nexport function cursorWorldSize(\n glyph: CursorGlyph,\n worldRadius: number,\n scale: number,\n): number {\n const r = glyph.radius ?? glyph.box / 2;\n return (worldRadius * scale * glyph.box) / r;\n}\n\n/**\n * The `lineWidthScale` that keeps a glyph's line weight at chrome weight while\n * its geometry scales — what a world-sized glyph wants, and what a fixed-size\n * one must not have (there, weight scaling is what makes the painted tier\n * match the baked one).\n */\nexport function chromeLineWidthScale(glyph: CursorGlyph, size: number): number {\n return glyph.box / size;\n}\n","import { cursorPaintOps } from './paint';\nimport type { CursorPaintOp } from './paint';\nimport { CURSOR_ANGLE_STEPS, CURSOR_MAX_CSS_PX } from './types';\nimport type { CursorGlyph } from './types';\n\nexport interface BakeOptions {\n /** Rendered size in CSS px. Default 24. */\n readonly size?: number;\n /** Clockwise rotation in radians about the box centre, quantized to\n * {@link CURSOR_ANGLE_STEPS} steps. Default 0. */\n readonly angle?: number;\n /** Keyword drawn if the browser rejects the image. Default 'default'. */\n readonly fallback?: string;\n}\n\n/**\n * Index of the quantization step an angle falls in, in `[0, CURSOR_ANGLE_STEPS)`.\n *\n * Exported because the bake cache keys on this rather than on the raw angle:\n * the hover pump feeds a continuously varying selection rotation, and two\n * angles in the same step must be one cache entry, not two.\n */\nexport function quantizeCursorAngle(angle: number): number {\n const i = Math.round((angle / (Math.PI * 2)) * CURSOR_ANGLE_STEPS);\n return ((i % CURSOR_ANGLE_STEPS) + CURSOR_ANGLE_STEPS) % CURSOR_ANGLE_STEPS;\n}\n\n/** One paint op as an SVG element. */\nfunction renderOp(op: CursorPaintOp): string {\n const fill = op.fill === undefined ? 'none' : op.fill;\n const stroke = op.stroke\n ? ` stroke=\"${op.stroke.color}\" stroke-width=\"${op.stroke.width}\"` +\n ` stroke-linecap=\"round\" stroke-linejoin=\"round\"`\n : '';\n return `<path d=\"${op.d}\" fill=\"${fill}\"${stroke}/>`;\n}\n\n/**\n * Render a glyph to a CSS cursor value.\n *\n * Ships SVG rather than a bitmap because Chrome rasterizes an SVG data-URI\n * cursor at device scale — it is already crisp on a retina display, so there\n * is no PNG pipeline and no `image-set()` here. See the spec.\n */\nexport function bakeCursor(glyph: CursorGlyph, opts: BakeOptions = {}): string {\n const size = opts.size ?? 24;\n if (size > CURSOR_MAX_CSS_PX) {\n throw new RangeError(\n `cursor size ${size} exceeds the ${CURSOR_MAX_CSS_PX}px cap: the browser ` +\n `would drop the image and silently fall back. Use the painted tier.`,\n );\n }\n // Halos first, then ink; within each pass, source order is z-order. The\n // pass structure is `paint.ts`'s, so the painted tier draws the same glyph\n // rather than a second drawing that resembles it.\n const paths = cursorPaintOps(glyph).map(renderOp).join('');\n\n const c = glyph.box / 2;\n const step = quantizeCursorAngle(opts.angle ?? 0);\n const theta = (step / CURSOR_ANGLE_STEPS) * Math.PI * 2;\n // A whole-glyph transform rather than rotated path data: the same record\n // has to reach `paint.ts` unrotated, and a `d` string is not re-authorable.\n const body = step === 0\n ? paths\n : `<g transform=\"rotate(${(step * 360) / CURSOR_ANGLE_STEPS} ${c} ${c})\">${paths}</g>`;\n const svg =\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"${size}\" height=\"${size}\"` +\n ` viewBox=\"0 0 ${glyph.box} ${glyph.box}\">${body}</svg>`;\n // The hotspot travels with the glyph. Left behind, a rotated arrow would\n // point from its tail.\n const dx = glyph.hotspot[0] - c;\n const dy = glyph.hotspot[1] - c;\n const cos = Math.cos(theta);\n const sin = Math.sin(theta);\n const hx = Math.round(((c + cos * dx - sin * dy) / glyph.box) * size);\n const hy = Math.round(((c + sin * dx + cos * dy) / glyph.box) * size);\n const uri = `data:image/svg+xml,${encodeURIComponent(svg)}`;\n return `url(\"${uri}\") ${hx} ${hy}, ${opts.fallback ?? 'default'}`;\n}\n","// GENERATED by scripts/gen-cursors.mjs — do not edit by hand.\n// Filled silhouettes on a 24-unit box; the halo is applied at bake time.\n\nimport type { CursorGlyph } from './types';\n\nexport const GLYPHS = {\n \"pencil\": {\n \"box\": 24,\n \"hotspot\": [\n 5,\n 19\n ],\n \"paths\": [\n {\n \"role\": \"ink\",\n \"d\": \"M 5 19 L 7.5 16.5 L 16 8 L 19 11 L 10.5 19.5 L 8 19 Z\"\n },\n {\n \"role\": \"detail\",\n \"d\": \"M 14 6 L 19 11\",\n \"width\": 1.2\n }\n ]\n },\n \"pen\": {\n \"box\": 24,\n \"hotspot\": [\n 5,\n 19\n ],\n \"paths\": [\n {\n \"role\": \"ink\",\n \"d\": \"M 5 19 L 8.5 9.5 L 13 5 L 18 10 L 13.5 14.5 Z\"\n },\n {\n \"role\": \"detail\",\n \"d\": \"M 8.4 13.9 L 12.2 10.1\",\n \"width\": 0.9\n }\n ]\n },\n \"penPlus\": {\n \"box\": 24,\n \"hotspot\": [\n 3,\n 17\n ],\n \"paths\": [\n {\n \"role\": \"ink\",\n \"d\": \"M 3 17 L 6.5 7.5 L 11 3 L 16 8 L 11.5 12.5 Z\"\n },\n {\n \"role\": \"detail\",\n \"d\": \"M 6.4 11.9 L 10.2 8.1\",\n \"width\": 0.9\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 16.1 13.5 L 18.9 13.5 L 18.9 16.1 L 21.5 16.1 L 21.5 18.9 L 18.9 18.9 L 18.9 21.5 L 16.1 21.5 L 16.1 18.9 L 13.5 18.9 L 13.5 16.1 L 16.1 16.1 Z\"\n }\n ]\n },\n \"eyedropper\": {\n \"box\": 24,\n \"hotspot\": [\n 5,\n 19\n ],\n \"paths\": [\n {\n \"role\": \"ink\",\n \"d\": \"M 5 19 L 6.8 14.8 L 14.4 7.2 L 16.8 9.6 L 9.2 17.2 Z\"\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 18.2 2.6 A 3.2 3.2 0 1 0 18.2 9 A 3.2 3.2 0 1 0 18.2 2.6 Z\"\n }\n ]\n },\n \"brush\": {\n \"box\": 24,\n \"hotspot\": [\n 12,\n 12\n ],\n \"radius\": 9.9,\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 12 2.1 A 9.9 9.9 0 1 0 12 21.9 A 9.9 9.9 0 1 0 12 2.1 Z\",\n \"width\": 1.6\n }\n ]\n },\n \"crosshairRect\": {\n \"box\": 24,\n \"hotspot\": [\n 9,\n 9\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 1.6 9 L 6.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 11.6 9 L 16.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 1.6 L 9 6.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 11.6 L 9 16.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 14.2 14.2 L 21 14.2 L 21 21 L 14.2 21 Z\"\n }\n ]\n },\n \"crosshairEllipse\": {\n \"box\": 24,\n \"hotspot\": [\n 9,\n 9\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 1.6 9 L 6.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 11.6 9 L 16.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 1.6 L 9 6.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 11.6 L 9 16.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 17.6 13.9 A 3.7 3.7 0 1 0 17.6 21.3 A 3.7 3.7 0 1 0 17.6 13.9 Z\"\n }\n ]\n },\n \"crosshairLine\": {\n \"box\": 24,\n \"hotspot\": [\n 9,\n 9\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 1.6 9 L 6.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 11.6 9 L 16.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 1.6 L 9 6.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 11.6 L 9 16.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 14.2 21 L 21 14.2\",\n \"width\": 2.2\n }\n ]\n },\n \"crosshairStar\": {\n \"box\": 24,\n \"hotspot\": [\n 9,\n 9\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 1.6 9 L 6.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 11.6 9 L 16.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 1.6 L 9 6.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 11.6 L 9 16.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 17.6 13.4 L 18.72 16.06 L 21.59 16.3 L 19.41 18.19 L 20.07 21 L 17.6 19.5 L 15.13 21 L 15.79 18.19 L 13.61 16.3 L 16.48 16.06 Z\"\n }\n ]\n },\n \"crosshairPolygon\": {\n \"box\": 24,\n \"hotspot\": [\n 9,\n 9\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 1.6 9 L 6.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 11.6 9 L 16.4 9\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 1.6 L 9 6.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"stroke\",\n \"d\": \"M 9 11.6 L 9 16.4\",\n \"width\": 1.6\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 17.6 13.5 L 21.5 16.33 L 20.01 20.92 L 15.19 20.92 L 13.7 16.33 Z\"\n }\n ]\n },\n \"resize\": {\n \"box\": 24,\n \"hotspot\": [\n 12,\n 12\n ],\n \"paths\": [\n {\n \"role\": \"ink\",\n \"d\": \"M 2.5 12 L 7 8.8 L 7 10.7 L 17 10.7 L 17 8.8 L 21.5 12 L 17 15.2 L 17 13.3 L 7 13.3 L 7 15.2 Z\"\n }\n ]\n },\n \"rotate\": {\n \"box\": 24,\n \"hotspot\": [\n 12,\n 12\n ],\n \"paths\": [\n {\n \"role\": \"stroke\",\n \"d\": \"M 5.8 9.74 A 6.6 6.6 0 0 1 18.2 9.74\",\n \"width\": 2.8\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 4.22 14.07 L 2.98 8.72 L 8.62 10.77 Z\"\n },\n {\n \"role\": \"ink\",\n \"d\": \"M 19.78 14.07 L 15.38 10.77 L 21.02 8.72 Z\"\n }\n ]\n }\n} as const satisfies Record<string, CursorGlyph>;\n\n/** Every glyph name in the set. */\nexport type CursorGlyphName = keyof typeof GLYPHS;\n","import { bakeCursor, quantizeCursorAngle } from './bake';\nimport type { BakeOptions } from './bake';\nimport { GLYPHS } from './glyphs';\nimport type { CursorGlyphName } from './glyphs';\nimport type { CursorGlyph } from './types';\n\nconst cache = new Map<string, string>();\n\n/**\n * The baked cursor string for a named glyph, memoized.\n *\n * The key space is bounded by the glyph set times 16 angles times the handful\n * of sizes and fallbacks in use, so the cache needs no eviction.\n */\nexport function cursorFor(name: CursorGlyphName, opts: BakeOptions = {}): string {\n const size = opts.size ?? 24;\n const fallback = opts.fallback ?? 'default';\n // The step index, not the raw angle: the hover pump feeds a continuously\n // varying selection rotation, so a raw-angle key would never hit.\n const step = quantizeCursorAngle(opts.angle ?? 0);\n const key = `${name}|${size}|${step}|${fallback}`;\n const hit = cache.get(key);\n if (hit !== undefined) return hit;\n\n // Widened deliberately: the key type says this is always defined, but the\n // guard is what makes a bad name from untyped JS throw instead of baking\n // `undefined` into a cursor string that silently does nothing.\n const glyph = (GLYPHS as Record<string, CursorGlyph | undefined>)[name];\n if (glyph === undefined) {\n throw new Error(`unknown cursor glyph: ${String(name)}`);\n }\n const css = bakeCursor(glyph, { size, fallback, angle: opts.angle ?? 0 });\n cache.set(key, css);\n return css;\n}\n","import { cursorFor } from './registry';\nimport type { CursorGlyphName } from './glyphs';\nimport { CURSOR_MAX_CSS_PX } from './types';\n\n/**\n * What a tool, action or affordance declares as its cursor.\n *\n * A bare string is a CSS cursor value and passes through untouched, so every\n * declaration written before this type existed keeps working.\n */\nexport type CursorSpec = string | CursorGlyphSpec;\n\n/** The glyph form of a {@link CursorSpec}. */\nexport interface CursorGlyphSpec {\n readonly glyph: CursorGlyphName;\n /** Rendered size in CSS px. Default 24. */\n readonly size?: number;\n /**\n * Size in world units instead of `size`, so the glyph tracks zoom — a brush\n * radius ring. Forces the painted tier at every zoom level, because a baked\n * image would have to be rebuilt on every wheel tick.\n */\n readonly worldRadius?: number;\n /** Clockwise rotation in radians, quantized to 22.5° at bake. */\n readonly angle?: number;\n /** Keyword drawn if the browser rejects the image. Default 'default'. */\n readonly fallback?: string;\n}\n\n/**\n * Which tier a spec reaches the screen through.\n *\n * Tools never choose this — they declare what they want and the resolver\n * picks, so a brush cursor stays a brush cursor across the radius where it\n * stops being expressible as a CSS cursor at all.\n */\nexport type ResolvedCursor =\n | { readonly kind: 'css'; readonly css: string }\n | {\n readonly kind: 'painted';\n readonly glyph: CursorGlyphName;\n readonly angle: number;\n /** CSS px, when the spec fixed one. */\n readonly size?: number;\n /** World units; the painter scales this by the live view. */\n readonly worldRadius?: number;\n };\n\n/**\n * Resolve a spec to the tier that can actually draw it.\n *\n * Escalates to painted when the glyph is sized in world units, or when its\n * fixed size is past {@link CURSOR_MAX_CSS_PX} — above which the browser drops\n * the image and silently falls back to the keyword.\n */\nexport function resolveCursorTier(spec: CursorSpec): ResolvedCursor {\n if (typeof spec === 'string') return { kind: 'css', css: spec };\n const { glyph, size, worldRadius, angle = 0, fallback } = spec;\n if (worldRadius !== undefined) {\n return { kind: 'painted', glyph, angle, worldRadius };\n }\n if (size !== undefined && size > CURSOR_MAX_CSS_PX) {\n return { kind: 'painted', glyph, angle, size };\n }\n return {\n kind: 'css',\n css: cursorFor(glyph, {\n ...(size !== undefined ? { size } : {}),\n ...(fallback !== undefined ? { fallback } : {}),\n angle,\n }),\n };\n}\n\n/**\n * A {@link CursorSpec} as a CSS cursor value.\n *\n * Every consumer of a widened cursor field calls this, so a declaration never\n * has to know which form it holds. A painted cursor answers `'none'` — the\n * native cursor gets out of the way and the painted-tier layer draws it. A\n * caller that writes that string without the layer installed leaves the user\n * with no cursor, so reach for {@link resolveCursorTier} if you need to know.\n */\nexport function resolveCursor(spec: CursorSpec | undefined): string | undefined {\n if (spec === undefined) return undefined;\n const r = resolveCursorTier(spec);\n return r.kind === 'css' ? r.css : 'none';\n}\n","/**\n * The painted cursor a surface is currently showing, shared between whoever\n * decides it and the layer that draws it.\n *\n * Two slots rather than one, mirroring the precedence the cursor pipeline\n * already has: `<Canvas>` writes the active tool's cursor to `base` from\n * render, and the hover pump writes an affordance's or an action's cursor to\n * `override` imperatively. The override wins while it is set, and clearing it\n * restores the base without a React commit — the same reason the pump uses an\n * inline style for the CSS tier.\n */\n\nimport type { ResolvedCursor } from './resolve';\n\n/** The painted arm of `ResolvedCursor` — what this module stores. */\nexport type PaintedCursor = Extract<ResolvedCursor, { kind: 'painted' }>;\n\nexport interface PaintedCursorFrame {\n readonly cursor: PaintedCursor;\n /** Pointer position in the layer's own space: canvas-local CSS px. */\n readonly at: { readonly x: number; readonly y: number };\n}\n\nexport interface PaintedCursorState {\n setBase(cursor: PaintedCursor | null): void;\n setOverride(cursor: PaintedCursor | null): void;\n /** Canvas-local CSS px. */\n setPointer(x: number, y: number): void;\n /** Pointer left the surface: nothing to draw until it returns. */\n clearPointer(): void;\n /**\n * Whether a painted cursor is set at all, regardless of where the pointer is.\n *\n * Lets the pointer path skip measuring the canvas rect on every move in the\n * overwhelmingly common case where the compositor is drawing the cursor and\n * nothing here needs its position.\n */\n active(): boolean;\n /** What the layer should draw this frame, or null for none. */\n current(): PaintedCursorFrame | null;\n /** Run `fn` whenever the answer to `current()` may have changed. */\n subscribe(fn: () => void): () => void;\n}\n\n/**\n * Structural, not `Object.is`. Both writers rebuild their cursor object from a\n * spec every time they run — the pump on every pointermove, `<Canvas>` on\n * every render — so identity comparison would report a change constantly and\n * drive a repaint per pointer event.\n */\nfunction same(a: PaintedCursor | null, b: PaintedCursor | null): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n return (\n a.glyph === b.glyph &&\n a.angle === b.angle &&\n a.size === b.size &&\n a.worldRadius === b.worldRadius\n );\n}\n\nexport function createPaintedCursorState(): PaintedCursorState {\n let base: PaintedCursor | null = null;\n let override: PaintedCursor | null = null;\n let at: { x: number; y: number } | null = null;\n const subs = new Set<() => void>();\n\n const notify = (): void => {\n for (const fn of subs) fn();\n };\n\n return {\n setBase(cursor) {\n if (same(base, cursor)) return;\n base = cursor;\n notify();\n },\n setOverride(cursor) {\n if (same(override, cursor)) return;\n override = cursor;\n notify();\n },\n setPointer(x, y) {\n // A moved pointer only matters while something is being painted; a\n // notify per pointermove otherwise would request a redraw per event\n // for a cursor the compositor is drawing itself.\n if (at && at.x === x && at.y === y) return;\n at = { x, y };\n if (override ?? base) notify();\n },\n clearPointer() {\n if (at === null) return;\n at = null;\n notify();\n },\n active() {\n return (override ?? base) !== null;\n },\n current() {\n const cursor = override ?? base;\n if (!cursor || !at) return null;\n return { cursor, at };\n },\n subscribe(fn) {\n subs.add(fn);\n return () => { subs.delete(fn); };\n },\n };\n}\n"]}
|
package/package.json
CHANGED