@sigx/lynx-icons 0.4.0 → 0.4.2

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/Icon.d.ts CHANGED
@@ -1,6 +1,27 @@
1
1
  import { type Define } from '@sigx/lynx';
2
2
  import '@sigx/lynx-icons/__font-face.css';
3
- export type IconProps = Define.Prop<'set', string, true> & Define.Prop<'name', string, true> & Define.Prop<'size', number, false> & Define.Prop<'color', string, false> & Define.Prop<'class', string, false>;
3
+ import type { IconColorResolver, IconPropsExtensions } from './types.js';
4
+ /**
5
+ * Injectable for the active theme's color resolver. Receives the full
6
+ * `<Icon>` props (including theme-augmented fields like daisy's
7
+ * `variant`) and returns a CSS color value to substitute into the SVG
8
+ * `fill=`, or `undefined` to fall through.
9
+ *
10
+ * Themes provide this via `defineProvide(useIconColorResolver, …)` from
11
+ * inside a provider component (e.g. daisy's `<ThemeProvider>`). Core
12
+ * `<Icon>` has no concept of named variants — it just asks the resolver
13
+ * for a color.
14
+ */
15
+ export declare const useIconColorResolver: import("@sigx/runtime-core").InjectableFunction<IconColorResolver | null>;
16
+ export type IconProps = Define.Prop<'set', string, true> & Define.Prop<'name', string, true> & Define.Prop<'size', number, false> & Define.Prop<'color', string, false> & Define.Prop<'class', string, false>
17
+ /**
18
+ * Augmentation point — theme packages declaration-merge into
19
+ * `IconPropsExtensions` to add their own typed props (e.g. daisy
20
+ * adds `variant?: DaisyColor`). Core declares no specific
21
+ * extensions, so without a theme installed, no extra props exist
22
+ * and the type system rejects `<Icon variant="…">` at compile time.
23
+ */
24
+ & IconPropsExtensions;
4
25
  export declare function sanitizeColor(color: string): string;
5
26
  export declare function inlineSvg(template: string, color: string): string;
6
27
  /**
package/dist/Icon.js ADDED
@@ -0,0 +1,111 @@
1
+ import { jsx as _jsx } from "@sigx/lynx/jsx-runtime";
2
+ import { component, defineInjectable } from '@sigx/lynx';
3
+ import { codepoints } from '@sigx/lynx-icons/__codepoints';
4
+ import { svgs } from '@sigx/lynx-icons/__svgs';
5
+ import '@sigx/lynx-icons/__font-face.css';
6
+ import { lookupGlyph } from './registry.js';
7
+ /**
8
+ * Injectable for the active theme's color resolver. Receives the full
9
+ * `<Icon>` props (including theme-augmented fields like daisy's
10
+ * `variant`) and returns a CSS color value to substitute into the SVG
11
+ * `fill=`, or `undefined` to fall through.
12
+ *
13
+ * Themes provide this via `defineProvide(useIconColorResolver, …)` from
14
+ * inside a provider component (e.g. daisy's `<ThemeProvider>`). Core
15
+ * `<Icon>` has no concept of named variants — it just asks the resolver
16
+ * for a color.
17
+ */
18
+ export const useIconColorResolver = defineInjectable(() => null);
19
+ /**
20
+ * Match a conservative subset of valid CSS color formats:
21
+ * - `currentColor` and named colors (`red`, `dodgerblue`, `transparent` …)
22
+ * - `#rgb` / `#rgba` / `#rrggbb` / `#rrggbbaa`
23
+ * - `rgb(...)` / `rgba(...)` / `hsl(...)` / `hsla(...)` with digits, dots,
24
+ * commas, percent signs, and whitespace inside the parens
25
+ * - `var(--name)` referencing a CSS custom property — the only way to
26
+ * plumb theme tokens (`var(--color-primary)`) into the SVG `fill=`
27
+ * attribute, since Lynx's `<svg content=…>` parses the SVG string as
28
+ * a standalone fragment that doesn't inherit `color` from the host
29
+ * element (so `fill="currentColor"` + a host-level class is a no-op).
30
+ *
31
+ * Anything else (quotes, angle brackets, ampersands, arbitrary HTML) falls
32
+ * back to `currentColor`. The `color` prop ends up substituted directly into
33
+ * the SVG markup that we hand to Lynx's `<svg content={…}>` parser, so a
34
+ * value like `red" stroke="…` would break out of the `fill=""` attribute.
35
+ * Allow-list, not escape-list — keeps the surface area provably small.
36
+ */
37
+ const SAFE_COLOR_RE = /^(?:currentColor|[a-zA-Z]+|#[0-9a-fA-F]{3,8}|(?:rgb|rgba|hsl|hsla)\(\s*[\d.,%\s]+\)|var\(\s*--[\w-]+\s*\))$/;
38
+ export function sanitizeColor(color) {
39
+ return SAFE_COLOR_RE.test(color) ? color : 'currentColor';
40
+ }
41
+ export function inlineSvg(template, color) {
42
+ return template.replace(/__COLOR__/g, sanitizeColor(color));
43
+ }
44
+ /**
45
+ * Render a glyph from a registered icon set.
46
+ *
47
+ * Sets are declared in `signalx.config.ts` via `iconSets: [...]` (build-time,
48
+ * tree-shaken) or via `defineIconSet({ id, glyphs })` (runtime, ad-hoc).
49
+ *
50
+ * Resolution order: **SVG first**, then codepoint, then missing placeholder.
51
+ *
52
+ * - SVG-mode sets render with Lynx's native `<svg content={...}>` element
53
+ * (the engine parses the inline XML; no JSX children, no data: URIs).
54
+ * - Font-mode sets fall back to a single character inside a `<text>` element
55
+ * with a matching `font-family`. The plugin only emits codepoints when the
56
+ * matching `@font-face` has also been registered (v1.1+); v1 always hits
57
+ * the SVG branch.
58
+ * - Missing glyphs render an empty `<view>` of the same size so layout
59
+ * doesn't jump.
60
+ *
61
+ * @example
62
+ * ```tsx
63
+ * <Icon set="fa" name="user" size={20} color="#333" />
64
+ * <Icon set="lucide" name="search" size={16} />
65
+ * ```
66
+ */
67
+ export const Icon = component(({ props }) => {
68
+ // Resolve the active theme's color resolver once at setup. The
69
+ // resolver itself is a stable function reference; it reads the
70
+ // theme-augmented props (e.g. `props.variant` for daisy) and any
71
+ // reactive theme state (e.g. `theme.name`) on each call.
72
+ const resolveColor = useIconColorResolver();
73
+ return () => {
74
+ const size = props.size ?? 16;
75
+ const set = props.set;
76
+ const name = props.name;
77
+ const sizeStyle = { width: size, height: size };
78
+ // Resolution order: explicit `props.color` wins → then the
79
+ // theme resolver's return (driven by augmented props like
80
+ // `variant`) → finally `currentColor`. Substituted directly
81
+ // into the SVG `fill=` attribute by `inlineSvg`; class is not
82
+ // used to convey color because Lynx's `<svg content=…>` parses
83
+ // the SVG string in isolation and doesn't inherit host CSS
84
+ // `color`.
85
+ //
86
+ // `props` is cast to the unknown-record shape the resolver
87
+ // signature expects — at compile time the augmented fields are
88
+ // typed correctly inside the resolver itself (where the theme
89
+ // package's augmentation is in scope).
90
+ const themeColor = resolveColor
91
+ ? resolveColor(props)
92
+ : undefined;
93
+ const color = props.color ?? themeColor ?? 'currentColor';
94
+ const glyph = lookupGlyph(codepoints, svgs, set, name);
95
+ if (glyph?.svg) {
96
+ return (_jsx("svg", { content: inlineSvg(glyph.svg.svg, color), class: props.class, style: sizeStyle }));
97
+ }
98
+ if (glyph?.codepoint !== undefined) {
99
+ const textStyle = {
100
+ fontFamily: set,
101
+ fontSize: size,
102
+ lineHeight: `${size}px`,
103
+ width: size,
104
+ height: size,
105
+ color,
106
+ };
107
+ return (_jsx("text", { class: props.class, style: textStyle, children: String.fromCodePoint(glyph.codepoint) }));
108
+ }
109
+ return _jsx("view", { class: props.class, style: sizeStyle });
110
+ };
111
+ });
@@ -1,4 +1,4 @@
1
- import type { IconSetDef } from './types';
1
+ import type { IconSetDef } from './types.js';
2
2
  /**
3
3
  * Register a custom icon set at module load time.
4
4
  *
@@ -24,4 +24,4 @@ import type { IconSetDef } from './types';
24
24
  * markup get substituted with the user's `color` prop at render time.
25
25
  */
26
26
  export declare function defineIconSet(def: IconSetDef): IconSetDef;
27
- export type { IconSetDef } from './types';
27
+ export type { IconSetDef } from './types.js';
@@ -1,2 +1,29 @@
1
- import { t as e } from "./defineIconSet-BIFjZq08.js";
2
- export { e as defineIconSet };
1
+ import { registerIconSet } from './registry.js';
2
+ /**
3
+ * Register a custom icon set at module load time.
4
+ *
5
+ * Build-time sets declared in `signalx.config.ts` are auto-detected and
6
+ * tree-shaken; `defineIconSet` is the escape hatch for ad-hoc sets defined
7
+ * directly in app code (e.g. a small private set used in one screen).
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * defineIconSet({
12
+ * id: 'brand',
13
+ * glyphs: {
14
+ * logo: {
15
+ * svg: {
16
+ * svg: '<svg viewBox="0 0 24 24" fill="__COLOR__"><path d="M3 12L12 3l9 9-9 9z"/></svg>',
17
+ * },
18
+ * },
19
+ * },
20
+ * });
21
+ * ```
22
+ *
23
+ * The inner `svg` value is raw SVG markup. `__COLOR__` placeholders in that
24
+ * markup get substituted with the user's `color` prop at render time.
25
+ */
26
+ export function defineIconSet(def) {
27
+ registerIconSet(def);
28
+ return def;
29
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { Icon } from './Icon';
2
- export type { IconProps } from './Icon';
3
- export { defineIconSet } from './defineIconSet';
4
- export type { GlyphData, GlyphSvg, IconAdapter, IconSetDef } from './types';
1
+ export { Icon, useIconColorResolver } from './Icon.js';
2
+ export type { IconProps } from './Icon.js';
3
+ export { defineIconSet } from './defineIconSet.js';
4
+ export type { GlyphData, GlyphSvg, IconAdapter, IconColorResolver, IconPropsExtensions, IconSetDef, IconSpec, } from './types.js';
package/dist/index.js CHANGED
@@ -1,50 +1,2 @@
1
- import { n as e, t } from "./defineIconSet-BIFjZq08.js";
2
- import { component as n } from "@sigx/lynx";
3
- import { codepoints as r } from "@sigx/lynx-icons/__codepoints";
4
- import { svgs as i } from "@sigx/lynx-icons/__svgs";
5
- import "@sigx/lynx-icons/__font-face.css";
6
- import { jsx as a } from "@sigx/lynx/jsx-runtime";
7
- //#region src/Icon.tsx
8
- var o = /^(?:currentColor|[a-zA-Z]+|#[0-9a-fA-F]{3,8}|(?:rgb|rgba|hsl|hsla)\(\s*[\d.,%\s]+\))$/;
9
- function s(e) {
10
- return o.test(e) ? e : "currentColor";
11
- }
12
- function c(e, t) {
13
- return e.replace(/__COLOR__/g, s(t));
14
- }
15
- var l = n(({ props: t }) => () => {
16
- let n = t.size ?? 16, o = t.set, s = t.name, l = {
17
- width: n,
18
- height: n
19
- }, u = e(r, i, o, s);
20
- if (u?.svg) {
21
- let e = t.color ?? "currentColor";
22
- return /* @__PURE__ */ a("svg", {
23
- content: c(u.svg.svg, e),
24
- class: t.class,
25
- style: l
26
- });
27
- }
28
- if (u?.codepoint !== void 0) {
29
- let e = {
30
- fontFamily: o,
31
- fontSize: n,
32
- lineHeight: `${n}px`,
33
- width: n,
34
- height: n
35
- };
36
- return t.color && (e.color = t.color), /* @__PURE__ */ a("text", {
37
- class: t.class,
38
- style: e,
39
- children: String.fromCodePoint(u.codepoint)
40
- });
41
- }
42
- return /* @__PURE__ */ a("view", {
43
- class: t.class,
44
- style: l
45
- });
46
- });
47
- //#endregion
48
- export { l as Icon, t as defineIconSet };
49
-
50
- //# sourceMappingURL=index.js.map
1
+ export { Icon, useIconColorResolver } from './Icon.js';
2
+ export { defineIconSet } from './defineIconSet.js';
@@ -1,4 +1,4 @@
1
- import type { CodepointMap, GlyphSvg, IconSetDef, SvgMap } from './types';
1
+ import type { CodepointMap, GlyphSvg, IconSetDef, SvgMap } from './types.js';
2
2
  export declare function registerIconSet(def: IconSetDef): void;
3
3
  export declare function lookupCodepoint(buildTime: CodepointMap, set: string, name: string): number | undefined;
4
4
  export declare function lookupSvg(buildTime: SvgMap, set: string, name: string): GlyphSvg | undefined;
@@ -0,0 +1,31 @@
1
+ const runtimeCodepoints = {};
2
+ const runtimeSvgs = {};
3
+ export function registerIconSet(def) {
4
+ const codepoints = {};
5
+ const svgs = {};
6
+ for (const [name, g] of Object.entries(def.glyphs)) {
7
+ if (g.codepoint !== undefined)
8
+ codepoints[name] = g.codepoint;
9
+ if (g.svg)
10
+ svgs[name] = g.svg;
11
+ }
12
+ if (Object.keys(codepoints).length > 0)
13
+ runtimeCodepoints[def.id] = codepoints;
14
+ if (Object.keys(svgs).length > 0)
15
+ runtimeSvgs[def.id] = svgs;
16
+ }
17
+ export function lookupCodepoint(buildTime, set, name) {
18
+ return buildTime[set]?.[name] ?? runtimeCodepoints[set]?.[name];
19
+ }
20
+ export function lookupSvg(buildTime, set, name) {
21
+ return buildTime[set]?.[name] ?? runtimeSvgs[set]?.[name];
22
+ }
23
+ export function lookupGlyph(buildTimeCodepoints, buildTimeSvgs, set, name) {
24
+ const svg = lookupSvg(buildTimeSvgs, set, name);
25
+ if (svg)
26
+ return { svg };
27
+ const codepoint = lookupCodepoint(buildTimeCodepoints, set, name);
28
+ if (codepoint !== undefined)
29
+ return { codepoint };
30
+ return undefined;
31
+ }
@@ -1,4 +1,4 @@
1
- import type { CodepointMap } from '../types';
1
+ import type { CodepointMap } from '../types.js';
2
2
  /**
3
3
  * Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an
4
4
  * Rspack alias at build time, populating it with the codepoints used by the
@@ -1,6 +1,8 @@
1
- //#region src/stubs/codepoints.ts
2
- var e = {};
3
- //#endregion
4
- export { e as codepoints };
5
-
6
- //# sourceMappingURL=codepoints.js.map
1
+ /**
2
+ * Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an
3
+ * Rspack alias at build time, populating it with the codepoints used by the
4
+ * app's `<Icon>` calls. Without the plugin, no built-in sets are registered
5
+ * and all icons fall back to `defineIconSet`-registered sets (if any) or the
6
+ * missing-glyph placeholder.
7
+ */
8
+ export const codepoints = {};
@@ -1,4 +1,4 @@
1
- import type { CodepointMap } from '../types';
1
+ import type { CodepointMap } from '../types.js';
2
2
 
3
3
  /**
4
4
  * Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an
@@ -1,4 +1,4 @@
1
- import type { SvgMap } from '../types';
1
+ import type { SvgMap } from '../types.js';
2
2
  /**
3
3
  * Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an
4
4
  * Rspack alias at build time, populating it with the SVG path data for the
@@ -1,6 +1,6 @@
1
- //#region src/stubs/svgs.ts
2
- var e = {};
3
- //#endregion
4
- export { e as svgs };
5
-
6
- //# sourceMappingURL=svgs.js.map
1
+ /**
2
+ * Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an
3
+ * Rspack alias at build time, populating it with the SVG path data for the
4
+ * icons used by the app's `<Icon>` calls.
5
+ */
6
+ export const svgs = {};
@@ -1,4 +1,4 @@
1
- import type { SvgMap } from '../types';
1
+ import type { SvgMap } from '../types.js';
2
2
 
3
3
  /**
4
4
  * Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an
package/dist/types.d.ts CHANGED
@@ -1,3 +1,61 @@
1
+ /**
2
+ * Augmentation point for theme / extension packages. Declaration-merge
3
+ * into this interface to add fields that `<Icon>` accepts beyond the
4
+ * core props. The pinned components in adapter packages and the daisy
5
+ * navigation primitives inherit the augmentation automatically via
6
+ * intersection.
7
+ *
8
+ * `@sigx/lynx-icons` deliberately knows nothing about themes — the
9
+ * extension shape is up to whatever theme is installed. Daisy adds a
10
+ * `variant?: DaisyColor` field; a custom theme could add anything else
11
+ * (`tone?: 'muted' | 'loud'`, `tint?: number`, …) and provide its own
12
+ * resolver.
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // In a theme package:
17
+ * declare module '@sigx/lynx-icons' {
18
+ * interface IconPropsExtensions {
19
+ * variant?: 'primary' | 'secondary' | 'accent';
20
+ * }
21
+ * }
22
+ * ```
23
+ */
24
+ export interface IconPropsExtensions {
25
+ }
26
+ /**
27
+ * Function form provided to `useIconColorResolver`. Receives the full
28
+ * `<Icon>` props (including whatever fields the active theme augmented
29
+ * onto `IconPropsExtensions`) and returns a **CSS color value**
30
+ * substituted into the rendered SVG's `fill=` attribute. Return
31
+ * `undefined` to fall through to `props.color` / `currentColor`.
32
+ *
33
+ * Color rather than class: Lynx's `<svg content=…>` parses the inline
34
+ * SVG markup as a standalone fragment that doesn't inherit `color` CSS
35
+ * from the host element. Substituting the value into the markup is the
36
+ * only reliable way to make theme tokens show through.
37
+ *
38
+ * Props rather than a single variant string: the core has no opinion on
39
+ * how a theme keys its colors. A theme picks whichever augmented field
40
+ * (or combination) makes sense and reads it from `props` itself.
41
+ */
42
+ export type IconColorResolver = (props: Readonly<Record<string, unknown>>) => string | undefined;
43
+ /**
44
+ * Lightweight `{ set, name }` reference to an icon in a registered set —
45
+ * the canonical "data" form a consumer can pass to a UI primitive that
46
+ * accepts an icon (e.g. `<Tabs.Screen icon={{ set: 'lucide', name: 'map' }}>`).
47
+ *
48
+ * The receiving component (e.g. `<NavTabBar>`, `<NavHeader>`) is responsible
49
+ * for turning the spec into rendered JSX — typically by composing
50
+ * `<Icon set={spec.set} name={spec.name} color="currentColor" size={…}>`
51
+ * with theme-aware wrapping. Consumers who want full control (custom
52
+ * color, third-party component, size override) pass JSX directly instead
53
+ * of a spec.
54
+ */
55
+ export interface IconSpec {
56
+ readonly set: string;
57
+ readonly name: string;
58
+ }
1
59
  /**
2
60
  * Per-glyph vector data. Used by SVG-mode rendering.
3
61
  *
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigx/lynx-icons",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Icon component and registry for sigx-lynx — works with adapter packages like @sigx/lynx-icons-fa-free and @sigx/lynx-icons-lucide.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -35,12 +35,12 @@
35
35
  "LICENSE"
36
36
  ],
37
37
  "peerDependencies": {
38
- "@sigx/lynx": "^0.4.0"
38
+ "@sigx/lynx": "^0.4.2"
39
39
  },
40
40
  "devDependencies": {
41
- "@sigx/vite": "^0.4.3",
41
+ "@typescript/native-preview": "7.0.0-dev.20260521.1",
42
42
  "typescript": "^6.0.3",
43
- "@sigx/lynx": "^0.4.0"
43
+ "@sigx/lynx": "^0.4.2"
44
44
  },
45
45
  "author": "Andreas Ekdahl",
46
46
  "license": "MIT",
@@ -66,7 +66,8 @@
66
66
  "lucide"
67
67
  ],
68
68
  "scripts": {
69
- "build": "vite build && tsgo --emitDeclarationOnly && node ../../scripts/copy-assets.mjs src/stubs dist/stubs",
70
- "dev": "vite build --watch"
69
+ "build": "node ../../scripts/clean.mjs dist && tsgo && node ../../scripts/copy-assets.mjs src/stubs dist/stubs",
70
+ "dev": "tsgo --watch",
71
+ "clean": "node ../../scripts/clean.mjs dist .turbo"
71
72
  }
72
73
  }
@@ -1,28 +0,0 @@
1
- //#region src/registry.ts
2
- var e = {}, t = {};
3
- function n(n) {
4
- let r = {}, i = {};
5
- for (let [e, t] of Object.entries(n.glyphs)) t.codepoint !== void 0 && (r[e] = t.codepoint), t.svg && (i[e] = t.svg);
6
- Object.keys(r).length > 0 && (e[n.id] = r), Object.keys(i).length > 0 && (t[n.id] = i);
7
- }
8
- function r(t, n, r) {
9
- return t[n]?.[r] ?? e[n]?.[r];
10
- }
11
- function i(e, n, r) {
12
- return e[n]?.[r] ?? t[n]?.[r];
13
- }
14
- function a(e, t, n, a) {
15
- let o = i(t, n, a);
16
- if (o) return { svg: o };
17
- let s = r(e, n, a);
18
- if (s !== void 0) return { codepoint: s };
19
- }
20
- //#endregion
21
- //#region src/defineIconSet.ts
22
- function o(e) {
23
- return n(e), e;
24
- }
25
- //#endregion
26
- export { a as n, o as t };
27
-
28
- //# sourceMappingURL=defineIconSet-BIFjZq08.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"defineIconSet-BIFjZq08.js","names":[],"sources":["../src/registry.ts","../src/defineIconSet.ts"],"sourcesContent":["import type { CodepointMap, GlyphSvg, IconSetDef, SvgMap } from './types';\n\nconst runtimeCodepoints: CodepointMap = {};\nconst runtimeSvgs: SvgMap = {};\n\nexport function registerIconSet(def: IconSetDef): void {\n const codepoints: Record<string, number> = {};\n const svgs: Record<string, GlyphSvg> = {};\n for (const [name, g] of Object.entries(def.glyphs)) {\n if (g.codepoint !== undefined) codepoints[name] = g.codepoint;\n if (g.svg) svgs[name] = g.svg;\n }\n if (Object.keys(codepoints).length > 0) runtimeCodepoints[def.id] = codepoints;\n if (Object.keys(svgs).length > 0) runtimeSvgs[def.id] = svgs;\n}\n\nexport function lookupCodepoint(\n buildTime: CodepointMap,\n set: string,\n name: string,\n): number | undefined {\n return buildTime[set]?.[name] ?? runtimeCodepoints[set]?.[name];\n}\n\nexport function lookupSvg(\n buildTime: SvgMap,\n set: string,\n name: string,\n): GlyphSvg | undefined {\n return buildTime[set]?.[name] ?? runtimeSvgs[set]?.[name];\n}\n\nexport function lookupGlyph(\n buildTimeCodepoints: CodepointMap,\n buildTimeSvgs: SvgMap,\n set: string,\n name: string,\n): { codepoint?: number; svg?: GlyphSvg } | undefined {\n const svg = lookupSvg(buildTimeSvgs, set, name);\n if (svg) return { svg };\n\n const codepoint = lookupCodepoint(buildTimeCodepoints, set, name);\n if (codepoint !== undefined) return { codepoint };\n\n return undefined;\n}\n","import { registerIconSet } from './registry';\nimport type { IconSetDef } from './types';\n\n/**\n * Register a custom icon set at module load time.\n *\n * Build-time sets declared in `signalx.config.ts` are auto-detected and\n * tree-shaken; `defineIconSet` is the escape hatch for ad-hoc sets defined\n * directly in app code (e.g. a small private set used in one screen).\n *\n * @example\n * ```ts\n * defineIconSet({\n * id: 'brand',\n * glyphs: {\n * logo: {\n * svg: {\n * svg: '<svg viewBox=\"0 0 24 24\" fill=\"__COLOR__\"><path d=\"M3 12L12 3l9 9-9 9z\"/></svg>',\n * },\n * },\n * },\n * });\n * ```\n *\n * The inner `svg` value is raw SVG markup. `__COLOR__` placeholders in that\n * markup get substituted with the user's `color` prop at render time.\n */\nexport function defineIconSet(def: IconSetDef): IconSetDef {\n registerIconSet(def);\n return def;\n}\n\nexport type { IconSetDef } from './types';\n"],"mappings":";AAEA,IAAM,IAAkC,EAAE,EACpC,IAAsB,EAAE;AAE9B,SAAgB,EAAgB,GAAuB;CACnD,IAAM,IAAqC,EAAE,EACvC,IAAiC,EAAE;CACzC,KAAK,IAAM,CAAC,GAAM,MAAM,OAAO,QAAQ,EAAI,OAAO,EAE9C,AADI,EAAE,cAAc,KAAA,MAAW,EAAW,KAAQ,EAAE,YAChD,EAAE,QAAK,EAAK,KAAQ,EAAE;CAG9B,AADI,OAAO,KAAK,EAAW,CAAC,SAAS,MAAG,EAAkB,EAAI,MAAM,IAChE,OAAO,KAAK,EAAK,CAAC,SAAS,MAAG,EAAY,EAAI,MAAM;;AAG5D,SAAgB,EACZ,GACA,GACA,GACkB;CAClB,OAAO,EAAU,KAAO,MAAS,EAAkB,KAAO;;AAG9D,SAAgB,EACZ,GACA,GACA,GACoB;CACpB,OAAO,EAAU,KAAO,MAAS,EAAY,KAAO;;AAGxD,SAAgB,EACZ,GACA,GACA,GACA,GACkD;CAClD,IAAM,IAAM,EAAU,GAAe,GAAK,EAAK;CAC/C,IAAI,GAAK,OAAO,EAAE,QAAK;CAEvB,IAAM,IAAY,EAAgB,GAAqB,GAAK,EAAK;CACjE,IAAI,MAAc,KAAA,GAAW,OAAO,EAAE,cAAW;;;;ACfrD,SAAgB,EAAc,GAA6B;CAEvD,OADA,EAAgB,EAAI,EACb"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/Icon.tsx"],"sourcesContent":["import { component, type Define } from '@sigx/lynx';\nimport { codepoints } from '@sigx/lynx-icons/__codepoints';\nimport { svgs } from '@sigx/lynx-icons/__svgs';\nimport '@sigx/lynx-icons/__font-face.css';\nimport { lookupGlyph } from './registry';\n\nexport type IconProps =\n & Define.Prop<'set', string, true>\n & Define.Prop<'name', string, true>\n & Define.Prop<'size', number, false>\n & Define.Prop<'color', string, false>\n & Define.Prop<'class', string, false>;\n\n/**\n * Match a conservative subset of valid CSS color formats:\n * - `currentColor` and named colors (`red`, `dodgerblue`, `transparent` …)\n * - `#rgb` / `#rgba` / `#rrggbb` / `#rrggbbaa`\n * - `rgb(...)` / `rgba(...)` / `hsl(...)` / `hsla(...)` with digits, dots,\n * commas, percent signs, and whitespace inside the parens\n *\n * Anything else (quotes, angle brackets, ampersands, arbitrary HTML) falls\n * back to `currentColor`. The `color` prop ends up substituted directly into\n * the SVG markup that we hand to Lynx's `<svg content={…}>` parser, so a\n * value like `red\" stroke=\"…` would break out of the `fill=\"\"` attribute.\n * Allow-list, not escape-list — keeps the surface area provably small.\n */\nconst SAFE_COLOR_RE =\n /^(?:currentColor|[a-zA-Z]+|#[0-9a-fA-F]{3,8}|(?:rgb|rgba|hsl|hsla)\\(\\s*[\\d.,%\\s]+\\))$/;\n\nexport function sanitizeColor(color: string): string {\n return SAFE_COLOR_RE.test(color) ? color : 'currentColor';\n}\n\nexport function inlineSvg(template: string, color: string): string {\n return template.replace(/__COLOR__/g, sanitizeColor(color));\n}\n\n/**\n * Render a glyph from a registered icon set.\n *\n * Sets are declared in `signalx.config.ts` via `iconSets: [...]` (build-time,\n * tree-shaken) or via `defineIconSet({ id, glyphs })` (runtime, ad-hoc).\n *\n * Resolution order: **SVG first**, then codepoint, then missing placeholder.\n *\n * - SVG-mode sets render with Lynx's native `<svg content={...}>` element\n * (the engine parses the inline XML; no JSX children, no data: URIs).\n * - Font-mode sets fall back to a single character inside a `<text>` element\n * with a matching `font-family`. The plugin only emits codepoints when the\n * matching `@font-face` has also been registered (v1.1+); v1 always hits\n * the SVG branch.\n * - Missing glyphs render an empty `<view>` of the same size so layout\n * doesn't jump.\n *\n * @example\n * ```tsx\n * <Icon set=\"fa\" name=\"user\" size={20} color=\"#333\" />\n * <Icon set=\"lucide\" name=\"search\" size={16} />\n * ```\n */\nexport const Icon = component<IconProps>(({ props }) => {\n return () => {\n const size = props.size ?? 16;\n const set = props.set;\n const name = props.name;\n const sizeStyle = { width: size, height: size } as const;\n\n const glyph = lookupGlyph(codepoints, svgs, set, name);\n if (glyph?.svg) {\n const color = props.color ?? 'currentColor';\n return (\n <svg\n content={inlineSvg(glyph.svg.svg, color)}\n class={props.class}\n style={sizeStyle}\n />\n );\n }\n\n if (glyph?.codepoint !== undefined) {\n const textStyle: Record<string, string | number> = {\n fontFamily: set,\n fontSize: size,\n lineHeight: `${size}px`,\n width: size,\n height: size,\n };\n if (props.color) textStyle.color = props.color;\n return (\n <text class={props.class} style={textStyle}>\n {String.fromCodePoint(glyph.codepoint)}\n </text>\n );\n }\n\n return <view class={props.class} style={sizeStyle} />;\n };\n});\n"],"mappings":";;;;;;;AA0BA,IAAM,IACF;AAEJ,SAAgB,EAAc,GAAuB;CACjD,OAAO,EAAc,KAAK,EAAM,GAAG,IAAQ;;AAG/C,SAAgB,EAAU,GAAkB,GAAuB;CAC/D,OAAO,EAAS,QAAQ,cAAc,EAAc,EAAM,CAAC;;AA0B/D,IAAa,IAAO,GAAsB,EAAE,qBAC3B;CACT,IAAM,IAAO,EAAM,QAAQ,IACrB,IAAM,EAAM,KACZ,IAAO,EAAM,MACb,IAAY;EAAE,OAAO;EAAM,QAAQ;EAAM,EAEzC,IAAQ,EAAY,GAAY,GAAM,GAAK,EAAK;CACtD,IAAI,GAAO,KAAK;EACZ,IAAM,IAAQ,EAAM,SAAS;EAC7B,OACI,kBAAC,OAAD;GACI,SAAS,EAAU,EAAM,IAAI,KAAK,EAAM;GACxC,OAAO,EAAM;GACb,OAAO;GACT,CAAA;;CAIV,IAAI,GAAO,cAAc,KAAA,GAAW;EAChC,IAAM,IAA6C;GAC/C,YAAY;GACZ,UAAU;GACV,YAAY,GAAG,EAAK;GACpB,OAAO;GACP,QAAQ;GACX;EAED,OADI,EAAM,UAAO,EAAU,QAAQ,EAAM,QAErC,kBAAC,QAAD;GAAM,OAAO,EAAM;GAAO,OAAO;aAC5B,OAAO,cAAc,EAAM,UAAU;GACnC,CAAA;;CAIf,OAAO,kBAAC,QAAD;EAAM,OAAO,EAAM;EAAO,OAAO;EAAa,CAAA;EAE3D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"codepoints.js","names":[],"sources":["../../src/stubs/codepoints.ts"],"sourcesContent":["import type { CodepointMap } from '../types';\n\n/**\n * Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an\n * Rspack alias at build time, populating it with the codepoints used by the\n * app's `<Icon>` calls. Without the plugin, no built-in sets are registered\n * and all icons fall back to `defineIconSet`-registered sets (if any) or the\n * missing-glyph placeholder.\n */\nexport const codepoints: CodepointMap = {};\n"],"mappings":";AASA,IAAa,IAA2B,EAAE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"svgs.js","names":[],"sources":["../../src/stubs/svgs.ts"],"sourcesContent":["import type { SvgMap } from '../types';\n\n/**\n * Empty stub. The @sigx/lynx-plugin icons slice replaces this module via an\n * Rspack alias at build time, populating it with the SVG path data for the\n * icons used by the app's `<Icon>` calls.\n */\nexport const svgs: SvgMap = {};\n"],"mappings":";AAOA,IAAa,IAAe,EAAE"}