@yahoo/uds-create-config 4.10.2 → 4.10.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/dist/configs/CanvasConfig.d.ts +165 -77
  2. package/dist/configs/documentation.d.ts +1 -1
  3. package/dist/configs/react-native-system.d.ts +111 -52
  4. package/dist/configs/system.d.ts +121 -87
  5. package/dist/configs/system.js +4 -0
  6. package/dist/entities/documentation/Documentation.d.ts +1 -1
  7. package/dist/entities/native/NativeModifier.d.ts +1 -1
  8. package/dist/entities/native/NativeSettings.d.ts +1 -1
  9. package/dist/entities/native/NativeStyleProperty.d.ts +2 -2
  10. package/dist/entities/native/NativeToken.d.ts +4 -3
  11. package/dist/entities/system/CanvasRole.d.ts +1 -1
  12. package/dist/entities/system/Component.d.ts +23 -13
  13. package/dist/entities/system/Component.js +41 -15
  14. package/dist/entities/system/Composite.d.ts +1 -1
  15. package/dist/entities/system/Device.d.ts +1 -1
  16. package/dist/entities/system/File.d.ts +2 -2
  17. package/dist/entities/system/Font.d.ts +1 -1
  18. package/dist/entities/system/GlobalStyle.d.ts +1 -1
  19. package/dist/entities/system/Guidance.d.ts +1 -1
  20. package/dist/entities/system/GuidanceStyle.d.ts +1 -1
  21. package/dist/entities/system/Icon.d.ts +63 -11
  22. package/dist/entities/system/Icon.js +70 -14
  23. package/dist/entities/system/LinkedSystem.d.ts +1 -1
  24. package/dist/entities/system/Modifier.d.ts +2 -2
  25. package/dist/entities/system/Motion.d.ts +1 -1
  26. package/dist/entities/system/Package.d.ts +1 -1
  27. package/dist/entities/system/Settings.d.ts +1 -1
  28. package/dist/entities/system/StyleProperty.d.ts +12 -19
  29. package/dist/entities/system/StyleProperty.js +7 -10
  30. package/dist/entities/system/Token.d.ts +4 -3
  31. package/dist/entities/system/defineComponent.d.ts +21 -6
  32. package/dist/entities/system/defineComponent.js +7 -12
  33. package/dist/entities/system/glyph-identity.d.ts +7 -0
  34. package/dist/entities/system/glyph-identity.js +153 -0
  35. package/dist/entities/system/icon-domain-invariants.d.ts +11 -0
  36. package/dist/entities/system/icon-domain-invariants.js +178 -0
  37. package/dist/entities/system/icon-library.d.ts +93 -34
  38. package/dist/entities/system/icon-library.js +218 -50
  39. package/dist/entities/system/icon-metadata.d.ts +10 -4
  40. package/dist/entities/system/icon-metadata.js +45 -5
  41. package/dist/entities/system/runtime-modules.js +5 -5
  42. package/dist/entities/system/style-bag.js +14 -2
  43. package/dist/framework/Config.d.ts +1 -1
  44. package/dist/framework/Config.js +34 -5
  45. package/dist/framework/defineConfig.d.ts +6 -3
  46. package/dist/framework/defineEntity.d.ts +54 -15
  47. package/dist/framework/defineEntity.js +56 -2
  48. package/dist/framework/projections.d.ts +40 -2
  49. package/dist/framework/projections.js +99 -5
  50. package/dist/framework/prop-surface.d.ts +6 -5
  51. package/dist/framework/prop-surface.js +12 -14
  52. package/dist/framework/registry.d.ts +17 -1
  53. package/dist/framework/registry.js +2 -1
  54. package/dist/framework/render-spec.d.ts +6 -5
  55. package/dist/framework/render-spec.js +16 -11
  56. package/dist/framework/schema-version.d.ts +1 -1
  57. package/dist/framework/schema-version.js +5 -5
  58. package/dist/framework/signature.js +5 -1
  59. package/dist/framework/utils/group.d.ts +15 -1
  60. package/dist/framework/utils/ref-grammar.d.ts +35 -0
  61. package/dist/framework/utils/ref-grammar.js +76 -0
  62. package/dist/framework/utils/refs.d.ts +13 -45
  63. package/dist/framework/utils/refs.js +31 -94
  64. package/dist/framework/value-domain.js +53 -8
  65. package/dist/index.d.ts +6 -6
  66. package/dist/index.js +5 -5
  67. package/dist/migrations/2.0.0/v1-artifact.d.ts +62 -45
  68. package/dist/migrations/2.0.0/v1-artifact.js +5 -7
  69. package/dist/migrations/20260921044042_icon_library_without_component.d.ts +19 -0
  70. package/dist/migrations/20260921044042_icon_library_without_component.js +86 -0
  71. package/dist/migrations/20260922013625_icon_libraries.d.ts +18 -0
  72. package/dist/migrations/20260922013625_icon_libraries.js +8 -0
  73. package/dist/renderer/UdsRenderer.d.ts +8 -1
  74. package/dist/renderer/UdsRenderer.js +7 -3
  75. package/dist/renderer/glyphEntries.d.ts +32 -0
  76. package/dist/renderer/glyphEntries.js +48 -0
  77. package/dist/renderer/glyphRenderable.d.ts +10 -0
  78. package/dist/renderer/glyphRenderable.js +40 -0
  79. package/dist/renderer/icon-library.d.ts +18 -11
  80. package/dist/renderer/index.d.ts +4 -2
  81. package/dist/renderer/index.js +4 -2
  82. package/dist/renderer/resolveGlyph.d.ts +47 -0
  83. package/dist/renderer/resolveGlyph.js +78 -0
  84. package/dist/renderer/wrapRegistry.d.ts +13 -4
  85. package/dist/renderer/wrapRegistry.js +7 -5
  86. package/dist/renderer/wrappers/component-refs.js +6 -2
  87. package/dist/renderer/wrappers/glyph-props.d.ts +15 -0
  88. package/dist/renderer/wrappers/glyph-props.js +65 -0
  89. package/dist/spec/glyph-jsx.d.ts +41 -0
  90. package/dist/spec/{asset-jsx.js → glyph-jsx.js} +7 -25
  91. package/dist/spec/index.d.ts +2 -2
  92. package/dist/spec/index.js +2 -2
  93. package/dist/spec/jsxToSpec.d.ts +10 -4
  94. package/dist/spec/jsxToSpec.js +21 -4
  95. package/dist/spec/specToJsx.d.ts +20 -9
  96. package/dist/spec/specToJsx.js +77 -16
  97. package/dist/tsconfig.tsbuildinfo +1 -1
  98. package/package.json +2 -2
  99. package/dist/renderer/assetEntries.d.ts +0 -37
  100. package/dist/renderer/assetEntries.js +0 -38
  101. package/dist/renderer/assetRenderable.d.ts +0 -25
  102. package/dist/renderer/assetRenderable.js +0 -19
  103. package/dist/spec/asset-jsx.d.ts +0 -61
@@ -1,20 +1,27 @@
1
+ import { IconVariantAs } from "../entities/system/Icon.js";
1
2
  import React from "react";
2
3
 
3
4
  //#region src/renderer/icon-library.d.ts
5
+ /**
6
+ * One icon library as the emitted registry hands it to the renderer: the set's members as the
7
+ * components the package exports, the facts a bare glyph is drawn with, and the component that
8
+ * draws a placed glyph when the config has one — derived from the component whose own prop declares
9
+ * the set as its domain, never declared on the set.
10
+ */
4
11
  interface IconLibrary {
5
- /** The slug members are indexed under — `phosphor`. */
6
12
  readonly name: string;
7
- /** The component members render THROUGH, keyed by its registry ref stored verbatim
8
- * (`component:primitives/Icon`, not the stripped path) — a library's own `Icon`, which owns the
9
- * set-specific size/variant → glyph mapping. Absent, a member draws itself. */
10
- readonly component?: string;
11
- /** The members to register, in the order they should appear. */
12
- readonly assetNames: readonly string[];
13
- /** Name → the component that draws it. Absent for a library rebuilt from JSON, which has names but
14
- * nothing live to render — a browser surface draws those through the bundled registry instead. */
13
+ readonly glyphNames: readonly string[];
15
14
  readonly members?: Record<string, React.ComponentType<any> | undefined>;
16
- /** The spec `type` one member is addressed by — `icon:phosphor/StarIcon`. */
17
- readonly assetType: (name: string) => string;
15
+ readonly glyphType: (name: string) => string;
16
+ /** How a `variant` reaches a glyph, when the set declares it, so a raw glyph draws the requested
17
+ * variant rather than its default. */
18
+ readonly variantAs?: IconVariantAs;
19
+ /** Named sizes (`sm: 16`), so a raw glyph's `size` names a bucket rather than a pixel count. */
20
+ readonly sizes?: Readonly<Record<string, number>>;
21
+ /** The registry key of the component that draws this set's glyphs (`component:primitives/Icon`),
22
+ * and the prop it takes the glyph on. Absent, a placed glyph is the member drawn raw. */
23
+ readonly component?: string;
24
+ readonly glyphProp?: string;
18
25
  }
19
26
  //#endregion
20
27
  export { IconLibrary };
@@ -1,14 +1,16 @@
1
1
  import { ComponentEntry, MakeComponentEntryOptions, entryRenderer, makeComponentEntry, slotFillValue } from "./componentEntry.js";
2
- import { AssetEntriesResult, AssetMembers, assetRegistryEntries } from "./assetEntries.js";
2
+ import { GlyphEntriesResult, GlyphMembers, glyphRegistryEntries } from "./glyphEntries.js";
3
3
  import { FragmentRenderer } from "./primitives/FragmentRenderer.js";
4
4
  import { SlotRenderer } from "./primitives/SlotRenderer.js";
5
5
  import { RendererErrorBoundary } from "./RendererErrorBoundary.js";
6
+ import { Glyph, GlyphLibrary, ResolvedGlyph, resolveGlyph } from "./resolveGlyph.js";
6
7
  import { RuntimeComponentFailureContext, RuntimeComponentFailureReporter, RuntimeComponentRebuildContext, RuntimeComponentRebuildReporter, RuntimeComponentScope, RuntimeComponentStatus, RuntimeComponentStore, RuntimeComponentStoreContext, installRuntimeComponentStore, runtimeComponent } from "./runtimeComponent.js";
7
8
  import { REVEALED_LAYER_ATTR, specContent } from "./spec-content.js";
9
+ import { GlyphPropMap, wrapGlyphProps } from "./wrappers/glyph-props.js";
8
10
  import { aliasHtmlTypes } from "./wrappers/html-aliases.js";
9
11
  import { RegistryDescriptor, wrapRegistry } from "./wrapRegistry.js";
10
12
  import { UdsRenderer, UdsRendererProps } from "./UdsRenderer.js";
11
13
  import { wrapEventBridge } from "./wrappers/event-bridge.js";
12
14
  import { wrapNormalizeHex } from "./wrappers/hex-normalize.js";
13
15
  import { wrapInlineStyleProps } from "./wrappers/inline-styles.js";
14
- export { type AssetEntriesResult, type AssetMembers, type ComponentEntry, FragmentRenderer, type MakeComponentEntryOptions, REVEALED_LAYER_ATTR, type RegistryDescriptor, RendererErrorBoundary, RuntimeComponentFailureContext, type RuntimeComponentFailureReporter, RuntimeComponentRebuildContext, type RuntimeComponentRebuildReporter, type RuntimeComponentScope, type RuntimeComponentStatus, type RuntimeComponentStore, RuntimeComponentStoreContext, SlotRenderer, UdsRenderer, type UdsRendererProps, aliasHtmlTypes, assetRegistryEntries, entryRenderer, installRuntimeComponentStore, makeComponentEntry, runtimeComponent, slotFillValue, specContent, wrapEventBridge, wrapInlineStyleProps, wrapNormalizeHex, wrapRegistry };
16
+ export { type ComponentEntry, FragmentRenderer, type Glyph, type GlyphEntriesResult, type GlyphLibrary, type GlyphMembers, type GlyphPropMap, type MakeComponentEntryOptions, REVEALED_LAYER_ATTR, type RegistryDescriptor, RendererErrorBoundary, type ResolvedGlyph, RuntimeComponentFailureContext, type RuntimeComponentFailureReporter, RuntimeComponentRebuildContext, type RuntimeComponentRebuildReporter, type RuntimeComponentScope, type RuntimeComponentStatus, type RuntimeComponentStore, RuntimeComponentStoreContext, SlotRenderer, UdsRenderer, type UdsRendererProps, aliasHtmlTypes, entryRenderer, glyphRegistryEntries, installRuntimeComponentStore, makeComponentEntry, resolveGlyph, runtimeComponent, slotFillValue, specContent, wrapEventBridge, wrapGlyphProps, wrapInlineStyleProps, wrapNormalizeHex, wrapRegistry };
@@ -1,14 +1,16 @@
1
1
  import { RendererErrorBoundary } from "./RendererErrorBoundary.js";
2
2
  import { REVEALED_LAYER_ATTR, specContent } from "./spec-content.js";
3
3
  import { entryRenderer, makeComponentEntry, slotFillValue } from "./componentEntry.js";
4
- import { assetRegistryEntries } from "./assetEntries.js";
4
+ import { resolveGlyph } from "./resolveGlyph.js";
5
+ import { glyphRegistryEntries } from "./glyphEntries.js";
5
6
  import { FragmentRenderer } from "./primitives/FragmentRenderer.js";
6
7
  import { SlotRenderer } from "./primitives/SlotRenderer.js";
7
8
  import { RuntimeComponentFailureContext, RuntimeComponentRebuildContext, RuntimeComponentStoreContext, installRuntimeComponentStore, runtimeComponent } from "./runtimeComponent.js";
8
9
  import { wrapEventBridge } from "./wrappers/event-bridge.js";
10
+ import { wrapGlyphProps } from "./wrappers/glyph-props.js";
9
11
  import { wrapNormalizeHex } from "./wrappers/hex-normalize.js";
10
12
  import { aliasHtmlTypes } from "./wrappers/html-aliases.js";
11
13
  import { wrapInlineStyleProps } from "./wrappers/inline-styles.js";
12
14
  import { wrapRegistry } from "./wrapRegistry.js";
13
15
  import { UdsRenderer } from "./UdsRenderer.js";
14
- export { FragmentRenderer, REVEALED_LAYER_ATTR, RendererErrorBoundary, RuntimeComponentFailureContext, RuntimeComponentRebuildContext, RuntimeComponentStoreContext, SlotRenderer, UdsRenderer, aliasHtmlTypes, assetRegistryEntries, entryRenderer, installRuntimeComponentStore, makeComponentEntry, runtimeComponent, slotFillValue, specContent, wrapEventBridge, wrapInlineStyleProps, wrapNormalizeHex, wrapRegistry };
16
+ export { FragmentRenderer, REVEALED_LAYER_ATTR, RendererErrorBoundary, RuntimeComponentFailureContext, RuntimeComponentRebuildContext, RuntimeComponentStoreContext, SlotRenderer, UdsRenderer, aliasHtmlTypes, entryRenderer, glyphRegistryEntries, installRuntimeComponentStore, makeComponentEntry, resolveGlyph, runtimeComponent, slotFillValue, specContent, wrapEventBridge, wrapGlyphProps, wrapInlineStyleProps, wrapNormalizeHex, wrapRegistry };
@@ -0,0 +1,47 @@
1
+ import { IconVariantAs } from "../entities/system/Icon.js";
2
+ import React from "react";
3
+
4
+ //#region src/renderer/resolveGlyph.d.ts
5
+ type Glyph = React.ComponentType<any>;
6
+ /**
7
+ * What a library hands the resolver: its glyph namespace and the two set facts that turn a `variant`
8
+ * and a named `size` into props on a glyph. Structural, so the emitted registry literal, a runtime
9
+ * module's namespace import and a test stub all satisfy it.
10
+ */
11
+ interface GlyphLibrary {
12
+ /** The library's exports by name. Typed open because a real module namespace also carries
13
+ * non-component exports (`SSR`, `IconContext`); a name is narrowed to a component at lookup. */
14
+ readonly glyphs: Readonly<Record<string, unknown>>;
15
+ readonly variantAs?: IconVariantAs;
16
+ readonly sizes?: Readonly<Record<string, number>>;
17
+ }
18
+ interface ResolvedGlyph {
19
+ readonly Glyph: Glyph;
20
+ /** The props that carry the requested size and variant, in the library's own vocabulary. */
21
+ readonly props: Record<string, unknown>;
22
+ }
23
+ interface ResolveGlyphArgs {
24
+ library: GlyphLibrary;
25
+ /** The glyph itself, or the name the library exports it under. */
26
+ glyph: Glyph | string;
27
+ size?: string | number;
28
+ variant?: string;
29
+ }
30
+ /**
31
+ * The one place a `variant` and a named `size` become glyph props, so the drawer component, a
32
+ * picker cell and a raw glyph all draw the same thing for the same request.
33
+ *
34
+ * `variantAs` decides the variant: `prop` sets it as a prop, `export` swaps in the variant's own
35
+ * export when the template names one and it exists, and no declaration drops it. A named size reads
36
+ * the library's `sizes` and is dropped when it names no bucket; a number reaches the glyph as it is.
37
+ *
38
+ * A glyph the library does not export keeps its size and loses its variant, since neither arm of
39
+ * `variantAs` can be applied without the export name. A name the library does not export resolves
40
+ * to `undefined`.
41
+ */
42
+ declare function resolveGlyph(args: ResolveGlyphArgs & {
43
+ glyph: Glyph;
44
+ }): ResolvedGlyph;
45
+ declare function resolveGlyph(args: ResolveGlyphArgs): ResolvedGlyph | undefined;
46
+ //#endregion
47
+ export { Glyph, GlyphLibrary, ResolvedGlyph, resolveGlyph };
@@ -0,0 +1,78 @@
1
+ //#region src/renderer/resolveGlyph.ts
2
+ function resolveGlyph({ library, glyph, size, variant }) {
3
+ let bare;
4
+ let name;
5
+ if (typeof glyph === "string") {
6
+ bare = glyphAt({
7
+ glyphs: library.glyphs,
8
+ name: glyph
9
+ });
10
+ name = glyph;
11
+ } else {
12
+ bare = glyph;
13
+ name = exportNameOf({
14
+ glyphs: library.glyphs,
15
+ glyph
16
+ });
17
+ }
18
+ if (!bare) return void 0;
19
+ const props = {};
20
+ const px = sizePx({
21
+ sizes: library.sizes,
22
+ size
23
+ });
24
+ if (px !== void 0) props.size = px;
25
+ const { variantAs } = library;
26
+ if (!variant || !variantAs || name === void 0) return {
27
+ Glyph: bare,
28
+ props
29
+ };
30
+ if ("prop" in variantAs) {
31
+ props[variantAs.prop] = variant;
32
+ return {
33
+ Glyph: bare,
34
+ props
35
+ };
36
+ }
37
+ const template = variantAs.export[variant];
38
+ if (!template) return {
39
+ Glyph: bare,
40
+ props
41
+ };
42
+ return {
43
+ Glyph: glyphAt({
44
+ glyphs: library.glyphs,
45
+ name: template.replaceAll("{name}", name)
46
+ }) ?? bare,
47
+ props
48
+ };
49
+ }
50
+ /** The export at `name` when it can be rendered: a function component, or an object such as a
51
+ * `forwardRef`/`memo` wrapper. Own keys only: a plain-object namespace inherits `constructor`, and
52
+ * a glyph by that name would resolve `Object`. */
53
+ function glyphAt({ glyphs, name }) {
54
+ if (!Object.hasOwn(glyphs, name)) return void 0;
55
+ const value = glyphs[name];
56
+ if (typeof value === "function") return value;
57
+ if (typeof value === "object" && value !== null) return value;
58
+ }
59
+ /** Reverse maps by namespace. A namespace is one stable object per library, so the walk over its
60
+ * exports happens once however many glyphs resolve against it. */
61
+ const namesByNamespace = /* @__PURE__ */ new WeakMap();
62
+ /** The name a glyph is exported under, found by reference. */
63
+ function exportNameOf({ glyphs, glyph }) {
64
+ let names = namesByNamespace.get(glyphs);
65
+ if (!names) {
66
+ names = /* @__PURE__ */ new Map();
67
+ for (const key of Object.keys(glyphs)) names.set(glyphs[key], key);
68
+ namesByNamespace.set(glyphs, names);
69
+ }
70
+ return names.get(glyph);
71
+ }
72
+ /** A named bucket's pixels, or a number as it is. A name the library does not bucket is dropped. */
73
+ function sizePx({ sizes, size }) {
74
+ if (typeof size === "string") return sizes?.[size];
75
+ return size;
76
+ }
77
+ //#endregion
78
+ export { resolveGlyph };
@@ -1,3 +1,5 @@
1
+ import { GlyphMembers } from "./glyphEntries.js";
2
+ import { GlyphPropMap } from "./wrappers/glyph-props.js";
1
3
  import { StylePropClassMap } from "./wrappers/html-aliases.js";
2
4
  import { ComponentRegistry } from "@json-render/react";
3
5
 
@@ -41,6 +43,12 @@ interface RegistryDescriptor {
41
43
  * an imported `style` is dropped — text shouldn't carry a bounding box.
42
44
  */
43
45
  textComponents?: readonly string[];
46
+ /**
47
+ * Registry key → prop → the icon libraries that prop's domain names, in declaration order. A
48
+ * glyph is stored as its leaf or its `icon:` ref, and `wrapGlyphProps` hands the render the
49
+ * member component instead, read from the members the registry was built with.
50
+ */
51
+ glyphProps?: GlyphPropMap;
44
52
  }
45
53
  /**
46
54
  * Apply the standard wrapper stack + built-in spec primitives to an
@@ -50,10 +58,11 @@ interface RegistryDescriptor {
50
58
  * wrappers on top.
51
59
  *
52
60
  * `descriptor` carries the config-derived facts the config-dependent
53
- * wrappers (`inline-styles`, `html-aliases`) need; omit it and those
54
- * wrappers pass through. Composition order matters — each wrapper assumes
55
- * its outer wrappers haven't yet seen the element.
61
+ * wrappers (`inline-styles`, `html-aliases`, `glyph-props`) need; omit it and those
62
+ * wrappers pass through. `iconMembers` is what a glyph prop resolves through, so it is
63
+ * needed only alongside a descriptor that names glyph props. Composition order matters — each
64
+ * wrapper assumes its outer wrappers haven't yet seen the element.
56
65
  */
57
- declare function wrapRegistry(reg: ComponentRegistry, descriptor?: RegistryDescriptor): ComponentRegistry;
66
+ declare function wrapRegistry(reg: ComponentRegistry, descriptor?: RegistryDescriptor, iconMembers?: GlyphMembers): ComponentRegistry;
58
67
  //#endregion
59
68
  export { RegistryDescriptor, wrapRegistry };
@@ -2,6 +2,7 @@ import { FragmentRenderer } from "./primitives/FragmentRenderer.js";
2
2
  import { SlotRenderer } from "./primitives/SlotRenderer.js";
3
3
  import { wrapComponentRefs } from "./wrappers/component-refs.js";
4
4
  import { wrapEventBridge } from "./wrappers/event-bridge.js";
5
+ import { wrapGlyphProps } from "./wrappers/glyph-props.js";
5
6
  import { wrapNormalizeHex } from "./wrappers/hex-normalize.js";
6
7
  import { aliasHtmlTypes } from "./wrappers/html-aliases.js";
7
8
  import { wrapInlineStyleProps } from "./wrappers/inline-styles.js";
@@ -14,11 +15,12 @@ import { wrapInlineStyleProps } from "./wrappers/inline-styles.js";
14
15
  * wrappers on top.
15
16
  *
16
17
  * `descriptor` carries the config-derived facts the config-dependent
17
- * wrappers (`inline-styles`, `html-aliases`) need; omit it and those
18
- * wrappers pass through. Composition order matters — each wrapper assumes
19
- * its outer wrappers haven't yet seen the element.
18
+ * wrappers (`inline-styles`, `html-aliases`, `glyph-props`) need; omit it and those
19
+ * wrappers pass through. `iconMembers` is what a glyph prop resolves through, so it is
20
+ * needed only alongside a descriptor that names glyph props. Composition order matters — each
21
+ * wrapper assumes its outer wrappers haven't yet seen the element.
20
22
  */
21
- function wrapRegistry(reg, descriptor) {
23
+ function wrapRegistry(reg, descriptor, iconMembers) {
22
24
  const base = {
23
25
  ...reg,
24
26
  Fragment: reg.Fragment ?? FragmentRenderer,
@@ -26,7 +28,7 @@ function wrapRegistry(reg, descriptor) {
26
28
  };
27
29
  const stylePropNames = descriptor?.stylePropClasses ? new Set([...Object.keys(descriptor.stylePropClasses), ...Object.values(descriptor.cssPropertyToStyleProp ?? {})]) : void 0;
28
30
  const holder = {};
29
- const final = aliasHtmlTypes(wrapInlineStyleProps(wrapNormalizeHex(wrapEventBridge(wrapComponentRefs(base, (type) => holder.registry?.[type])), stylePropNames), {
31
+ const final = aliasHtmlTypes(wrapInlineStyleProps(wrapNormalizeHex(wrapEventBridge(wrapGlyphProps(wrapComponentRefs(base, (type) => holder.registry?.[type]), descriptor?.glyphProps, iconMembers)), stylePropNames), {
30
32
  cssPropertyToStyleProp: descriptor?.cssPropertyToStyleProp,
31
33
  layoutComponents: descriptor?.layoutComponents,
32
34
  textComponents: descriptor?.textComponents
@@ -1,4 +1,4 @@
1
- import { isComponentRef } from "../../spec/preview-elements.js";
1
+ import { isComponentRef, isPreviewElement } from "../../spec/preview-elements.js";
2
2
  import React from "react";
3
3
  //#region src/renderer/wrappers/component-refs.tsx
4
4
  /** A revived ref is called by the component that received it, outside any spec event routing. */
@@ -9,17 +9,21 @@ const NO_EVENT = {
9
9
  };
10
10
  /**
11
11
  * Whether anything under a value is a stored component ref. Stops at a live React element: walking
12
- * its owner Fiber reaches a circular structure, and an element never holds an un-revived ref.
12
+ * its owner Fiber reaches a circular structure, and an element never holds an un-revived ref. Stops
13
+ * at a stored preview element too: its `element` ref is the tag the marker reviver renders it as, and
14
+ * the refs inside its props are that render's to revive.
13
15
  */
14
16
  function hasRefDeep(value) {
15
17
  if (typeof value !== "object" || value === null) return false;
16
18
  if ("$$typeof" in value) return false;
19
+ if (isPreviewElement(value)) return false;
17
20
  if (isComponentRef(value)) return true;
18
21
  return (Array.isArray(value) ? value : Object.values(value)).some(hasRefDeep);
19
22
  }
20
23
  function reviveDeep(value, revive) {
21
24
  if (typeof value !== "object" || value === null) return value;
22
25
  if ("$$typeof" in value) return value;
26
+ if (isPreviewElement(value)) return value;
23
27
  if (isComponentRef(value)) return revive(value);
24
28
  if (Array.isArray(value)) return value.map((item) => reviveDeep(item, revive));
25
29
  const out = {};
@@ -0,0 +1,15 @@
1
+ import { GlyphMembers } from "../glyphEntries.js";
2
+ import { ComponentRegistry } from "@json-render/react";
3
+
4
+ //#region src/renderer/wrappers/glyph-props.d.ts
5
+ /** Registry key → glyph prop → the icon libraries that are the prop's domain, in declaration
6
+ * order. A stored leaf resolves under the first library that holds it. */
7
+ type GlyphPropMap = Readonly<Record<string, Readonly<Record<string, readonly string[]>>>>;
8
+ /**
9
+ * A glyph reaches a render as the member component, whatever form the spec stored it in. Only the
10
+ * props the descriptor names are read, so a component's own string props are never mistaken for
11
+ * glyph names.
12
+ */
13
+ declare function wrapGlyphProps(reg: ComponentRegistry, glyphProps?: GlyphPropMap, members?: GlyphMembers): ComponentRegistry;
14
+ //#endregion
15
+ export { GlyphPropMap, wrapGlyphProps };
@@ -0,0 +1,65 @@
1
+ import { isRef, splitRef } from "../../framework/utils/ref-grammar.js";
2
+ import React from "react";
3
+ //#region src/renderer/wrappers/glyph-props.tsx
4
+ /**
5
+ * The member a stored glyph names: a leaf (`'StarIcon'`), resolved under the first of the prop's
6
+ * libraries that holds it, or an `icon:` ref (`icon:phosphor/StarIcon`, with or without a source
7
+ * qualifier) naming library and member itself. A ref is taken whatever the prop's libraries are,
8
+ * since it says where to look. A value that is already a component, or that no library holds, is
9
+ * left alone.
10
+ */
11
+ function glyphFor({ value, libraries, members }) {
12
+ if (typeof value === "string") {
13
+ for (const slug of libraries) {
14
+ const glyph = members.get(slug)?.get(value);
15
+ if (glyph) return glyph;
16
+ }
17
+ return;
18
+ }
19
+ if (!isRef(value)) return void 0;
20
+ const parts = splitRef(value);
21
+ if (parts.kind !== "icon") return void 0;
22
+ const slash = parts.path.lastIndexOf("/");
23
+ if (slash <= 0) return void 0;
24
+ return members.get(parts.path.slice(0, slash))?.get(parts.path.slice(slash + 1));
25
+ }
26
+ /**
27
+ * A glyph reaches a render as the member component, whatever form the spec stored it in. Only the
28
+ * props the descriptor names are read, so a component's own string props are never mistaken for
29
+ * glyph names.
30
+ */
31
+ function wrapGlyphProps(reg, glyphProps, members) {
32
+ if (!glyphProps || !members) return reg;
33
+ const wrapped = { ...reg };
34
+ for (const [key, props] of Object.entries(glyphProps)) {
35
+ const Comp = reg[key];
36
+ if (!Comp) continue;
37
+ wrapped[key] = function GlyphPropsWrapper(renderProps) {
38
+ const bag = renderProps.element.props;
39
+ if (!bag) return React.createElement(Comp, renderProps);
40
+ let patched = false;
41
+ const next = { ...bag };
42
+ for (const [prop, libraries] of Object.entries(props)) {
43
+ const glyph = glyphFor({
44
+ value: next[prop],
45
+ libraries,
46
+ members
47
+ });
48
+ if (!glyph) continue;
49
+ next[prop] = glyph;
50
+ patched = true;
51
+ }
52
+ if (!patched) return React.createElement(Comp, renderProps);
53
+ return React.createElement(Comp, {
54
+ ...renderProps,
55
+ element: {
56
+ ...renderProps.element,
57
+ props: next
58
+ }
59
+ });
60
+ };
61
+ }
62
+ return wrapped;
63
+ }
64
+ //#endregion
65
+ export { wrapGlyphProps };
@@ -0,0 +1,41 @@
1
+ //#region src/spec/glyph-jsx.d.ts
2
+ /**
3
+ * The identifier a type is KNOWN AS in JSX — `component:buttons/Primary` → `Primary`.
4
+ *
5
+ * A type is a ref (`component:buttons/Primary`), and neither half of it is JSX: the kind isn't part
6
+ * of the name, and a grouped path isn't a legal tag or import name. The leaf is what codegen names
7
+ * the export, so `<Primary />` and `import { Primary }` agree with the module that actually exists.
8
+ * An intrinsic tag has no kind and no group, so it passes through untouched.
9
+ */
10
+ declare function jsxIdentifier(type: string): string;
11
+ /** Pull the library slug + member out of a glyph node's type, or `null` for anything else — a
12
+ * component, a package export, an intrinsic tag. */
13
+ declare function parseGlyphType(type: string): {
14
+ slug: string;
15
+ member: string;
16
+ } | null;
17
+ /**
18
+ * `slug → the component a placed member renders through` (e.g. `{ icons: 'Icon' }`).
19
+ * Derived from the asset groups that declare a `component`; a slug missing from
20
+ * the map has no authoring component and falls back to the bare member tag.
21
+ * A value may be the component's bare name OR its full path (`primitives/Icon`) —
22
+ * serialization reduces it to the JSX leaf either way ({@link jsxIdentifier}),
23
+ * since a grouped path is not a legal tag.
24
+ */
25
+ interface GlyphJsxForm {
26
+ /** The member's own name is its tag: `<Trophy />`. */
27
+ tag: string;
28
+ /** What the emitted file imports, from the library's module. */
29
+ importName: string;
30
+ }
31
+ /** How a placed glyph node prints: the member as its own tag. `null` for a normal component type. */
32
+ declare function glyphJsxForm(type: string): GlyphJsxForm | null;
33
+ /**
34
+ * Reads a bare member tag back to the glyph's ref, or `null` for a component node. `host` is the
35
+ * qualified type of the enclosing element (`component:Box`), or `null` at the root or under a tag
36
+ * the parser could not qualify, so a resolver can narrow a name two libraries both ship to the one
37
+ * the host draws from.
38
+ */
39
+ type GlyphNodeResolver = (tag: string, props: Readonly<Record<string, unknown>>, host: string | null) => string | null;
40
+ //#endregion
41
+ export { GlyphJsxForm, GlyphNodeResolver, glyphJsxForm, jsxIdentifier, parseGlyphType };
@@ -1,5 +1,5 @@
1
1
  import { pathOf } from "../framework/utils/refs.js";
2
- //#region src/spec/asset-jsx.ts
2
+ //#region src/spec/glyph-jsx.ts
3
3
  const ASSET_KIND = "icon:";
4
4
  /**
5
5
  * The identifier a type is KNOWN AS in JSX — `component:buttons/Primary` → `Primary`.
@@ -13,9 +13,9 @@ function jsxIdentifier(type) {
13
13
  const path = pathOf(type);
14
14
  return path.slice(path.lastIndexOf("/") + 1);
15
15
  }
16
- /** Pull the library slug + member out of an asset node's type, or `null` for anything else — a
16
+ /** Pull the library slug + member out of a glyph node's type, or `null` for anything else — a
17
17
  * component, a package export, an intrinsic tag. */
18
- function parseAssetType(type) {
18
+ function parseGlyphType(type) {
19
19
  if (!type.startsWith(ASSET_KIND)) return null;
20
20
  const bodyStart = 5;
21
21
  const slashIndex = type.indexOf("/", bodyStart);
@@ -28,32 +28,14 @@ function parseAssetType(type) {
28
28
  member
29
29
  };
30
30
  }
31
- /**
32
- * Resolve a spec element `type` to its exported-JSX form, or `null` when the
33
- * type is not an asset node (a normal component — caller serializes as usual).
34
- *
35
- * - mapped slug → `<Icon name="Member" … />` (component-typed export);
36
- * - unmapped slug → `<Member … />` (component-less fallback — a self-contained
37
- * member renders itself; a bare-path glyph would need raw `<svg>`, which the
38
- * string-only serializers can't synthesize, so the member tag is the best
39
- * they can emit).
40
- */
41
- function assetJsxForm(type, assetComponents) {
42
- const parsed = parseAssetType(type);
31
+ /** How a placed glyph node prints: the member as its own tag. `null` for a normal component type. */
32
+ function glyphJsxForm(type) {
33
+ const parsed = parseGlyphType(type);
43
34
  if (!parsed) return null;
44
- const component = assetComponents?.[parsed.slug];
45
- if (component) {
46
- const tag = jsxIdentifier(component);
47
- return {
48
- tag,
49
- nameAttr: parsed.member,
50
- importName: tag
51
- };
52
- }
53
35
  return {
54
36
  tag: parsed.member,
55
37
  importName: parsed.member
56
38
  };
57
39
  }
58
40
  //#endregion
59
- export { assetJsxForm, jsxIdentifier, parseAssetType };
41
+ export { glyphJsxForm, jsxIdentifier, parseGlyphType };
@@ -4,8 +4,8 @@ import { Cell, CellSlot } from "./cell.js";
4
4
  import { DocumentRename, RewritableSpec, SpecRewrite, documentRenamesFromPatches, rewriteSpecRefs } from "./rewrite-refs.js";
5
5
  import { applyForcedModifiers, collectForcedModifierProps } from "./apply-forced-modifiers.js";
6
6
  import { AcceptsChildNodes, ChildNodesClassification, ClassifyChildNodes, buildAcceptsChildNodes, buildClassifyChildNodes, stripInjectedSlots, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots } from "./empty-node-slots.js";
7
- import { AssetComponentMap, AssetJsxForm, AssetNodeResolver, assetJsxForm, jsxIdentifier, parseAssetType } from "./asset-jsx.js";
8
7
  import { CollapseTextLabelsResult, CollapsedTextLabel, collapseWrappedTextLabels } from "./collapse-text-labels.js";
8
+ import { GlyphJsxForm, GlyphNodeResolver, glyphJsxForm, jsxIdentifier, parseGlyphType } from "./glyph-jsx.js";
9
9
  import { JsxToSpecOptions, UNRESOLVED, hasUnresolvedProps, jsxToSpec, stripUnresolvedProps } from "./jsxToSpec.js";
10
10
  import { SpecToJsxOptions, SpecToJsxResult, specToJsx } from "./specToJsx.js";
11
11
 
@@ -28,4 +28,4 @@ import { SpecToJsxOptions, SpecToJsxResult, specToJsx } from "./specToJsx.js";
28
28
  * element, without changing the rendered layer hierarchy. */
29
29
  declare const SYNTHETIC_TEXT_SLOT_PROP = "data-uds-synthetic-text-slot";
30
30
  //#endregion
31
- export { type AcceptsChildNodes, type AssetComponentMap, type AssetJsxForm, type AssetNodeResolver, type Cell, type CellSlot, type ChildNodesClassification, type ClassifyChildNodes, type CollapseTextLabelsResult, type CollapsedTextLabel, type DocumentRename, type GraftableElement, type GraftableSpec, type JsxToSpecOptions, type PreviewElement, type PreviewRef, type PreviewSpecNode, type PreviewValue, type RewritableSpec, SYNTHETIC_TEXT_SLOT_PROP, type Slot, type SlotCell, type SpecRewrite, type SpecToJsxOptions, type SpecToJsxResult, UNRESOLVED, applyForcedModifiers, assetJsxForm, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, documentRenamesFromPatches, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, parseAssetType, previewElementNodes, previewElementType, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
31
+ export { type AcceptsChildNodes, type Cell, type CellSlot, type ChildNodesClassification, type ClassifyChildNodes, type CollapseTextLabelsResult, type CollapsedTextLabel, type DocumentRename, type GlyphJsxForm, type GlyphNodeResolver, type GraftableElement, type GraftableSpec, type JsxToSpecOptions, type PreviewElement, type PreviewRef, type PreviewSpecNode, type PreviewValue, type RewritableSpec, SYNTHETIC_TEXT_SLOT_PROP, type Slot, type SlotCell, type SpecRewrite, type SpecToJsxOptions, type SpecToJsxResult, UNRESOLVED, applyForcedModifiers, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, documentRenamesFromPatches, glyphJsxForm, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, parseGlyphType, previewElementNodes, previewElementType, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
@@ -1,5 +1,5 @@
1
1
  import { graftPreviewElements, graftSlotContent, hasPreviewElementDeep, isComponentRef, isPreviewElement, isPreviewRef, previewElementNodes, previewElementType } from "./preview-elements.js";
2
- import { assetJsxForm, jsxIdentifier, parseAssetType } from "./asset-jsx.js";
2
+ import { glyphJsxForm, jsxIdentifier, parseGlyphType } from "./glyph-jsx.js";
3
3
  import { applyForcedModifiers, collectForcedModifierProps } from "./apply-forced-modifiers.js";
4
4
  import { collapseWrappedTextLabels } from "./collapse-text-labels.js";
5
5
  import { buildAcceptsChildNodes, buildClassifyChildNodes, stripInjectedSlots, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots } from "./empty-node-slots.js";
@@ -25,4 +25,4 @@ import { specToJsx } from "./specToJsx.js";
25
25
  * element, without changing the rendered layer hierarchy. */
26
26
  const SYNTHETIC_TEXT_SLOT_PROP = "data-uds-synthetic-text-slot";
27
27
  //#endregion
28
- export { SYNTHETIC_TEXT_SLOT_PROP, UNRESOLVED, applyForcedModifiers, assetJsxForm, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, documentRenamesFromPatches, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, parseAssetType, previewElementNodes, previewElementType, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
28
+ export { SYNTHETIC_TEXT_SLOT_PROP, UNRESOLVED, applyForcedModifiers, buildAcceptsChildNodes, buildClassifyChildNodes, collapseWrappedTextLabels, collectForcedModifierProps, documentRenamesFromPatches, glyphJsxForm, graftPreviewElements, graftSlotContent, hasPreviewElementDeep, hasUnresolvedProps, isComponentRef, isPreviewElement, isPreviewRef, jsxIdentifier, jsxToSpec, parseGlyphType, previewElementNodes, previewElementType, rewriteSpecRefs, specToJsx, stripInjectedSlots, stripUnresolvedProps, withContainerChildSlots, withEmptyNodeSlots, withRenderSlots };
@@ -1,4 +1,4 @@
1
- import { AssetNodeResolver } from "./asset-jsx.js";
1
+ import { GlyphNodeResolver } from "./glyph-jsx.js";
2
2
  import { Spec } from "@json-render/core";
3
3
 
4
4
  //#region src/spec/jsxToSpec.d.ts
@@ -31,12 +31,18 @@ interface JsxToSpecOptions {
31
31
  */
32
32
  knownComponents?: ReadonlyMap<string, string>;
33
33
  /**
34
- * Reconstructs placed asset nodes from a rendering-component tag —
34
+ * Reconstructs placed glyph nodes from a bare member tag —
35
35
  * `<Icon name="Trophy" …/>` → type `icon:icons/Trophy`. The exact inverse of
36
- * `specToJsx`'s `assetComponents` mapping; without it an `<Icon name=…/>`
36
+ * `specToJsx`'s `glyphJsx` mapping; without it an `<Icon name=…/>`
37
37
  * round-trips as a literal `Icon` component node.
38
38
  */
39
- resolveAssetNode?: AssetNodeResolver;
39
+ resolveGlyphNode?: GlyphNodeResolver;
40
+ /**
41
+ * Component type → prop → the icon libraries its values come from, the same map `specToJsx`
42
+ * prints from. An identifier under such a prop (`name={StarIcon}`) is the member's leaf in the
43
+ * spec, whichever of the libraries holds it.
44
+ */
45
+ glyphProps?: Readonly<Record<string, Readonly<Record<string, readonly string[]>>>>;
40
46
  }
41
47
  /**
42
48
  * Convert JSX source code into a json-render Spec.
@@ -303,6 +303,12 @@ function collectTextChildren(children) {
303
303
  function hasElementChildren(children) {
304
304
  return children.some((c) => c.type === "JSXElement" || c.type === "JSXFragment");
305
305
  }
306
+ /** The name a `{Identifier}` attribute value spells, or `undefined` for any other value. */
307
+ function bareIdentifier(valueNode) {
308
+ if (valueNode?.type !== "JSXExpressionContainer") return void 0;
309
+ const expr = valueNode.expression;
310
+ return expr.type === "Identifier" ? expr.name : void 0;
311
+ }
306
312
  function nextKey(ctx) {
307
313
  return `el${ctx.counter++}`;
308
314
  }
@@ -371,6 +377,8 @@ function convertElement(node, ctx) {
371
377
  const key = nextKey(ctx);
372
378
  const props = {};
373
379
  const slots = {};
380
+ const outerHost = ctx.host;
381
+ ctx.host = ctx.knownComponents?.get(tagName) ?? null;
374
382
  const onBindings = {};
375
383
  for (const attr of node.openingElement.attributes) if (attr.type === "JSXAttribute") {
376
384
  const jsxAttr = attr;
@@ -385,6 +393,12 @@ function convertElement(node, ctx) {
385
393
  slots[propName] = [propValue[SLOT_FILL]];
386
394
  continue;
387
395
  }
396
+ const glyph = bareIdentifier(jsxAttr.value);
397
+ const libraries = ctx.glyphProps?.[ctx.knownComponents?.get(tagName) ?? tagName]?.[propName];
398
+ if (propValue === UNRESOLVED && glyph !== void 0 && libraries !== void 0 && libraries.length > 0) {
399
+ props[propName] = glyph;
400
+ continue;
401
+ }
388
402
  props[propName] = propValue;
389
403
  } else if (attr.type === "JSXSpreadAttribute") props.__spread__ = UNRESOLVED;
390
404
  const childKeys = [];
@@ -394,10 +408,11 @@ function convertElement(node, ctx) {
394
408
  const childKey = convertJsxNode(child, ctx);
395
409
  if (childKey) childKeys.push(childKey);
396
410
  }
397
- const assetType = ctx.resolveAssetNode?.(tagName, props) ?? null;
398
- if (assetType) delete props.name;
411
+ ctx.host = outerHost;
412
+ const glyphType = ctx.resolveGlyphNode?.(tagName, props, outerHost) ?? null;
413
+ if (glyphType) delete props.name;
399
414
  const element = {
400
- type: assetType ?? ctx.knownComponents?.get(tagName) ?? tagName,
415
+ type: glyphType ?? ctx.knownComponents?.get(tagName) ?? tagName,
401
416
  props
402
417
  };
403
418
  if (childKeys.length > 0) element.children = childKeys;
@@ -504,7 +519,9 @@ function jsxToSpec(source, options) {
504
519
  scope,
505
520
  inlining: /* @__PURE__ */ new Set(),
506
521
  knownComponents: options?.knownComponents ?? null,
507
- resolveAssetNode: options?.resolveAssetNode ?? null
522
+ resolveGlyphNode: options?.resolveGlyphNode ?? null,
523
+ host: null,
524
+ glyphProps: options?.glyphProps ?? null
508
525
  };
509
526
  const rootKey = convertJsxNode(jsxRoot, ctx);
510
527
  if (!rootKey) return null;