uxml-preview 0.1.0 → 0.3.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.
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Differences from Unity that measurement has confirmed and that fixing the
3
+ * renderer cannot close. `docs/accuracy.md` describes 16 divergent values;
4
+ * all but one class of them are listed below.
5
+ *
6
+ * The 1px cases (3 values) are excluded on purpose: they sit inside the
7
+ * golden tests' own 0.5px-tighter-than-plan tolerance. That is a test
8
+ * judgement parameter, not a rendering limitation — listing it here would
9
+ * tell a host to worry about a difference the renderer did not actually make.
10
+ *
11
+ * If a fourth category is ever promoted here, extend this list and the
12
+ * pinned entry count in tests/known-divergences.test.ts together — do not
13
+ * let the list grow silently, since `id` is a contract hosts key off.
14
+ */
15
+ export interface KnownDivergence {
16
+ /** Stable identifier. Hosts may key off this; do not rename. */
17
+ readonly id: string;
18
+ /**
19
+ * Why this cannot simply be fixed.
20
+ *
21
+ * 'unreproducible' — the difference comes from something we cannot
22
+ * reproduce at all, such as font metrics that move with the platform.
23
+ * 'unspecified' — Unity's own rule for this case is not specified,
24
+ * so there is no target to match yet.
25
+ * 'upstream' — the difference is fully reproducible and deterministic,
26
+ * but it lives in something this renderer depends on rather than in this
27
+ * renderer's own code — the place that would need to change is not this
28
+ * repository.
29
+ */
30
+ readonly kind: 'unreproducible' | 'unspecified' | 'upstream';
31
+ /** One line, for a diagnostics list. */
32
+ readonly summary: string;
33
+ /** What is different and why, for someone deciding whether to care. */
34
+ readonly detail: string;
35
+ }
36
+ export declare const KNOWN_DIVERGENCES: readonly KnownDivergence[];
@@ -42,9 +42,31 @@ export interface LayoutOptions {
42
42
  */
43
43
  measureText: MeasureText;
44
44
  }
45
+ /**
46
+ * A laid-out element the file never mentioned, built by a control.
47
+ *
48
+ * Separate from `boxes` rather than sharing its keys: `NodeId` identifies
49
+ * something in the document, and these have no document to be in. Giving them
50
+ * borrowed ids would make "which node is this?" answerable with a lie, and an
51
+ * editor asking that question needs to hear "this one is not yours".
52
+ */
53
+ export interface LayoutPart {
54
+ /** Unity's name, e.g. `unity-content-viewport`. */
55
+ name: string;
56
+ /** The element whose control built it. */
57
+ owner: NodeId;
58
+ box: LayoutBox;
59
+ /** Fixed styling, with `builtin-theme` provenance on every value. */
60
+ style: ComputedStyle;
61
+ }
45
62
  export interface LayoutTree {
46
63
  /** Computed box per element, in panel coordinates. */
47
64
  boxes: ReadonlyMap<NodeId, LayoutBox>;
65
+ /**
66
+ * Parts each element's control built, outermost first. The element's own
67
+ * children were laid out inside the last one.
68
+ */
69
+ parts: ReadonlyMap<NodeId, readonly LayoutPart[]>;
48
70
  /** Elements that were laid out, parents before children. */
49
71
  painted: readonly ElementNode[];
50
72
  warnings: readonly Warning[];
@@ -60,4 +82,15 @@ export interface LayoutTree {
60
82
  * Deps/Effects: creates Yoga nodes; the returned tree owns them until disposed.
61
83
  * Requires: `loadLayoutEngine()` must have resolved.
62
84
  */
63
- export declare function layoutDocument(root: ElementNode, styles: ReadonlyMap<NodeId, ComputedStyle>, options: LayoutOptions): LayoutTree;
85
+ export declare function layoutDocument(root: ElementNode, styles: ReadonlyMap<NodeId, ComputedStyle>,
86
+ /**
87
+ * Style per control part, from the cascade.
88
+ *
89
+ * Taken rather than computed. This module used to build part styles itself
90
+ * from the registry's fixed declarations, which meant author USS could never
91
+ * reach them — `#unity-content-container { flex-direction: row }` had nowhere
92
+ * to land, because the part did not exist until after the cascade had run.
93
+ * Resolving parts alongside elements is what fixes that, and computing them
94
+ * in two places would put the second cascade back.
95
+ */
96
+ partStyles: ReadonlyMap<NodeId, ReadonlyMap<string, ComputedStyle>>, options: LayoutOptions): LayoutTree;
@@ -244,15 +244,22 @@ export interface UxmlDocument {
244
244
  * own `source`, which is why the source text lives on the sheet.
245
245
  */
246
246
  sheets: StyleSheet[];
247
- /** Parse-time only — malformed input. Support judgments happen downstream. */
247
+ /**
248
+ * Parse-time only — `'malformed'` (bad syntax) and `'import-unresolved'`
249
+ * (a `<Style src="…">` or `@import` that `resolveImport` could not read).
250
+ * Support judgments (unsupported controls/properties/selectors/units,
251
+ * unresolved assets) happen downstream, in `RenderResult.warnings`.
252
+ */
248
253
  warnings: Warning[];
249
254
  }
250
255
  /**
251
256
  * Where a computed value came from.
252
257
  *
253
258
  * Phase 8 needs this to answer "write to the inline style, or edit the rule?".
254
- * Every variant points into the model, so the editor can jump straight to the
255
- * declaration's span and rewrite exactly that text.
259
+ * Every variant *except `builtin-theme`* points into the model, so the editor
260
+ * can jump straight to the declaration's span and rewrite exactly that text.
261
+ * `builtin-theme` is the one that does not, and it says so rather than
262
+ * inventing a location — an editor must branch on it, not follow it.
256
263
  *
257
264
  * Note the inline variant indexes declarations parsed on demand from the
258
265
  * element's `style` attribute — those declarations are not stored on the node.
@@ -268,11 +275,40 @@ export type StyleOrigin = {
268
275
  sheet: number;
269
276
  item: number;
270
277
  declIndex: number;
278
+ /**
279
+ * Pseudo-class states the matched selector required, e.g. `['hover']`.
280
+ *
281
+ * Absent when the rule applies unconditionally. Present, it means the
282
+ * value is only true while those states hold — an editor showing "this
283
+ * came from line 12" without it would be telling half the truth, and a
284
+ * user would edit the base appearance while looking at the hover one.
285
+ */
286
+ states?: readonly string[];
271
287
  } | {
272
288
  kind: 'inherited';
273
289
  from: NodeId;
274
290
  origin: StyleOrigin;
275
291
  }
292
+ /**
293
+ * A control default this renderer supplies, standing in for Unity's theme
294
+ * stylesheet. **There is no file and no span**: the declaration exists in this
295
+ * library, not in the user's project.
296
+ *
297
+ * Its own variant rather than a `rule` with an invented path, because an
298
+ * editor offering "jump to the declaration" must be able to tell that there
299
+ * is nowhere to jump — pointing at a file that does not exist is worse than
300
+ * saying the value is not editable.
301
+ *
302
+ * `unityVersion` records where the value was measured. Theme values move
303
+ * between Unity versions, and when one stops matching this is the first thing
304
+ * to check.
305
+ */
306
+ | {
307
+ kind: 'builtin-theme';
308
+ selector: string;
309
+ property: string;
310
+ unityVersion: string;
311
+ }
276
312
  /** USS default, which is not always the CSS default — `flex-direction` is `column`. */
277
313
  | {
278
314
  kind: 'default';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * XML entity decoding, for the moment a stored attribute becomes a real value.
3
+ *
4
+ * The parser deliberately keeps attribute values exactly as written — that is
5
+ * what makes serialization byte-exact — so `&amp;` survives in the model. Any
6
+ * consumer that means to *use* the value rather than re-emit it has to decode
7
+ * first, and there are two: painted text, and paths handed to the host's
8
+ * resolver hooks. A `<Style src="…?a=1&amp;b=2">` reaching `resolveImport`
9
+ * undecoded gives the host `amp;b=2` to parse, which is not a thing.
10
+ */
11
+ /**
12
+ * Purpose: turn stored attribute text into the value it stands for.
13
+ * Ensures: never throws. A numeric reference outside the Unicode range
14
+ * makes `String.fromCodePoint` raise a RangeError, and one bad
15
+ * character must not take down a render (CLAUDE.md rule 6).
16
+ * Unreadable references are left as written, which also
17
+ * round-trips.
18
+ */
19
+ export declare function decodeEntities(text: string): string;
@@ -1,7 +1,16 @@
1
1
  import { NodeId, Warning } from '../model/types';
2
2
  import { ComputedStyle } from '../style/resolve';
3
3
  export interface CssMapOptions {
4
- resolveAsset?: ((path: string) => string | null) | undefined;
4
+ /**
5
+ * `form` tells `url("...")` apart from `resource("...")` — Unity resolves
6
+ * them differently: `url()` is a path (relative to the project or
7
+ * absolute), `resource()` names an asset inside a Resources folder by its
8
+ * Unity resource path (no extension). A host that ignores `form` and
9
+ * treats every path the same will resolve `resource("foo")` as a literal
10
+ * file path, which silently draws the wrong image whenever a same-named
11
+ * file happens to sit alongside it — or resolves nothing at all.
12
+ */
13
+ resolveAsset?: ((path: string, form: 'url' | 'resource') => string | null) | undefined;
5
14
  }
6
15
  export interface CssMapResult {
7
16
  declarations: Record<string, string>;
@@ -1,8 +1,20 @@
1
1
  import { ElementNode, NodeId, Warning } from '../model/types';
2
2
  import { ComputedStyle } from '../style/resolve';
3
- import { LayoutBox } from '../layout/yoga';
3
+ import { LayoutBox, LayoutPart } from '../layout/yoga';
4
4
  import { CssMapOptions } from './css-map';
5
5
  export declare const NODE_ATTRIBUTE = "data-uxml-node";
6
+ /**
7
+ * Marks an element a control built rather than one the file asked for, and
8
+ * carries Unity's name for it.
9
+ *
10
+ * Deliberately not `data-uxml-node`: these have no node. An editor that traced
11
+ * a click back through the node attribute would get an id that belongs to
12
+ * something else, and would then offer to edit the wrong element. Here it finds
13
+ * a different attribute and can say "this part is not yours to change".
14
+ */
15
+ export declare const PART_ATTRIBUTE = "data-uxml-part";
16
+ /** The element whose control built a part. Lets a click land on its owner. */
17
+ export declare const PART_OWNER_ATTRIBUTE = "data-uxml-part-owner";
6
18
  export interface PaintResult {
7
19
  warnings: Warning[];
8
20
  /** Painted element per model node, for hit-testing and for tests. */
@@ -16,4 +28,4 @@ export interface PaintOptions extends CssMapOptions {
16
28
  * Deps/Effects: replaces the container's children. The caller owns disposal.
17
29
  * Requires: `boxes` must come from a layout of this same tree.
18
30
  */
19
- export declare function paint(root: ElementNode, boxes: ReadonlyMap<NodeId, LayoutBox>, styles: ReadonlyMap<NodeId, ComputedStyle>, container: HTMLElement, options: PaintOptions): PaintResult;
31
+ export declare function paint(root: ElementNode, boxes: ReadonlyMap<NodeId, LayoutBox>, parts: ReadonlyMap<NodeId, readonly LayoutPart[]>, styles: ReadonlyMap<NodeId, ComputedStyle>, container: HTMLElement, options: PaintOptions): PaintResult;
@@ -7,20 +7,68 @@ export interface ComputedValue {
7
7
  }
8
8
  export type ComputedStyle = ReadonlyMap<string, ComputedValue>;
9
9
  export interface ResolveOptions {
10
- /** Pseudo-classes to treat as active, e.g. `new Set(['hover'])`. */
10
+ /**
11
+ * Pseudo-classes to treat as active on every element, e.g. `new Set(['hover'])`.
12
+ *
13
+ * Blunt, and kept for the case where that is what you want. Per-element
14
+ * control is `states`.
15
+ */
11
16
  activeStates?: ReadonlySet<string>;
17
+ /**
18
+ * Pseudo-classes to activate per element, keyed by USS selector.
19
+ *
20
+ * ```ts
21
+ * { '#UseButton': ['hover'], '#DropButton': ['disabled'] }
22
+ * ```
23
+ *
24
+ * States are explicit input, never mouse events: a render that depends on
25
+ * where the pointer is cannot be compared to anything twice.
26
+ *
27
+ * Keys are matched against the tree **without** any state applied, so a key
28
+ * cannot switch itself on, and `:hover` in a key is meaningless.
29
+ */
30
+ states?: Readonly<Record<string, readonly string[]>>;
12
31
  }
13
32
  export interface ResolveResult {
14
33
  styles: ReadonlyMap<NodeId, ComputedStyle>;
34
+ /**
35
+ * Style per control part, keyed by the owning element and then Unity's name
36
+ * for the part.
37
+ *
38
+ * Parts are resolved here rather than in the layout, because that is what
39
+ * lets author USS reach them: `#unity-content-container { flex-direction: row }`
40
+ * is how a real project lays out a scroll region's contents, and a part built
41
+ * after the cascade had already run could never receive it.
42
+ */
43
+ partStyles: ReadonlyMap<NodeId, ReadonlyMap<string, ComputedStyle>>;
15
44
  warnings: readonly Warning[];
16
45
  }
46
+ /**
47
+ * Where a declaration sits in the cascade's outermost comparison.
48
+ *
49
+ * Unity's control defaults are not "a stylesheet that loaded first" — they are
50
+ * a lower origin, the way a browser's user-agent sheet is, so **an author rule
51
+ * beats them regardless of specificity**. Measured, not reasoned: the
52
+ * `states-disabled` case writes `Button { margin: 0 }` (a type selector, 0,0,1)
53
+ * against the theme's `.unity-button` (a class, 0,1,0), and Unity puts the
54
+ * button at x=0. Specificity alone would have put it at x=3.
55
+ *
56
+ * This is the axis the cascade was missing. Without it the theme wins ties it
57
+ * should lose, and every value added to `theme.ts` spreads the error.
58
+ */
59
+ export declare const enum Rank {
60
+ BuiltinTheme = 0,
61
+ Author = 1
62
+ }
17
63
  /** One declaration that competed, winner or not. */
18
64
  export interface Candidate {
19
65
  property: string;
20
66
  value: string;
21
67
  origin: StyleOrigin;
68
+ /** Compared before specificity. A built-in default never beats author USS. */
69
+ rank: Rank;
22
70
  specificity: Specificity;
23
- /** Position in cascade order; higher wins a specificity tie. */
71
+ /** Position in cascade order; higher wins a specificity tie within a rank. */
24
72
  order: number;
25
73
  winner: boolean;
26
74
  }
@@ -1,7 +1,21 @@
1
- import { ElementNode, Selector } from '../model/types';
2
- export interface MatchContext {
3
- /** Parent of every node, built once per document. */
4
- parentOf: (node: ElementNode) => ElementNode | null;
1
+ import { Selector } from '../model/types';
2
+ /**
3
+ * How to interrogate whatever is being matched.
4
+ *
5
+ * Deps: every function here is read during matching, including for ancestors —
6
+ * `.panel:hover .item` asks the ancestor for its states, not the element's.
7
+ */
8
+ export interface MatchContext<T> {
9
+ /**
10
+ * The style parent, which is not always the layout parent.
11
+ *
12
+ * Measured, and it decides the shape of this whole design: a slot inside a
13
+ * ScrollView sits physically inside `unity-content-container`, yet
14
+ * `#Grid > .slot` reaches it in Unity. So an element from the file keeps its
15
+ * file parent here, and the parts a control builds are transparent to it.
16
+ * See `child-combinator-through-parts` and the 2026-08-06 decision rows.
17
+ */
18
+ parentOf: (target: T) => T | null;
5
19
  /**
6
20
  * The element `:root` refers to.
7
21
  *
@@ -11,11 +25,38 @@ export interface MatchContext {
11
25
  * keeps the usual custom-property token pattern working, since values
12
26
  * declared there inherit down the whole tree.
13
27
  */
14
- root: ElementNode;
15
- /** Pseudo-classes currently active, e.g. `hover`. Empty in a static render. */
16
- activeStates: ReadonlySet<string>;
28
+ isRoot: (target: T) => boolean;
29
+ /** Pseudo-classes active on this target, e.g. `hover`. */
30
+ statesOf: (target: T) => ReadonlySet<string>;
31
+ /** What a type selector compares against — the C# class name, not a tag. */
32
+ typeNameOf: (target: T) => string;
33
+ /** What `#name` compares against. */
34
+ idOf: (target: T) => string | undefined;
35
+ /** Written classes plus any the control adds, e.g. `unity-button`. */
36
+ classesOf: (target: T) => readonly string[];
17
37
  }
18
- export declare function matchesSelector(selector: Selector, node: ElementNode, ctx: MatchContext): boolean;
38
+ export declare function matchesSelector<T>(selector: Selector, target: T, ctx: MatchContext<T>): boolean;
39
+ /**
40
+ * Purpose: the state pseudo-classes a selector depends on, in source order.
41
+ * Ensures: `:root` is excluded — it names a position in the tree, not a
42
+ * state, and cannot be switched on or off.
43
+ *
44
+ * Used for provenance: a value from `.unity-button:hover` is only true while
45
+ * hovering, and an editor that does not know this rewrites the wrong rule.
46
+ */
47
+ export declare function statesRequiredBy(selector: Selector): string[];
48
+ /**
49
+ * Purpose: names and classes in a selector that belong to Unity, not the author.
50
+ * Ensures: returns the `unity-` prefixed fragments only.
51
+ *
52
+ * An author writing `#unity-text-input` or `.unity-scroll-view__content-container`
53
+ * is aiming at a part a control builds. If nothing matches, that is not an
54
+ * unused rule — it is a rule that cannot work, and saying nothing about it is
55
+ * the silent loss CLAUDE.md rule 6 exists to prevent.
56
+ */
57
+ export declare function unityFragmentsIn(selectors: readonly Selector[]): string[];
19
58
  /** The first unsupported fragment in a selector group, if any. */
20
59
  export declare function unsupportedFragment(selectors: readonly Selector[]): string | null;
21
- export declare function buildParentMap(root: ElementNode): Map<ElementNode, ElementNode | null>;
60
+ export declare function buildParentMap<T extends {
61
+ children: readonly T[];
62
+ }>(root: T): Map<T, T | null>;
@@ -0,0 +1,14 @@
1
+ export interface GuidIndex {
2
+ /** Absolute path of the asset with this GUID, or null. */
3
+ get(guid: string): string | null;
4
+ /** Number of entries. Lets a host report an empty scan. */
5
+ readonly size: number;
6
+ }
7
+ /**
8
+ * Purpose: resolve a `project://database/Assets/...` reference whose file has
9
+ * moved, by walking `.meta` files for their `guid:` line instead.
10
+ * Deps/Effects: reads the directory tree under `projectRoot`; unreadable
11
+ * `.meta` files are skipped rather than thrown, so one bad file cannot fail
12
+ * the whole scan.
13
+ */
14
+ export declare function buildGuidIndex(projectRoot: string): Promise<GuidIndex>;
@@ -0,0 +1,41 @@
1
+ import { readdir as c, readFile as u } from "node:fs/promises";
2
+ import { join as s } from "node:path";
3
+ const l = /* @__PURE__ */ new Set(["Library", "Temp", "obj", ".git"]), f = /^guid:\s*([0-9a-fA-F]+)/m;
4
+ async function y(n) {
5
+ const t = /* @__PURE__ */ new Map();
6
+ return await o(n, t), {
7
+ get(e) {
8
+ return t.get(e) ?? null;
9
+ },
10
+ get size() {
11
+ return t.size;
12
+ }
13
+ };
14
+ }
15
+ async function o(n, t) {
16
+ const e = await c(n, { withFileTypes: !0 });
17
+ for (const i of e) {
18
+ const a = s(n, i.name);
19
+ if (i.isDirectory()) {
20
+ if (l.has(i.name)) continue;
21
+ await o(a, t);
22
+ continue;
23
+ }
24
+ if (!i.name.endsWith(".meta")) continue;
25
+ const r = await m(a);
26
+ r !== null && t.set(r, a.slice(0, -5));
27
+ }
28
+ }
29
+ async function m(n) {
30
+ var e;
31
+ let t;
32
+ try {
33
+ t = await u(n, "utf8");
34
+ } catch {
35
+ return null;
36
+ }
37
+ return ((e = f.exec(t)) == null ? void 0 : e[1]) ?? null;
38
+ }
39
+ export {
40
+ y as buildGuidIndex
41
+ };
package/package.json CHANGED
@@ -1,59 +1,90 @@
1
- {
2
- "name": "uxml-preview",
3
- "version": "0.1.0",
4
- "description": "Preview Unity UI Toolkit UXML/USS in a browser without the Unity Editor. Uses Yoga (WebAssembly), the same layout engine UI Toolkit uses: 242 of 244 element coordinates identical to Unity 6. Parses and writes files back byte-for-byte.",
5
- "keywords": [
6
- "unity", "unity3d", "ui-toolkit", "uitk", "uielements", "uxml", "uss",
7
- "yoga", "yoga-layout", "flexbox", "webassembly", "renderer", "preview",
8
- "game-ui", "typescript", "editor-tooling", "unity-editor", "css-to-uss"
9
- ],
10
- "license": "Apache-2.0",
11
- "type": "module",
12
- "main": "./dist/index.js",
13
- "module": "./dist/index.js",
14
- "types": "./dist/index.d.ts",
15
- "exports": {
16
- ".": {
17
- "types": "./dist/index.d.ts",
18
- "import": "./dist/index.js"
19
- }
20
- },
21
- "files": ["dist", "README.md", "LICENSE", "THIRD-PARTY-NOTICES.md", "CHANGELOG.md"],
22
- "sideEffects": false,
23
- "engines": { "node": ">=18" },
24
- "publishConfig": { "access": "public" },
25
- "repository": {
26
- "type": "git",
27
- "url": "git+https://github.com/ReuHomi/uxml-preview.git"
28
- },
29
- "homepage": "https://reuhomi.github.io/uxml-preview/",
30
- "bugs": { "url": "https://github.com/ReuHomi/uxml-preview/issues" },
31
- "scripts": {
32
- "dev": "vite",
33
- "build": "tsc --noEmit && vite build",
34
- "build:site": "vite build --config vite.playground.config.ts",
35
- "preview:site": "vite preview --config vite.playground.config.ts",
36
- "test": "vitest run",
37
- "test:watch": "vitest",
38
- "test:roundtrip": "vitest run tests/roundtrip",
39
- "test:golden": "vitest run tests/golden --passWithNoTests",
40
- "golden:emit": "node --experimental-strip-types --no-warnings scripts/emit-golden-cases.mjs",
41
- "golden:update": "node scripts/update-golden.mjs",
42
- "typecheck": "tsc --noEmit",
43
- "prepublishOnly": "npm run typecheck && npm run test && npm run build"
44
- },
45
- "dependencies": {
46
- "yoga-layout": "^3.2.1"
47
- },
48
- "devDependencies": {
49
- "@types/node": "^22.0.0",
50
- "jsdom": "^30.0.1",
51
- "pixelmatch": "^6.0.0",
52
- "pngjs": "^7.0.0",
53
- "typescript": "^5.6.0",
54
- "vite": "^6.0.0",
55
- "vite-plugin-dts": "^4.3.0",
56
- "vitest": "^2.1.0"
57
- },
58
- "packageManager": "pnpm@9.12.0"
59
- }
1
+ {
2
+ "name": "uxml-preview",
3
+ "version": "0.3.0",
4
+ "description": "Preview Unity UI Toolkit UXML/USS in a browser without the Unity Editor. Uses Yoga (WebAssembly), the same layout engine UI Toolkit uses: 548 of 564 element coordinates identical to Unity 6. Parses and writes files back byte-for-byte.",
5
+ "keywords": [
6
+ "unity",
7
+ "unity3d",
8
+ "ui-toolkit",
9
+ "uitk",
10
+ "uielements",
11
+ "uxml",
12
+ "uss",
13
+ "yoga",
14
+ "yoga-layout",
15
+ "flexbox",
16
+ "webassembly",
17
+ "renderer",
18
+ "preview",
19
+ "game-ui",
20
+ "typescript",
21
+ "editor-tooling",
22
+ "unity-editor",
23
+ "css-to-uss"
24
+ ],
25
+ "license": "Apache-2.0",
26
+ "type": "module",
27
+ "main": "./dist/index.js",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js"
34
+ },
35
+ "./unity-project": {
36
+ "types": "./dist/unity-project/index.d.ts",
37
+ "import": "./dist/unity-project/index.js"
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md",
43
+ "LICENSE",
44
+ "THIRD-PARTY-NOTICES.md",
45
+ "CHANGELOG.md"
46
+ ],
47
+ "sideEffects": false,
48
+ "engines": {
49
+ "node": ">=18"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "repository": {
55
+ "type": "git",
56
+ "url": "git+https://github.com/ReuHomi/uxml-preview.git"
57
+ },
58
+ "homepage": "https://reuhomi.github.io/uxml-preview/",
59
+ "bugs": {
60
+ "url": "https://github.com/ReuHomi/uxml-preview/issues"
61
+ },
62
+ "scripts": {
63
+ "dev": "vite",
64
+ "build": "tsc --noEmit && vite build",
65
+ "build:site": "vite build --config vite.playground.config.ts",
66
+ "preview:site": "vite preview --config vite.playground.config.ts",
67
+ "test": "vitest run",
68
+ "test:watch": "vitest",
69
+ "test:roundtrip": "vitest run tests/roundtrip",
70
+ "test:golden": "vitest run tests/golden --passWithNoTests",
71
+ "golden:emit": "node --experimental-strip-types --no-warnings scripts/emit-golden-cases.mjs",
72
+ "golden:update": "node scripts/update-golden.mjs",
73
+ "typecheck": "tsc --noEmit",
74
+ "prepublishOnly": "npm run typecheck && npm run test && npm run build"
75
+ },
76
+ "dependencies": {
77
+ "yoga-layout": "^3.2.1"
78
+ },
79
+ "devDependencies": {
80
+ "@types/node": "^22.0.0",
81
+ "jsdom": "^30.0.1",
82
+ "pixelmatch": "^6.0.0",
83
+ "pngjs": "^7.0.0",
84
+ "typescript": "^5.6.0",
85
+ "vite": "^6.0.0",
86
+ "vite-plugin-dts": "^4.3.0",
87
+ "vitest": "^2.1.0"
88
+ },
89
+ "packageManager": "pnpm@9.12.0"
90
+ }