@weasel-js/svg 1.6.1 → 1.7.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/README.md CHANGED
@@ -66,6 +66,14 @@ That cuts both ways on import: a `fill="url(#mesh1) #c04a3f"` from Inkscape,
66
66
  whose mesh gradients this package does not model, imports as flat `#c04a3f`
67
67
  rather than as a dropped fill.
68
68
 
69
+ `serializeSvg` is synchronous, so a kind loaded on demand (`mesh-gradient`, or
70
+ one declared with `registerPaintKindLoader`) that has not landed yet has no
71
+ `toSvg` to call, and its def is left out with a warning. `await warmSvg(nodes)`
72
+ first loads the kinds those nodes paint with, and the faces a sub- or
73
+ superscript run with its own `baselineShift` takes its size from — nothing
74
+ else, so an unrelated kind that fails to load cannot fail the export.
75
+ `svgNeeds(nodes)` lists the same things without loading them.
76
+
69
77
  ### A gradient's blend space
70
78
 
71
79
  A gradient's `interpolate` — `'oklab'` or `'oklch'` — has no SVG spelling
@@ -83,6 +91,69 @@ gradient — it just blends the stops in sRGB, which moves the midpoint rather
83
91
  than losing the paint. A space this package does not know is dropped on import
84
92
  rather than carried through.
85
93
 
94
+ ## Scene nodes, both ways
95
+
96
+ `svgNodesToKitDrafts(parseSvg(text), nextId)` lowers a document to scene-node
97
+ drafts the kit's built-in path, text and image painters draw, and registers
98
+ the document's markers. Containers carry no opacity, so an element or group
99
+ `opacity` is multiplied into the paints of the leaves under it. `nextId` is
100
+ handed the node each id is for, and `options.leaf` maps each leaf's kit data
101
+ into data of your own shape, from the source node's metadata too.
102
+
103
+ `svgNodesFromKit(scene)` goes the other way: containers become groups, and
104
+ each leaf is written the way its painter draws it, with the pose baked into
105
+ the geometry and box-relative paints resolved into that box. Hand
106
+ `serializeSvg` the result. Options pick the roots, skip nodes (a hidden
107
+ layer), replace a leaf's lowering or decorate a group. The per-leaf pieces are
108
+ public too: `svgLeafFromKit`, `svgPaintFromKit`, `svgStrokeFromKit`,
109
+ `svgImageFromKit`.
110
+
111
+ ## Stroke markers
112
+
113
+ A marker reference goes out as `marker-start` / `-mid` / `-end="url(#id)"` with
114
+ one `<marker>` def per distinct reference. The path keeps its full-length `d`:
115
+ baking the kit's inset in would have a re-import trim the line a second time.
116
+ The cost is that other renderers draw the line under a hollow head.
117
+
118
+ | Reference | Def id | What the def carries |
119
+ |---|---|---|
120
+ | `'arrow'` | `arrow` | `markerUnits="strokeWidth"` |
121
+ | `{ key: 'arrow', size: 3 }` | `arrow-s3` | `markerUnits="userSpaceOnUse"`, `wzl:key="arrow"`, `wzl:size="3"` |
122
+ | `{ key: 'arrow', size: { px: 6 } }` | `arrow-s6px` | the same, drawn at 6 user units, `wzl:size="6px"` |
123
+
124
+ Any entry with a nonzero inset adds `wzl:inset` in marker units, and the root
125
+ declares the `wzl` namespace whenever one of these attributes appears. `orient`
126
+ is `auto-start-reverse`, because the kit turns every start head around, or an
127
+ entry's fixed angle in degrees. An entry with its own `toSvg` writes its own
128
+ def and gets none of the `wzl` attributes (a sized reference to one warns). A
129
+ reference to a key nothing registered writes no attribute.
130
+
131
+ On import, `url(#id)` naming a registered key reads back as that key, so an
132
+ unsized built-in needs no def. A def with `wzl:key` and `wzl:size` reads back
133
+ as that sized reference, registering the def's geometry under the key if
134
+ nothing else has. Any other `<marker>` becomes an entry in
135
+ `ParseResult.markers`, keyed by its id plus a hash of what it draws, and
136
+ `svgNodesToKitDrafts` registers it. It is minted per reference rather than per
137
+ def, because a `userSpaceOnUse` size and an `orient="auto"` start both depend on
138
+ the referencing stroke. `wzl:inset` restores the inset; without it the inset is
139
+ 0. A reference to a marker neither registered nor defined warns and is dropped.
140
+
141
+ ## Stylesheets
142
+
143
+ `<style>` rules cascade with the presentation attributes and `style=""`. A parse
144
+ is one static render, so `@media` and `<style media>` are answered once, against
145
+ a fixed environment: a `screen` whose viewport is the root's `width`/`height`
146
+ (else its `viewBox`, else 300 × 150), `prefers-color-scheme: light`, and no
147
+ hover or pointer. Pass `parseSvg(svg, { media: { prefersColorScheme: 'dark',
148
+ width: 1200 } })` to render for different media; `evaluateMediaQuery` answers a
149
+ query against the same environment.
150
+
151
+ `@supports` asks whether this parser honors a declaration, not whether a browser
152
+ parses it: `(fill: red)` holds, `(display: grid)` and `(clip-path: url(#c))` do
153
+ not, because nothing reads them from a stylesheet. `@import` is not fetched, and
154
+ each one is reported in `warnings`. Rules inside `@layer`, `@container` and
155
+ other at-rules are skipped.
156
+
86
157
  ## Raster images
87
158
 
88
159
  `<image>` parses to an `SvgImageNode` holding the `href` verbatim — an
package/dist/index.d.ts CHANGED
@@ -1,5 +1,44 @@
1
1
  import { Mat3 } from '@weasel-js/geom';
2
- import { Path, FillStyle, ScreenLength, StrokeAlign, StyledRun, TextStyle, TextVerticalAlign, Stroke, MarkerEntry, TilePatternSpec, ImageNodeData, IngestCtx } from '@weasel-js/core';
2
+ import { Path, FillStyle, ScreenLength, StrokeAlign, MarkerRef, StyledRun, TextStyle, TextVerticalAlign, Stroke, MarkerEntry, RenderNeeds, TilePatternSpec, ImageNodeData, IngestCtx } from '@weasel-js/core';
3
+
4
+ /**
5
+ * Media Queries 4 evaluation against a fixed environment.
6
+ *
7
+ * A parse represents one static render, so `@media` and `<style media>` are
8
+ * answered once, against an {@link SvgMediaEnvironment}, rather than tracked
9
+ * as live conditions.
10
+ */
11
+ /** The media a parse is taken to be rendered on. */
12
+ interface SvgMediaEnvironment {
13
+ /** Media type; `all` always matches as well. */
14
+ readonly type: string;
15
+ /** Viewport width in CSS px. */
16
+ readonly width: number;
17
+ /** Viewport height in CSS px. */
18
+ readonly height: number;
19
+ /** Device pixels per CSS px. */
20
+ readonly resolution: number;
21
+ readonly prefersColorScheme: 'light' | 'dark';
22
+ readonly prefersReducedMotion: 'no-preference' | 'reduce';
23
+ /** Answers both `hover` and `any-hover`. */
24
+ readonly hover: 'none' | 'hover';
25
+ /** Answers both `pointer` and `any-pointer`. */
26
+ readonly pointer: 'none' | 'coarse' | 'fine';
27
+ /** How often the output can change once rendered. */
28
+ readonly update: 'none' | 'slow' | 'fast';
29
+ /** Bits per color component; 0 on a monochrome device. */
30
+ readonly color: number;
31
+ /** Bits per pixel of a monochrome device; 0 otherwise. */
32
+ readonly monochrome: number;
33
+ }
34
+ /** A static, light-scheme screen render with no pointer. Width and height are the replaced-element default. */
35
+ declare const DEFAULT_MEDIA_ENVIRONMENT: SvgMediaEnvironment;
36
+ /**
37
+ * Whether a media query list matches `env`. An empty list matches; a
38
+ * malformed query in the list is `not all`, leaving its neighbors intact; an
39
+ * unknown feature or value is unknown, which never matches, even under `not`.
40
+ */
41
+ declare function evaluateMediaQuery(list: string, env: SvgMediaEnvironment): boolean;
3
42
 
4
43
  /**
5
44
  * Public types for `@weasel-js/svg`. The package exposes a flat,
@@ -99,10 +138,12 @@ interface SvgStroke {
99
138
  * writes the stroke centered and says so through `onWarn`; the parser
100
139
  * never sets it. */
101
140
  align?: StrokeAlign;
102
- /** `marker-start` / `marker-mid` / `marker-end`, as the bare `url(#id)` key. */
103
- markerStart?: string;
104
- markerMid?: string;
105
- markerEnd?: string;
141
+ /** `marker-start` / `marker-mid` / `marker-end` as the kit's `MarkerRef`: the
142
+ * bare `url(#id)` key, or a key with the `size` the serializer writes into a
143
+ * def of its own. */
144
+ markerStart?: MarkerRef;
145
+ markerMid?: MarkerRef;
146
+ markerEnd?: MarkerRef;
106
147
  }
107
148
  /**
108
149
  * Leaf node: a path geometry plus fill/stroke. All other v1 shapes
@@ -253,6 +294,14 @@ interface ParseOptions {
253
294
  * the structured `meta` bag; they are silently dropped at serialize time.
254
295
  */
255
296
  namespaces?: Record<string, string>;
297
+ /**
298
+ * The media `@media` rules and `<style media>` are evaluated against. A
299
+ * parse is one static render: by default a `screen` whose viewport is the
300
+ * root's `width` / `height` (else its `viewBox`, else 300 × 150), with a
301
+ * light color scheme and no hover or pointer. Fields given here replace
302
+ * those defaults.
303
+ */
304
+ media?: Partial<SvgMediaEnvironment>;
256
305
  }
257
306
  /** Output of {@link parseSvg}. */
258
307
  interface ParseResult {
@@ -350,12 +399,24 @@ interface SerializeOptions {
350
399
  declare function parseSvg(svg: string, opts?: ParseOptions): ParseResult;
351
400
 
352
401
  /**
353
- * Serialize a `SvgNode[]` tree to an SVG document string. Every leaf is
354
- * emitted as a `<path>` (even shapes that started as `<rect>` etc.) —
355
- * lossless geometry round-trip beats round-tripping the element-kind
356
- * label. Gradient paints are gathered into a single `<defs>` block.
402
+ * Map between SVG `<linearGradient>` / `<radialGradient>` elements and
403
+ * weasel's `FillStyle` gradient variants. Definitions are indexed by id
404
+ * wherever in the document they appear, and looked up when a `fill` /
405
+ * `stroke` attribute references one as `url(#id)`. On serialize, we
406
+ * emit a fresh `<defs>` block with stable generated ids.
357
407
  */
358
408
 
409
+ /** `true` when SVG has a paint server for this kind, so the reference needs no
410
+ * fallback color and the root needs no foreign namespace. */
411
+ /** Whether SVG has its own paint server for paint kind `kind`: solids, the
412
+ * linear and radial gradients, and patterns. Any other kind is written in
413
+ * weasel's namespace with a fallback color, which other renderers paint flat. */
414
+ declare function nativeSvgKind(kind: string): boolean;
415
+ /** Whether a gradient interpolating through `space` blends the same in every
416
+ * SVG renderer — only sRGB, the default. A perceptual space still exports,
417
+ * but renderers other than weasel blend it in sRGB. */
418
+ declare function nativeSvgSpace(space: string | null | undefined): boolean;
419
+
359
420
  /**
360
421
  * Walk the tree and produce an SVG document string. The root `<svg>`'s
361
422
  * `viewBox` is taken from `opts.viewBox` if supplied, otherwise computed
@@ -363,6 +424,23 @@ declare function parseSvg(svg: string, opts?: ParseOptions): ParseResult;
363
424
  */
364
425
  declare function serializeSvg(nodes: SvgNode[], opts?: SerializeOptions): string;
365
426
 
427
+ /**
428
+ * What `serializeSvg(nodes)` reads that loads on demand: the paint kind of
429
+ * every fill and stroke it writes as a paint server, and the face of every
430
+ * run whose script size it takes from font metrics. The kinds come from the
431
+ * serializer's own paint pre-pass, so the two cannot disagree. Registered
432
+ * kinds are included.
433
+ */
434
+ declare function svgNeeds(nodes: readonly SvgNode[]): RenderNeeds;
435
+ /**
436
+ * Load what {@link svgNeeds} lists, so a `serializeSvg(nodes)` issued after it
437
+ * resolves writes every paint server's def and every script size from the
438
+ * face it renders in. Only those load: a lazily registered kind or font the
439
+ * nodes do not use is never fetched, and cannot fail the export. A kind
440
+ * nothing registered is passed over; the export warns about it instead.
441
+ */
442
+ declare function warmSvg(nodes: readonly SvgNode[]): Promise<void>;
443
+
366
444
  /**
367
445
  * Map between SVG `<pattern>` elements and weasel's `pattern` `FillStyle`.
368
446
  * The counterpart to `gradients.ts` for the other paint-server kind.
@@ -390,6 +468,102 @@ declare function tilePreviewSvg(spec: TilePatternSpec, background?: string): str
390
468
  /** `tilePreviewSvg` packed as a `url(...)` value for CSS `background-image`. */
391
469
  declare function tilePreviewCssUrl(spec: TilePatternSpec, background?: string): string;
392
470
 
471
+ /**
472
+ * `@supports` evaluation. A declaration test asks whether this parser would
473
+ * honor the declaration ({@link isDeclarationHonored}), not whether a browser
474
+ * could parse it — the answer decides whether the guarded rules apply here.
475
+ */
476
+ interface SupportsOptions {
477
+ /** Answers `selector(…)`; without it every selector test is false. */
478
+ readonly selector?: (selector: string) => boolean;
479
+ }
480
+ /**
481
+ * Whether a `@supports` condition holds. `selector()` defers to
482
+ * `opts.selector`; any other function and any unrecognized parenthesized
483
+ * content is false, so `not` of one is true. A malformed condition is false.
484
+ */
485
+ declare function evaluateSupports(condition: string, opts?: SupportsOptions): boolean;
486
+
487
+ /**
488
+ * The kit-to-SVG direction: scene leaves back to `SvgNode`s, the inverse of
489
+ * `svgNodesToKitDrafts`. Each leaf is written as the built-in painter that
490
+ * draws it would draw it — the pose baked into the geometry, box-relative
491
+ * paints resolved into that box — so `serializeSvg` over the result shows what
492
+ * the canvas shows.
493
+ */
494
+
495
+ /** A leaf's box: where it is drawn, and the frame its box-relative paints
496
+ * resolve against. */
497
+ interface SvgKitPose {
498
+ x: number;
499
+ y: number;
500
+ width: number;
501
+ height: number;
502
+ rotation?: number;
503
+ }
504
+ /** The leaf data the kit's path, text and image painters read — what
505
+ * `svgNodesToKitDrafts` writes. */
506
+ interface SvgKitLeafData {
507
+ path?: Path;
508
+ fill?: FillStyle | null;
509
+ stroke?: Stroke | null;
510
+ text?: string;
511
+ style?: TextStyle;
512
+ runs?: readonly StyledRun[];
513
+ verticalAlign?: TextVerticalAlign;
514
+ image?: ImageNodeData['image'];
515
+ }
516
+ /** A kit paint as an `SvgPaint`. `null` is `fill="none"`; a box-relative
517
+ * gradient or pattern is resolved into `box`, since the geometry it paints
518
+ * is written already placed there. */
519
+ declare function svgPaintFromKit(fill: FillStyle | null, box: SvgKitPose): SvgPaint;
520
+ /** A kit `Stroke` as an `SvgStroke` — the inverse of `strokeDataFromSvg`.
521
+ * One with no paint or no width draws nothing and is written as no stroke. */
522
+ declare function svgStrokeFromKit(stroke: Stroke | null | undefined, box: SvgKitPose): SvgStroke | undefined;
523
+ /** Write a `kit:image` leaf as an `SvgImageNode` — the inverse of the image
524
+ * arm of `svgNodesToKitDrafts`. The pose is the image's box. */
525
+ declare function svgImageFromKit(image: ImageNodeData['image'], pose: SvgKitPose): SvgImageNode;
526
+ /**
527
+ * Write one leaf as the `SvgNode` its built-in painter draws — image, text or
528
+ * path, in the order the painters claim a node. `null` for data none of them
529
+ * draws.
530
+ *
531
+ * A path with no `fill` takes `kit:path`'s fallback: the default shape fill
532
+ * when it has no stroke, and no fill when it has one.
533
+ */
534
+ declare function svgLeafFromKit(data: SvgKitLeafData, pose: SvgKitPose): SvgPathNode | SvgTextNode | SvgImageNode | null;
535
+ /** One node as {@link svgNodesFromKit} reads it. */
536
+ interface SvgKitTreeNode {
537
+ kind: string;
538
+ data: unknown;
539
+ pose: SvgKitPose;
540
+ }
541
+ /** The read side of a scene that {@link svgNodesFromKit} walks. A kit
542
+ * `Scene` is one as it stands. */
543
+ interface SvgKitTree<TId extends string = string> {
544
+ readonly roots: readonly TId[];
545
+ childrenOf(id: TId): readonly TId[];
546
+ get(id: TId): SvgKitTreeNode | undefined;
547
+ }
548
+ /** Options for {@link svgNodesFromKit}. */
549
+ interface SvgNodesFromKitOptions<TId extends string = string> {
550
+ /** Walk these ids, in this order, instead of the tree's roots. */
551
+ roots?: readonly TId[];
552
+ /** `false` leaves a node out, with everything under it — a hidden layer. */
553
+ include?(id: TId): boolean;
554
+ /** Lower a leaf yourself; `null` leaves it out. Defaults to {@link svgLeafFromKit}. */
555
+ leaf?(id: TId, node: SvgKitTreeNode): SvgNode | null;
556
+ /** Decorate the group written for a container, e.g. with `meta`. */
557
+ group?(id: TId, group: SvgGroupNode): SvgGroupNode;
558
+ }
559
+ /**
560
+ * Walk a scene's container tree into `SvgNode`s ready for `serializeSvg`:
561
+ * every `container` becomes a group of its children, in z-order, and every
562
+ * leaf goes through {@link svgLeafFromKit} unless `options.leaf` says
563
+ * otherwise.
564
+ */
565
+ declare function svgNodesFromKit<TId extends string>(tree: SvgKitTree<TId>, options?: SvgNodesFromKitOptions<TId>): SvgNode[];
566
+
393
567
  /** Axis-aligned box, in the coordinate space of the SVG being unpacked. Used
394
568
  * for the bounds a draft occupies before it becomes a scene node. */
395
569
  interface SvgDraftBounds {
@@ -406,7 +580,7 @@ type DraftPose = SvgDraftBounds & {
406
580
  * order (a draft's `parentId` always names an earlier draft, or `null` for
407
581
  * roots). Leaf `data` is kit-painter-native (see module doc).
408
582
  */
409
- type SvgSceneDraft = {
583
+ type SvgSceneDraft<TData = Record<string, unknown>> = {
410
584
  kind: 'container';
411
585
  id: string;
412
586
  parentId: string | null;
@@ -416,8 +590,30 @@ type SvgSceneDraft = {
416
590
  id: string;
417
591
  parentId: string | null;
418
592
  pose: DraftPose;
419
- data: Record<string, unknown>;
593
+ data: TData;
420
594
  };
595
+ /** A leaf `SvgNode`: everything but a group. */
596
+ type SvgLeafNode = Exclude<SvgNode, SvgGroupNode>;
597
+ /** Options for {@link svgNodesToKitDrafts}. */
598
+ interface SvgNodesToKitDraftsOptions<TData> {
599
+ /** Lower a leaf yourself, from the kit data the default writes for it and
600
+ * the node it came from — for data shaped other than the kit painters', or
601
+ * metadata the document carries. `null` leaves it out. */
602
+ leaf?(id: string, draft: {
603
+ pose: DraftPose;
604
+ data: SvgKitLeafData;
605
+ }, source: SvgLeafNode): TData | null;
606
+ }
607
+ /** Lower an `SvgPaint` onto the `kit:path` painter's `data.fill` — a
608
+ * `FillStyle`, or `null` for SVG's `fill="none"` (the painter skips the
609
+ * fill rather than falling back to its default). A solid paint keeps its
610
+ * `fill-opacity`.
611
+ *
612
+ * A gradient rides through as the `FillStyle` it already is, normalized to
613
+ * the leaf's own box: `objectBoundingBox` gradients already are, and a
614
+ * `userSpaceOnUse` one is rebased so it survives the fit-clamp and the
615
+ * drop-point placement that move the geometry out from under it. */
616
+ declare function fillDataFromSvg(paint: SvgPaint | undefined, box: SvgDraftBounds): FillStyle | null | undefined;
421
617
  /** Lower an `SvgStroke` onto the leaf's `data.stroke`.
422
618
  *
423
619
  * Everything the SVG carried — paint, width, cap, join, dash, miter limit,
@@ -426,16 +622,21 @@ type SvgSceneDraft = {
426
622
  * `userSpaceOnUse` gradient survives the fit-clamp and the drop-point
427
623
  * placement. */
428
624
  declare function strokeDataFromSvg(stroke: SvgStroke | undefined, box: SvgDraftBounds): Stroke | undefined;
429
- /** Write a `kit:image` leaf as an `SvgImageNode` — the inverse of the image
430
- * arm of {@link svgNodesToKitDrafts}. The pose is the image's box. */
431
- declare function svgImageFromKit(image: ImageNodeData['image'], pose: DraftPose): SvgImageNode;
432
625
  /**
433
626
  * Walk an `SvgNode[]` tree and emit a flat, parent-before-child list of
434
627
  * {@link SvgSceneDraft}s. Each `<g>` becomes a container whose pose is the
435
628
  * union AABB of its descendants (the kit `group` action's convention);
436
629
  * path/text leaves carry kit-painter-native data. Empty groups are dropped.
630
+ *
631
+ * Handed a whole `ParseResult`, it also registers the document's markers
632
+ * (`parsed.markers`), without which the strokes naming them draw bare.
633
+ * Containers carry no opacity, so an element or group `opacity` is multiplied
634
+ * into the paints of every leaf under it.
635
+ *
636
+ * `nextId` is handed the node each id is for. `options.leaf` replaces a leaf's
637
+ * data; a group whose leaves it all leaves out is dropped like an empty one.
437
638
  */
438
- declare function svgNodesToKitDrafts(nodes: readonly SvgNode[], nextId: () => string): SvgSceneDraft[];
639
+ declare function svgNodesToKitDrafts<TData = Record<string, unknown>>(input: ParseResult | readonly SvgNode[], nextId: (source: SvgNode) => string, options?: SvgNodesToKitDraftsOptions<TData>): SvgSceneDraft<TData>[];
439
640
  /**
440
641
  * Parse each file and insert its node tree — one undoable `applyOps` batch
441
642
  * per file. See the module doc for placement and wrapping policy. A file
@@ -444,4 +645,4 @@ declare function svgNodesToKitDrafts(nodes: readonly SvgNode[], nextId: () => st
444
645
  */
445
646
  declare function unpackSvgFiles(files: File[], ctx: IngestCtx): Promise<void>;
446
647
 
447
- export { IDENTITY_MATRIX, type Matrix, type NamespaceMeta, type NamespacedElement, type ParseOptions, type ParseResult, type SerializeOptions, type SvgDraftBounds, type SvgGroupNode, type SvgImageNode, type SvgNode, type SvgPaint, type SvgPathNode, type SvgSceneDraft, type SvgStroke, type SvgTextNode, parseSvg, serializeSvg, strokeDataFromSvg, svgImageFromKit, svgNodesToKitDrafts, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles };
648
+ export { DEFAULT_MEDIA_ENVIRONMENT, IDENTITY_MATRIX, type Matrix, type NamespaceMeta, type NamespacedElement, type ParseOptions, type ParseResult, type SerializeOptions, type SupportsOptions, type SvgDraftBounds, type SvgGroupNode, type SvgImageNode, type SvgKitLeafData, type SvgKitPose, type SvgKitTree, type SvgKitTreeNode, type SvgLeafNode, type SvgMediaEnvironment, type SvgNode, type SvgNodesFromKitOptions, type SvgNodesToKitDraftsOptions, type SvgPaint, type SvgPathNode, type SvgSceneDraft, type SvgStroke, type SvgTextNode, evaluateMediaQuery, evaluateSupports, fillDataFromSvg, nativeSvgKind, nativeSvgSpace, parseSvg, serializeSvg, strokeDataFromSvg, svgImageFromKit, svgLeafFromKit, svgNeeds, svgNodesFromKit, svgNodesToKitDrafts, svgPaintFromKit, svgStrokeFromKit, tilePreviewCssUrl, tilePreviewSvg, unpackSvgFiles, warmSvg };