@playgenx/components 0.1.0 → 0.2.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.
- package/dist/index.d.mts +328 -16
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +576 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -54,12 +54,37 @@ interface SliderProps {
|
|
|
54
54
|
step?: number;
|
|
55
55
|
/** Optional caption rendered above the slider. */
|
|
56
56
|
label?: string;
|
|
57
|
+
/**
|
|
58
|
+
* When set, the slider is "bound" to the playground state: initial
|
|
59
|
+
* value comes from state.get(stateKey), onChange writes state.set.
|
|
60
|
+
* `value` prop is ignored when stateKey is present.
|
|
61
|
+
*/
|
|
62
|
+
stateKey?: string;
|
|
57
63
|
}
|
|
58
64
|
/**
|
|
59
|
-
* Range input.
|
|
60
|
-
*
|
|
65
|
+
* Range input. Three operating modes:
|
|
66
|
+
*
|
|
67
|
+
* 1. **Uncontrolled** (no `value`, no `stateKey`): the slider owns
|
|
68
|
+
* its value as local React state. The onChange mutates that state.
|
|
69
|
+
* Use this when nothing else needs to observe the value.
|
|
70
|
+
*
|
|
71
|
+
* 2. **Controlled by `value` prop**: the slider is read-only —
|
|
72
|
+
* user input is ignored because there is no `onChange` callback
|
|
73
|
+
* wired by the deterministic renderer. Use this for "static"
|
|
74
|
+
* sliders in artifacts. (If you want fully interactive control,
|
|
75
|
+
* use mode 3.)
|
|
76
|
+
*
|
|
77
|
+
* 3. **Bound to PlaygroundState via `stateKey`**: the slider reads
|
|
78
|
+
* its value from the nearest Provider's store (initial: either
|
|
79
|
+
* the store's current value OR `value` prop, whichever is
|
|
80
|
+
* present; the live store wins when bound). On change, the
|
|
81
|
+
* slider writes to the store. Other components can subscribe
|
|
82
|
+
* to the same key.
|
|
83
|
+
*
|
|
84
|
+
* In all modes the displayed value is clamped to `[min, max]`. NaN
|
|
85
|
+
* inputs clamp to `min`. When `min === max` the input is disabled.
|
|
61
86
|
*/
|
|
62
|
-
declare function Slider({ min, max, value, step, label }: SliderProps): React.JSX.Element;
|
|
87
|
+
declare function Slider({ min, max, value, step, label, stateKey }: SliderProps): React.JSX.Element;
|
|
63
88
|
//#endregion
|
|
64
89
|
//#region src/Chart.d.ts
|
|
65
90
|
type ChartKind = 'bar' | 'line' | 'pie';
|
|
@@ -176,23 +201,310 @@ interface ListProps {
|
|
|
176
201
|
*/
|
|
177
202
|
declare function List({ items, ordered, children }: ListProps): React.JSX.Element;
|
|
178
203
|
//#endregion
|
|
179
|
-
//#region src/
|
|
204
|
+
//#region src/state.d.ts
|
|
205
|
+
/**
|
|
206
|
+
* Per-render state store. Components read/write values keyed by string.
|
|
207
|
+
* One store per Provider instance; nested Providers create nested scopes.
|
|
208
|
+
*/
|
|
209
|
+
interface PlaygroundState {
|
|
210
|
+
/** Read a value by key. Returns undefined if unset. */
|
|
211
|
+
get<T = unknown>(key: string): T | undefined;
|
|
212
|
+
/**
|
|
213
|
+
* Set a value. Triggers re-render of subscribers. No-op if the new
|
|
214
|
+
* value is `===` to the current value.
|
|
215
|
+
*/
|
|
216
|
+
set<T = unknown>(key: string, value: T): void;
|
|
217
|
+
/**
|
|
218
|
+
* Subscribe to changes on a single key. The callback fires on every
|
|
219
|
+
* `set(key, ...)` with a different value. Returns an unsubscribe
|
|
220
|
+
* function. Non-React callers can use this directly.
|
|
221
|
+
*/
|
|
222
|
+
subscribe(key: string, cb: (value: unknown) => void): () => void;
|
|
223
|
+
/**
|
|
224
|
+
* Bulk-read: returns a shallow snapshot of all keys. Useful for
|
|
225
|
+
* snapshotting state at a moment in time (e.g., for undo).
|
|
226
|
+
*/
|
|
227
|
+
snapshot(): Readonly<Record<string, unknown>>;
|
|
228
|
+
/** Bulk-write: replace all values. Subscribers fire for changed keys. */
|
|
229
|
+
replaceAll(values: Record<string, unknown>): void;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Create a fresh state store. Public so PlaygroundStateProvider can
|
|
233
|
+
* construct one, but consumers should use Provider/usePlaygroundState
|
|
234
|
+
* to access stores (not direct construction).
|
|
235
|
+
*
|
|
236
|
+
* The optional type parameter is the "shape" of values you intend to
|
|
237
|
+
* store. It's purely a documentation convenience — the store does
|
|
238
|
+
* not enforce it (so you can store heterogeneous values for
|
|
239
|
+
* playgrounds that mix kinds), but `get<T>()` and `set<T>()` will
|
|
240
|
+
* infer correctly when you pass it here.
|
|
241
|
+
*/
|
|
242
|
+
declare function createPlaygroundState<T = unknown>(initial?: Record<string, T>): PlaygroundState;
|
|
243
|
+
interface PlaygroundStateProviderProps {
|
|
244
|
+
/** Initial state values keyed by name. */
|
|
245
|
+
initial?: Record<string, unknown>;
|
|
246
|
+
/**
|
|
247
|
+
* Optional pre-existing state. If supplied, `initial` is merged on
|
|
248
|
+
* top (initial wins for overlapping keys). Use this for SSR
|
|
249
|
+
* rehydration or seeded values.
|
|
250
|
+
*/
|
|
251
|
+
seed?: Record<string, unknown>;
|
|
252
|
+
children: React.ReactNode;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Provide a PlaygroundState scope to descendant components.
|
|
256
|
+
*
|
|
257
|
+
* Each Provider instance has its own store; nested Providers create
|
|
258
|
+
* independent scopes. Components rendered outside any Provider will
|
|
259
|
+
* throw a clear error if they call usePlaygroundState.
|
|
260
|
+
*/
|
|
261
|
+
declare function PlaygroundStateProvider(props: PlaygroundStateProviderProps): React.JSX.Element;
|
|
262
|
+
/**
|
|
263
|
+
* Hook to access the nearest PlaygroundState store. Throws a clear
|
|
264
|
+
* error if called outside a Provider so consumers get a usable
|
|
265
|
+
* diagnostic instead of a `Cannot read properties of undefined`.
|
|
266
|
+
*
|
|
267
|
+
* Note for @playgenx/renderer users: the renderer's `renderBody`
|
|
268
|
+
* does NOT yet auto-wrap with a PlaygroundStateProvider. You'll
|
|
269
|
+
* need to wrap your own tree with `<PlaygroundStateProvider>` (or
|
|
270
|
+
* use the lower-level `renderNodes` API) before state-bound
|
|
271
|
+
* components can resolve. The `withState` option on `renderBody`
|
|
272
|
+
* is planned for a future renderer release (tracked in v0.6.0).
|
|
273
|
+
*/
|
|
274
|
+
declare function usePlaygroundState(): PlaygroundState;
|
|
275
|
+
/**
|
|
276
|
+
* Optional helper for binding a component prop to a state key. Returns
|
|
277
|
+
* the live value (re-renders subscribers on change) and a setter. Pass
|
|
278
|
+
* `undefined` for `key` to disable the binding — the component behaves
|
|
279
|
+
* as if it had no stateKey, and `set` becomes a no-op. Safe to call
|
|
280
|
+
* outside a Provider.
|
|
281
|
+
*
|
|
282
|
+
* Implementation note: this hook ALWAYS calls the same number of
|
|
283
|
+
* React hooks regardless of whether `key` is set or a Provider is in
|
|
284
|
+
* scope. The "no binding" / "no Provider" branch uses dummy local
|
|
285
|
+
* state so the hook count stays aligned across renders. That means a
|
|
286
|
+
* component can flip `stateKey` from defined to undefined (or move in
|
|
287
|
+
* or out of a Provider) without crashing with a hooks-order error.
|
|
288
|
+
*
|
|
289
|
+
* Components that accept a `stateKey` prop should call this internally
|
|
290
|
+
* and use the returned tuple as a drop-in for the underlying
|
|
291
|
+
* value/onChange pair. The return type widens to `readonly [...] | undefined`
|
|
292
|
+
* so the caller can still branch on `if (bound)` — but they don't have
|
|
293
|
+
* to (the tuple is always safe to index).
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* const bound = useBoundValueOrUndefined<number>(stateKey, 50);
|
|
297
|
+
* // ... onChange={(e) => bound?.[1](Number(e.currentTarget.value))}
|
|
298
|
+
*/
|
|
299
|
+
declare function useBoundValueOrUndefined<T>(key: string | undefined, fallback: T): readonly [T, (next: T) => void] | undefined;
|
|
300
|
+
declare function useBoundValue<T>(key: string, fallback: T): readonly [T, (next: T) => void];
|
|
301
|
+
/**
|
|
302
|
+
* State-binding action shape. Used by `<Button onClickAction={...}>`.
|
|
303
|
+
* { set: { count: 1 } } -> sets state.count = 1
|
|
304
|
+
* { toggle: 'open' } -> sets state.open = !state.open
|
|
305
|
+
*/
|
|
306
|
+
type StateAction = {
|
|
307
|
+
set?: Record<string, unknown>;
|
|
308
|
+
toggle?: string;
|
|
309
|
+
} | undefined;
|
|
310
|
+
/**
|
|
311
|
+
* Apply a StateAction to the nearest PlaygroundState. Returns an
|
|
312
|
+
* onClick handler that fires the action. If action is undefined
|
|
313
|
+
* the handler is a no-op.
|
|
314
|
+
*/
|
|
315
|
+
declare function useStateAction(action: StateAction): (event: unknown) => void;
|
|
316
|
+
//#endregion
|
|
317
|
+
//#region src/stateHelpers.d.ts
|
|
180
318
|
/**
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
319
|
+
* Structured diff between two state snapshots. Pure function — does
|
|
320
|
+
* not touch the stores. Returns `null` when the snapshots are
|
|
321
|
+
* shallowly equal (no additions, no changes, no removals).
|
|
184
322
|
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
323
|
+
* @example
|
|
324
|
+
* const a = { x: 1, y: 2 };
|
|
325
|
+
* const b = { x: 1, y: 3, z: 4 };
|
|
326
|
+
* diffSnapshots(a, b); // { added: ['z'], changed: ['y'], removed: [] }
|
|
327
|
+
*/
|
|
328
|
+
interface StateDiff {
|
|
329
|
+
/** Keys present in `next` but absent from `prev`. */
|
|
330
|
+
added: string[];
|
|
331
|
+
/** Keys whose value changed (using `Object.is` to compare). */
|
|
332
|
+
changed: string[];
|
|
333
|
+
/** Keys present in `prev` but absent from `next`. */
|
|
334
|
+
removed: string[];
|
|
335
|
+
}
|
|
336
|
+
declare function diffSnapshots(prev: Readonly<Record<string, unknown>>, next: Readonly<Record<string, unknown>>): StateDiff | null;
|
|
337
|
+
/**
|
|
338
|
+
* Convenience: take snapshots of both stores and diff them. Returns
|
|
339
|
+
* `null` when equal. Useful in tests and debug endpoints.
|
|
340
|
+
*/
|
|
341
|
+
declare function diffState(_a: PlaygroundState, _b: PlaygroundState, prev?: Readonly<Record<string, unknown>>, next?: Readonly<Record<string, unknown>>): StateDiff | null;
|
|
342
|
+
/**
|
|
343
|
+
* Validate a state-key string. Returns `null` on success, or a
|
|
344
|
+
* human-readable error message on failure. The rules:
|
|
345
|
+
*
|
|
346
|
+
* - 1..64 characters
|
|
347
|
+
* - No whitespace
|
|
348
|
+
* - Only alphanumerics, dots, dashes, underscores
|
|
349
|
+
* - Must start with an alphabetic character (so a parser can
|
|
350
|
+
* safely infer `<Slider stateKey="...">` from a body without
|
|
351
|
+
* swallowing accidental numeric keys).
|
|
352
|
+
*
|
|
353
|
+
* Note: keys like `with/slash` or `with spaces` are rejected because
|
|
354
|
+
* the schema-driven body parser uses them as attribute names —
|
|
355
|
+
* a key with weird characters would break the round-trip.
|
|
356
|
+
*/
|
|
357
|
+
declare function validateStateKey(key: string): string | null;
|
|
358
|
+
/**
|
|
359
|
+
* Remove every key from the store, firing subscribers with `undefined`.
|
|
360
|
+
* After this call, `state.snapshot()` is `{}`.
|
|
361
|
+
*/
|
|
362
|
+
declare function clearState(state: PlaygroundState): void;
|
|
363
|
+
/**
|
|
364
|
+
* JSON-envelope snapshot for offline inspection. Useful for:
|
|
365
|
+
* - piping into `jq` / debug endpoints
|
|
366
|
+
* - attaching to error reports
|
|
367
|
+
* - round-tripping through `JSON.parse(state.snapshot)`.
|
|
368
|
+
*
|
|
369
|
+
* The `version` field is bumped if the envelope shape ever changes,
|
|
370
|
+
* so consumers can branch on it.
|
|
371
|
+
*/
|
|
372
|
+
interface StateEnvelope {
|
|
373
|
+
/** Always 1 for now. */
|
|
374
|
+
version: 1;
|
|
375
|
+
/** Sorted list of keys (stable for diffing). */
|
|
376
|
+
keys: string[];
|
|
377
|
+
/** Shallow key→value map. */
|
|
378
|
+
values: Record<string, unknown>;
|
|
379
|
+
}
|
|
380
|
+
declare function dumpState(state: PlaygroundState): string;
|
|
381
|
+
/**
|
|
382
|
+
* Apply multiple `set` calls as a single atomic batch. Each
|
|
383
|
+
* subscriber sees only the LAST value written to its key in this
|
|
384
|
+
* batch. The store still batches subscriber fires via the microtask
|
|
385
|
+
* scheduler — `batch` does not flush synchronously, it only ensures
|
|
386
|
+
* that within the batch, repeated `set` calls to the same key don't
|
|
387
|
+
* queue redundant fires.
|
|
388
|
+
*
|
|
389
|
+
* Useful for: undo/redo, multi-field form commits, hydrated state
|
|
390
|
+
* restore where you want each subscriber to fire exactly once with
|
|
391
|
+
* the final value.
|
|
392
|
+
*
|
|
393
|
+
* @example
|
|
394
|
+
* batch(state, (s) => {
|
|
395
|
+
* s.set('x', 1);
|
|
396
|
+
* s.set('y', 2);
|
|
397
|
+
* s.set('x', 3); // overwrites; subscriber sees only 3
|
|
398
|
+
* });
|
|
399
|
+
*/
|
|
400
|
+
declare function batch<T>(state: PlaygroundState, fn: (state: PlaygroundState) => T): T;
|
|
401
|
+
//#endregion
|
|
402
|
+
//#region src/ArtifactErrorBoundary.d.ts
|
|
403
|
+
interface ArtifactErrorBoundaryProps {
|
|
404
|
+
children: React.ReactNode;
|
|
405
|
+
/**
|
|
406
|
+
* Called when a render error is caught. The `info.componentStack`
|
|
407
|
+
* string is the React component stack at the point of error. Log
|
|
408
|
+
* to your observability backend here.
|
|
409
|
+
*/
|
|
410
|
+
onError?: (error: Error, info: {
|
|
411
|
+
componentStack: string;
|
|
412
|
+
}) => void;
|
|
413
|
+
/**
|
|
414
|
+
* Optional body string. If present and the consumer renders the
|
|
415
|
+
* default fallback, a "Show source" toggle will be available that
|
|
416
|
+
* reveals this body verbatim. Ignored if a custom `fallback` is
|
|
417
|
+
* supplied (the consumer is responsible for surfacing source).
|
|
418
|
+
*/
|
|
419
|
+
body?: string;
|
|
420
|
+
/**
|
|
421
|
+
* Source language hint for the ShowSource toggle in the default
|
|
422
|
+
* fallback. Defaults to 'tsx' (most artifacts are TSX).
|
|
423
|
+
*/
|
|
424
|
+
language?: 'tsx' | 'json';
|
|
425
|
+
/**
|
|
426
|
+
* Optional custom fallback. Pass a React element for a static fallback
|
|
427
|
+
* or a function that receives the error for a dynamic one. If
|
|
428
|
+
* omitted, the default fallback renders a panel with the error
|
|
429
|
+
* message and (if `body` is supplied) a ShowSource toggle.
|
|
430
|
+
*/
|
|
431
|
+
fallback?: React.ReactNode | ((error: Error) => React.ReactNode);
|
|
432
|
+
/**
|
|
433
|
+
* Label shown in the default fallback header. Defaults to the
|
|
434
|
+
* artifact kind identifier (e.g. "playground artifact failed to
|
|
435
|
+
* render"). The consumer is expected to pass the kind.
|
|
436
|
+
*/
|
|
437
|
+
kind?: string;
|
|
438
|
+
}
|
|
439
|
+
interface State {
|
|
440
|
+
error: Error | null;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Error boundary. Catches render-time errors in `children` and shows
|
|
444
|
+
* the fallback. The error state is reset on prop change of `children`
|
|
445
|
+
* (or explicit `resetKey` bump) so the consumer can recover by
|
|
446
|
+
* re-rendering.
|
|
447
|
+
*/
|
|
448
|
+
declare class ArtifactErrorBoundary extends React.Component<ArtifactErrorBoundaryProps, State> {
|
|
449
|
+
state: State;
|
|
450
|
+
static getDerivedStateFromError(error: Error): State;
|
|
451
|
+
componentDidCatch(error: Error, info: React.ErrorInfo): void;
|
|
452
|
+
render(): React.ReactNode;
|
|
453
|
+
}
|
|
454
|
+
//#endregion
|
|
455
|
+
//#region src/ShowSource.d.ts
|
|
456
|
+
interface ShowSourceProps {
|
|
457
|
+
/** The raw body to display when the user expands the source. */
|
|
458
|
+
body: string;
|
|
459
|
+
/** Source language hint. Used in `data-language` for styling hooks. */
|
|
460
|
+
language?: 'tsx' | 'json';
|
|
461
|
+
/**
|
|
462
|
+
* Children is a render prop that receives `(toggle, showing)`.
|
|
463
|
+
* The consumer is expected to render a trigger element (button,
|
|
464
|
+
* link, etc.) and call `toggle` on user interaction.
|
|
465
|
+
*/
|
|
466
|
+
children: (toggle: () => void, showing: boolean) => React.ReactNode;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Toggle button + expandable source panel. The trigger UI is the
|
|
470
|
+
* consumer's choice; the panel is plain `<pre><code>`.
|
|
471
|
+
*/
|
|
472
|
+
declare function ShowSource(props: ShowSourceProps): React.JSX.Element;
|
|
473
|
+
//#endregion
|
|
474
|
+
//#region src/createRegistry.d.ts
|
|
475
|
+
/**
|
|
476
|
+
* Map of PascalCase component name to React component. Loose typing
|
|
477
|
+
* (`ComponentType<any>`) because each component's prop shape differs and
|
|
478
|
+
* the runtime doesn't know which one it's looking up. Consumers who
|
|
479
|
+
* need stricter types can build their own typed wrapper around
|
|
480
|
+
* `renderBody`.
|
|
481
|
+
*/
|
|
482
|
+
type ComponentMap = Record<string, ComponentType<any>>;
|
|
483
|
+
/**
|
|
484
|
+
* The full default registry (Button, Card, Chart, Code, Container,
|
|
485
|
+
* Heading, List, Slider, Stepper, Text, TextField). Frozen. Use
|
|
486
|
+
* `createRegistry(overrides)` to extend.
|
|
487
|
+
*/
|
|
488
|
+
declare const componentMap: ComponentMap;
|
|
489
|
+
/**
|
|
490
|
+
* Build a new registry by merging the defaults with caller-supplied
|
|
491
|
+
* overrides. The returned map is frozen (Object.freeze) to prevent
|
|
492
|
+
* post-hoc mutation that would surprise concurrent renders.
|
|
187
493
|
*
|
|
188
|
-
*
|
|
494
|
+
* **Lowercase HTML tags are NOT in this map.** The renderer handles
|
|
495
|
+
* `div`, `span`, etc. as React intrinsic elements separately. Calling
|
|
496
|
+
* `createRegistry({ div: StubDiv })` will NOT override the renderer
|
|
497
|
+
* for `<div>` (the renderer uses the lowercase lookup path first).
|
|
189
498
|
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```ts
|
|
501
|
+
* const map = createRegistry({
|
|
502
|
+
* MathExpression: MathExpressionComponent,
|
|
503
|
+
* LatexBlock: LatexBlockComponent,
|
|
504
|
+
* });
|
|
505
|
+
* ```
|
|
193
506
|
*/
|
|
194
|
-
declare
|
|
195
|
-
type ComponentMapKey = keyof typeof componentMap;
|
|
507
|
+
declare function createRegistry(overrides: Partial<ComponentMap>): ComponentMap;
|
|
196
508
|
//#endregion
|
|
197
|
-
export { Button, type ButtonProps, Card, type CardProps, Chart, type ChartKind, type ChartProps, Code, type CodeProps,
|
|
509
|
+
export { ArtifactErrorBoundary, type ArtifactErrorBoundaryProps, Button, type ButtonProps, Card, type CardProps, Chart, type ChartKind, type ChartProps, Code, type CodeProps, type ComponentMap, Container, type ContainerProps, Heading, type HeadingProps, List, type ListProps, type PlaygroundState, PlaygroundStateProvider, type PlaygroundStateProviderProps, ShowSource, type ShowSourceProps, Slider, type SliderProps, type StateAction, type StateDiff, type StateEnvelope, type Step, Stepper, type StepperProps, Text, TextField, type TextFieldProps, type TextProps, batch, clearState, componentMap, createPlaygroundState, createRegistry, diffSnapshots, diffState, dumpState, useBoundValue, useBoundValueOrUndefined, usePlaygroundState, useStateAction, validateStateKey };
|
|
198
510
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/Button.tsx","../src/TextField.tsx","../src/Slider.tsx","../src/Chart.tsx","../src/Container.tsx","../src/Code.tsx","../src/Heading.tsx","../src/Text.tsx","../src/Stepper.tsx","../src/Card.tsx","../src/List.tsx","../src/
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/Button.tsx","../src/TextField.tsx","../src/Slider.tsx","../src/Chart.tsx","../src/Container.tsx","../src/Code.tsx","../src/Heading.tsx","../src/Text.tsx","../src/Stepper.tsx","../src/Card.tsx","../src/List.tsx","../src/state.tsx","../src/stateHelpers.ts","../src/ArtifactErrorBoundary.tsx","../src/ShowSource.tsx","../src/createRegistry.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
//#region src/theme.ts
|
|
4
4
|
/**
|
|
5
5
|
* Shared theme tokens for @playgenx/components.
|
|
@@ -125,15 +125,285 @@ function TextField({ label, value, placeholder, disabled = false, onChange }) {
|
|
|
125
125
|
});
|
|
126
126
|
}
|
|
127
127
|
//#endregion
|
|
128
|
+
//#region src/state.tsx
|
|
129
|
+
/**
|
|
130
|
+
* Per-render interactive state store. Used by PlaygroundStateProvider to
|
|
131
|
+
* back interactive components (Slider, TextField, Button, Chart) without
|
|
132
|
+
* requiring them to be fully controlled.
|
|
133
|
+
*
|
|
134
|
+
* Design notes:
|
|
135
|
+
* - The store is a singleton instance per Provider, NOT React context.
|
|
136
|
+
* Components consume it via usePlaygroundState() inside React renders,
|
|
137
|
+
* and via .subscribe() / .get() / .set() from non-React callers.
|
|
138
|
+
* - `set` is synchronous; subscribers fire in registration order on the
|
|
139
|
+
* next microtask (so multiple sets in a single tick produce a single
|
|
140
|
+
* re-render per subscriber).
|
|
141
|
+
* - `set` with `===` equal value is a no-op (no subscriber fires).
|
|
142
|
+
* - Throws if used outside a PlaygroundStateProvider (when required by
|
|
143
|
+
* the caller's contract).
|
|
144
|
+
*
|
|
145
|
+
* @packageDocumentation
|
|
146
|
+
*/
|
|
147
|
+
/**
|
|
148
|
+
* Create a fresh state store. Public so PlaygroundStateProvider can
|
|
149
|
+
* construct one, but consumers should use Provider/usePlaygroundState
|
|
150
|
+
* to access stores (not direct construction).
|
|
151
|
+
*
|
|
152
|
+
* The optional type parameter is the "shape" of values you intend to
|
|
153
|
+
* store. It's purely a documentation convenience — the store does
|
|
154
|
+
* not enforce it (so you can store heterogeneous values for
|
|
155
|
+
* playgrounds that mix kinds), but `get<T>()` and `set<T>()` will
|
|
156
|
+
* infer correctly when you pass it here.
|
|
157
|
+
*/
|
|
158
|
+
function createPlaygroundState(initial = {}) {
|
|
159
|
+
const values = new Map(Object.entries(initial));
|
|
160
|
+
const subs = /* @__PURE__ */ new Map();
|
|
161
|
+
let pending = [];
|
|
162
|
+
let scheduled = false;
|
|
163
|
+
let batchDepth = 0;
|
|
164
|
+
let batchedPending = [];
|
|
165
|
+
const flush = () => {
|
|
166
|
+
if (pending.length === 0) return;
|
|
167
|
+
const batch = pending;
|
|
168
|
+
pending = [];
|
|
169
|
+
scheduled = false;
|
|
170
|
+
for (const { key, value } of batch) {
|
|
171
|
+
const list = subs.get(key);
|
|
172
|
+
if (list) for (const cb of [...list]) try {
|
|
173
|
+
cb(value);
|
|
174
|
+
} catch (err) {
|
|
175
|
+
if (process.env.NODE_ENV !== "production") console.error("[playgenx] subscriber for key %s threw:", key, err);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
const schedule = () => {
|
|
180
|
+
if (scheduled) return;
|
|
181
|
+
scheduled = true;
|
|
182
|
+
if (typeof queueMicrotask === "function") queueMicrotask(flush);
|
|
183
|
+
else Promise.resolve().then(flush);
|
|
184
|
+
};
|
|
185
|
+
const state = {
|
|
186
|
+
get(key) {
|
|
187
|
+
return values.get(key);
|
|
188
|
+
},
|
|
189
|
+
set(key, value) {
|
|
190
|
+
if (Object.is(values.get(key), value)) return;
|
|
191
|
+
values.set(key, value);
|
|
192
|
+
if (batchDepth > 0) batchedPending.push({
|
|
193
|
+
key,
|
|
194
|
+
value
|
|
195
|
+
});
|
|
196
|
+
else pending.push({
|
|
197
|
+
key,
|
|
198
|
+
value
|
|
199
|
+
});
|
|
200
|
+
schedule();
|
|
201
|
+
},
|
|
202
|
+
subscribe(key, cb) {
|
|
203
|
+
let list = subs.get(key);
|
|
204
|
+
if (!list) {
|
|
205
|
+
list = /* @__PURE__ */ new Set();
|
|
206
|
+
subs.set(key, list);
|
|
207
|
+
}
|
|
208
|
+
list.add(cb);
|
|
209
|
+
return () => {
|
|
210
|
+
const l = subs.get(key);
|
|
211
|
+
if (l) {
|
|
212
|
+
l.delete(cb);
|
|
213
|
+
if (l.size === 0) subs.delete(key);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
},
|
|
217
|
+
snapshot() {
|
|
218
|
+
return Object.fromEntries(values);
|
|
219
|
+
},
|
|
220
|
+
replaceAll(values) {
|
|
221
|
+
for (const [k, v] of Object.entries(values)) state.set(k, v);
|
|
222
|
+
},
|
|
223
|
+
_flush() {
|
|
224
|
+
flush();
|
|
225
|
+
},
|
|
226
|
+
_batch(fn) {
|
|
227
|
+
batchDepth++;
|
|
228
|
+
try {
|
|
229
|
+
return fn();
|
|
230
|
+
} finally {
|
|
231
|
+
batchDepth--;
|
|
232
|
+
if (batchDepth === 0 && batchedPending.length > 0) {
|
|
233
|
+
const lastPerKey = /* @__PURE__ */ new Map();
|
|
234
|
+
for (let i = batchedPending.length - 1; i >= 0; i--) {
|
|
235
|
+
const { key, value } = batchedPending[i];
|
|
236
|
+
if (!lastPerKey.has(key)) {
|
|
237
|
+
lastPerKey.set(key, value);
|
|
238
|
+
pending.push({
|
|
239
|
+
key,
|
|
240
|
+
value
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
batchedPending = [];
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
_clearAllSubscribers() {
|
|
249
|
+
subs.clear();
|
|
250
|
+
pending = [];
|
|
251
|
+
batchedPending = [];
|
|
252
|
+
scheduled = false;
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
return state;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* React context. Exposed as a type only — consumers should use
|
|
259
|
+
* PlaygroundStateProvider and usePlaygroundState rather than reading
|
|
260
|
+
* the context directly.
|
|
261
|
+
*/
|
|
262
|
+
const PlaygroundStateContext = React.createContext(null);
|
|
263
|
+
/**
|
|
264
|
+
* Provide a PlaygroundState scope to descendant components.
|
|
265
|
+
*
|
|
266
|
+
* Each Provider instance has its own store; nested Providers create
|
|
267
|
+
* independent scopes. Components rendered outside any Provider will
|
|
268
|
+
* throw a clear error if they call usePlaygroundState.
|
|
269
|
+
*/
|
|
270
|
+
function PlaygroundStateProvider(props) {
|
|
271
|
+
const [state] = React.useState(() => {
|
|
272
|
+
return createPlaygroundState({
|
|
273
|
+
...props.seed,
|
|
274
|
+
...props.initial
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
React.useEffect(() => {
|
|
278
|
+
return () => {
|
|
279
|
+
state._clearAllSubscribers?.();
|
|
280
|
+
};
|
|
281
|
+
}, [state]);
|
|
282
|
+
const value = React.useMemo(() => ({ state }), [state]);
|
|
283
|
+
return React.createElement(PlaygroundStateContext.Provider, { value }, props.children);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Hook to access the nearest PlaygroundState store. Throws a clear
|
|
287
|
+
* error if called outside a Provider so consumers get a usable
|
|
288
|
+
* diagnostic instead of a `Cannot read properties of undefined`.
|
|
289
|
+
*
|
|
290
|
+
* Note for @playgenx/renderer users: the renderer's `renderBody`
|
|
291
|
+
* does NOT yet auto-wrap with a PlaygroundStateProvider. You'll
|
|
292
|
+
* need to wrap your own tree with `<PlaygroundStateProvider>` (or
|
|
293
|
+
* use the lower-level `renderNodes` API) before state-bound
|
|
294
|
+
* components can resolve. The `withState` option on `renderBody`
|
|
295
|
+
* is planned for a future renderer release (tracked in v0.6.0).
|
|
296
|
+
*/
|
|
297
|
+
function usePlaygroundState() {
|
|
298
|
+
const ctx = React.useContext(PlaygroundStateContext);
|
|
299
|
+
if (ctx === null) throw new Error("usePlaygroundState() must be called inside a <PlaygroundStateProvider>. Wrap your component tree with the Provider. (A future renderer release will auto-wrap; for now, do it explicitly.)");
|
|
300
|
+
return ctx.state;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Optional helper for binding a component prop to a state key. Returns
|
|
304
|
+
* the live value (re-renders subscribers on change) and a setter. Pass
|
|
305
|
+
* `undefined` for `key` to disable the binding — the component behaves
|
|
306
|
+
* as if it had no stateKey, and `set` becomes a no-op. Safe to call
|
|
307
|
+
* outside a Provider.
|
|
308
|
+
*
|
|
309
|
+
* Implementation note: this hook ALWAYS calls the same number of
|
|
310
|
+
* React hooks regardless of whether `key` is set or a Provider is in
|
|
311
|
+
* scope. The "no binding" / "no Provider" branch uses dummy local
|
|
312
|
+
* state so the hook count stays aligned across renders. That means a
|
|
313
|
+
* component can flip `stateKey` from defined to undefined (or move in
|
|
314
|
+
* or out of a Provider) without crashing with a hooks-order error.
|
|
315
|
+
*
|
|
316
|
+
* Components that accept a `stateKey` prop should call this internally
|
|
317
|
+
* and use the returned tuple as a drop-in for the underlying
|
|
318
|
+
* value/onChange pair. The return type widens to `readonly [...] | undefined`
|
|
319
|
+
* so the caller can still branch on `if (bound)` — but they don't have
|
|
320
|
+
* to (the tuple is always safe to index).
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* const bound = useBoundValueOrUndefined<number>(stateKey, 50);
|
|
324
|
+
* // ... onChange={(e) => bound?.[1](Number(e.currentTarget.value))}
|
|
325
|
+
*/
|
|
326
|
+
function useBoundValueOrUndefined(key, fallback) {
|
|
327
|
+
const ctx = React.useContext(PlaygroundStateContext);
|
|
328
|
+
const bound = key !== void 0 && ctx !== null;
|
|
329
|
+
const [value, setValue] = React.useState(fallback);
|
|
330
|
+
const [, force] = React.useState(0);
|
|
331
|
+
React.useEffect(() => {
|
|
332
|
+
if (!bound || key === void 0 || ctx === null) return;
|
|
333
|
+
const initial = ctx.state.get(key) ?? fallback;
|
|
334
|
+
setValue(initial);
|
|
335
|
+
return ctx.state.subscribe(key, (next) => {
|
|
336
|
+
setValue(next);
|
|
337
|
+
force((n) => n + 1);
|
|
338
|
+
});
|
|
339
|
+
}, [
|
|
340
|
+
bound,
|
|
341
|
+
ctx,
|
|
342
|
+
key
|
|
343
|
+
]);
|
|
344
|
+
const set = React.useCallback((next) => {
|
|
345
|
+
if (!bound || key === void 0 || ctx === null) return;
|
|
346
|
+
ctx.state.set(key, next);
|
|
347
|
+
}, [
|
|
348
|
+
bound,
|
|
349
|
+
ctx,
|
|
350
|
+
key
|
|
351
|
+
]);
|
|
352
|
+
if (!bound) return void 0;
|
|
353
|
+
return [value, set];
|
|
354
|
+
}
|
|
355
|
+
function useBoundValue(key, fallback) {
|
|
356
|
+
const bound = useBoundValueOrUndefined(key, fallback);
|
|
357
|
+
if (bound === void 0) throw new Error("useBoundValue(key, ...) requires both a non-undefined `key` and a <PlaygroundStateProvider> ancestor. Use useBoundValueOrUndefined if you want optional binding.");
|
|
358
|
+
return bound;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Apply a StateAction to the nearest PlaygroundState. Returns an
|
|
362
|
+
* onClick handler that fires the action. If action is undefined
|
|
363
|
+
* the handler is a no-op.
|
|
364
|
+
*/
|
|
365
|
+
function useStateAction(action) {
|
|
366
|
+
const state = usePlaygroundState();
|
|
367
|
+
return React.useCallback((event) => {
|
|
368
|
+
if (action === void 0) return;
|
|
369
|
+
if (action.set) for (const [k, v] of Object.entries(action.set)) state.set(k, v);
|
|
370
|
+
if (action.toggle !== void 0) {
|
|
371
|
+
const key = action.toggle;
|
|
372
|
+
const current = state.get(key);
|
|
373
|
+
state.set(key, !current);
|
|
374
|
+
}
|
|
375
|
+
}, [action, state]);
|
|
376
|
+
}
|
|
377
|
+
//#endregion
|
|
128
378
|
//#region src/Slider.tsx
|
|
129
379
|
/**
|
|
130
|
-
* Range input.
|
|
131
|
-
*
|
|
380
|
+
* Range input. Three operating modes:
|
|
381
|
+
*
|
|
382
|
+
* 1. **Uncontrolled** (no `value`, no `stateKey`): the slider owns
|
|
383
|
+
* its value as local React state. The onChange mutates that state.
|
|
384
|
+
* Use this when nothing else needs to observe the value.
|
|
385
|
+
*
|
|
386
|
+
* 2. **Controlled by `value` prop**: the slider is read-only —
|
|
387
|
+
* user input is ignored because there is no `onChange` callback
|
|
388
|
+
* wired by the deterministic renderer. Use this for "static"
|
|
389
|
+
* sliders in artifacts. (If you want fully interactive control,
|
|
390
|
+
* use mode 3.)
|
|
391
|
+
*
|
|
392
|
+
* 3. **Bound to PlaygroundState via `stateKey`**: the slider reads
|
|
393
|
+
* its value from the nearest Provider's store (initial: either
|
|
394
|
+
* the store's current value OR `value` prop, whichever is
|
|
395
|
+
* present; the live store wins when bound). On change, the
|
|
396
|
+
* slider writes to the store. Other components can subscribe
|
|
397
|
+
* to the same key.
|
|
398
|
+
*
|
|
399
|
+
* In all modes the displayed value is clamped to `[min, max]`. NaN
|
|
400
|
+
* inputs clamp to `min`. When `min === max` the input is disabled.
|
|
132
401
|
*/
|
|
133
|
-
function Slider({ min, max, value, step = 1, label }) {
|
|
402
|
+
function Slider({ min, max, value, step = 1, label, stateKey }) {
|
|
134
403
|
const [internal, setInternal] = React.useState(() => clamp(value ?? min, min, max));
|
|
404
|
+
const bound = useBoundValueOrUndefined(stateKey, internal);
|
|
135
405
|
const controlled = value !== void 0;
|
|
136
|
-
const current = controlled ? clamp(value, min, max) : internal;
|
|
406
|
+
const current = stateKey !== void 0 && bound ? clamp(bound[0], min, max) : controlled ? clamp(value, min, max) : internal;
|
|
137
407
|
const trackStyle = {
|
|
138
408
|
display: "flex",
|
|
139
409
|
flexDirection: "column",
|
|
@@ -179,7 +449,8 @@ function Slider({ min, max, value, step = 1, label }) {
|
|
|
179
449
|
"aria-label": label,
|
|
180
450
|
onChange: (e) => {
|
|
181
451
|
const next = Number(e.currentTarget.value);
|
|
182
|
-
if (
|
|
452
|
+
if (stateKey !== void 0 && bound) bound[1](next);
|
|
453
|
+
else if (!controlled) setInternal(next);
|
|
183
454
|
},
|
|
184
455
|
style: inputStyle
|
|
185
456
|
}), /* @__PURE__ */ jsx("span", {
|
|
@@ -604,35 +875,315 @@ function List({ items, ordered, children }) {
|
|
|
604
875
|
}));
|
|
605
876
|
}
|
|
606
877
|
//#endregion
|
|
607
|
-
//#region src/
|
|
878
|
+
//#region src/stateHelpers.ts
|
|
879
|
+
function diffSnapshots(prev, next) {
|
|
880
|
+
const added = [];
|
|
881
|
+
const changed = [];
|
|
882
|
+
const removed = [];
|
|
883
|
+
const prevKeys = Object.keys(prev);
|
|
884
|
+
const nextKeys = Object.keys(next);
|
|
885
|
+
const prevSet = new Set(prevKeys);
|
|
886
|
+
const nextSet = new Set(nextKeys);
|
|
887
|
+
for (const k of nextKeys) {
|
|
888
|
+
if (!prevSet.has(k)) {
|
|
889
|
+
added.push(k);
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
if (!Object.is(prev[k], next[k])) changed.push(k);
|
|
893
|
+
}
|
|
894
|
+
for (const k of prevKeys) if (!nextSet.has(k)) removed.push(k);
|
|
895
|
+
added.sort();
|
|
896
|
+
changed.sort();
|
|
897
|
+
removed.sort();
|
|
898
|
+
if (added.length === 0 && changed.length === 0 && removed.length === 0) return null;
|
|
899
|
+
return {
|
|
900
|
+
added,
|
|
901
|
+
changed,
|
|
902
|
+
removed
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Convenience: take snapshots of both stores and diff them. Returns
|
|
907
|
+
* `null` when equal. Useful in tests and debug endpoints.
|
|
908
|
+
*/
|
|
909
|
+
function diffState(_a, _b, prev, next) {
|
|
910
|
+
return diffSnapshots(prev ?? _a.snapshot(), next ?? _b.snapshot());
|
|
911
|
+
}
|
|
912
|
+
/**
|
|
913
|
+
* Validate a state-key string. Returns `null` on success, or a
|
|
914
|
+
* human-readable error message on failure. The rules:
|
|
915
|
+
*
|
|
916
|
+
* - 1..64 characters
|
|
917
|
+
* - No whitespace
|
|
918
|
+
* - Only alphanumerics, dots, dashes, underscores
|
|
919
|
+
* - Must start with an alphabetic character (so a parser can
|
|
920
|
+
* safely infer `<Slider stateKey="...">` from a body without
|
|
921
|
+
* swallowing accidental numeric keys).
|
|
922
|
+
*
|
|
923
|
+
* Note: keys like `with/slash` or `with spaces` are rejected because
|
|
924
|
+
* the schema-driven body parser uses them as attribute names —
|
|
925
|
+
* a key with weird characters would break the round-trip.
|
|
926
|
+
*/
|
|
927
|
+
function validateStateKey(key) {
|
|
928
|
+
if (key.length === 0) return "stateKey must be a non-empty string";
|
|
929
|
+
if (key.length > 64) return "stateKey must be at most 64 characters";
|
|
930
|
+
if (/\s/.test(key)) return "stateKey must not contain whitespace";
|
|
931
|
+
if (!/^[A-Za-z][A-Za-z0-9._-]*$/.test(key)) return "stateKey contains forbidden characters (allowed: A-Z, a-z, 0-9, dot, dash, underscore; must start with a letter)";
|
|
932
|
+
return null;
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* Remove every key from the store, firing subscribers with `undefined`.
|
|
936
|
+
* After this call, `state.snapshot()` is `{}`.
|
|
937
|
+
*/
|
|
938
|
+
function clearState(state) {
|
|
939
|
+
for (const k of Object.keys(state.snapshot())) state.set(k, void 0);
|
|
940
|
+
}
|
|
941
|
+
function dumpState(state) {
|
|
942
|
+
const snap = state.snapshot();
|
|
943
|
+
const envelope = {
|
|
944
|
+
version: 1,
|
|
945
|
+
keys: Object.keys(snap).sort(),
|
|
946
|
+
values: { ...snap }
|
|
947
|
+
};
|
|
948
|
+
return JSON.stringify(envelope, null, 2);
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Apply multiple `set` calls as a single atomic batch. Each
|
|
952
|
+
* subscriber sees only the LAST value written to its key in this
|
|
953
|
+
* batch. The store still batches subscriber fires via the microtask
|
|
954
|
+
* scheduler — `batch` does not flush synchronously, it only ensures
|
|
955
|
+
* that within the batch, repeated `set` calls to the same key don't
|
|
956
|
+
* queue redundant fires.
|
|
957
|
+
*
|
|
958
|
+
* Useful for: undo/redo, multi-field form commits, hydrated state
|
|
959
|
+
* restore where you want each subscriber to fire exactly once with
|
|
960
|
+
* the final value.
|
|
961
|
+
*
|
|
962
|
+
* @example
|
|
963
|
+
* batch(state, (s) => {
|
|
964
|
+
* s.set('x', 1);
|
|
965
|
+
* s.set('y', 2);
|
|
966
|
+
* s.set('x', 3); // overwrites; subscriber sees only 3
|
|
967
|
+
* });
|
|
968
|
+
*/
|
|
969
|
+
function batch(state, fn) {
|
|
970
|
+
return state._batch(() => fn(state));
|
|
971
|
+
}
|
|
972
|
+
//#endregion
|
|
973
|
+
//#region src/ShowSource.tsx
|
|
608
974
|
/**
|
|
609
|
-
*
|
|
610
|
-
*
|
|
611
|
-
* implementation at runtime:
|
|
975
|
+
* Render-prop component that shows a toggle-able raw-source panel.
|
|
976
|
+
* Consumer controls the trigger UI via a children render prop.
|
|
612
977
|
*
|
|
613
|
-
*
|
|
614
|
-
*
|
|
978
|
+
* Design notes:
|
|
979
|
+
* - Render-prop pattern (not props.children-as-trigger) because the
|
|
980
|
+
* trigger UI varies wildly across consumers (button, link, icon,
|
|
981
|
+
* inline text). The consumer gets `(toggle, showing)` and decides
|
|
982
|
+
* what to render.
|
|
983
|
+
* - The source panel is a `<pre>` with `<code>` inside. Plain-text;
|
|
984
|
+
* no syntax highlighting (avoiding a 50KB highlight.js dep).
|
|
985
|
+
* Consumers who need highlighting can compose with their own
|
|
986
|
+
* syntax-highlighter.
|
|
987
|
+
* - The panel is rendered INLINE (not in a portal/tooltip) so the
|
|
988
|
+
* toggle's visual feedback is immediate.
|
|
989
|
+
*
|
|
990
|
+
* @packageDocumentation
|
|
991
|
+
*/
|
|
992
|
+
/**
|
|
993
|
+
* Toggle button + expandable source panel. The trigger UI is the
|
|
994
|
+
* consumer's choice; the panel is plain `<pre><code>`.
|
|
995
|
+
*/
|
|
996
|
+
function ShowSource(props) {
|
|
997
|
+
const { body, language, children } = props;
|
|
998
|
+
const [showing, setShowing] = React.useState(false);
|
|
999
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [children(React.useCallback(() => {
|
|
1000
|
+
setShowing((s) => !s);
|
|
1001
|
+
}, []), showing), showing ? /* @__PURE__ */ jsx("pre", {
|
|
1002
|
+
"data-pgx": "ShowSource",
|
|
1003
|
+
"data-language": language ?? "text",
|
|
1004
|
+
style: {
|
|
1005
|
+
background: "#0f172a",
|
|
1006
|
+
color: "#e2e8f0",
|
|
1007
|
+
padding: "12px",
|
|
1008
|
+
borderRadius: "6px",
|
|
1009
|
+
overflowX: "auto",
|
|
1010
|
+
fontFamily: "ui-monospace, Menlo, monospace",
|
|
1011
|
+
fontSize: "12px",
|
|
1012
|
+
lineHeight: 1.5,
|
|
1013
|
+
margin: "8px 0 0",
|
|
1014
|
+
maxHeight: "320px",
|
|
1015
|
+
whiteSpace: "pre"
|
|
1016
|
+
},
|
|
1017
|
+
children: /* @__PURE__ */ jsx("code", { children: body })
|
|
1018
|
+
}) : null] });
|
|
1019
|
+
}
|
|
1020
|
+
//#endregion
|
|
1021
|
+
//#region src/ArtifactErrorBoundary.tsx
|
|
1022
|
+
/**
|
|
1023
|
+
* Error boundary for rendered artifacts. Catches render-time errors
|
|
1024
|
+
* (malformed body, throw from a component, etc.) and shows a recoverable
|
|
1025
|
+
* fallback. Without this, a single bad artifact blanks the student's
|
|
1026
|
+
* whole page.
|
|
615
1027
|
*
|
|
616
|
-
*
|
|
1028
|
+
* Design notes:
|
|
1029
|
+
* - Uses React's class-component error boundary API (the only
|
|
1030
|
+
* first-class option in React 18/19).
|
|
1031
|
+
* - Default fallback shows the error message + an optional "Show source"
|
|
1032
|
+
* toggle that reveals the raw body. Consumer can override with
|
|
1033
|
+
* `fallback` for custom UX.
|
|
1034
|
+
* - `onError` fires before the fallback renders so consumers can log
|
|
1035
|
+
* to Sentry / Datadog / etc.
|
|
617
1036
|
*
|
|
618
|
-
*
|
|
619
|
-
|
|
620
|
-
|
|
1037
|
+
* @packageDocumentation
|
|
1038
|
+
*/
|
|
1039
|
+
/**
|
|
1040
|
+
* Default fallback panel. Renders the error message and, if `body` is
|
|
1041
|
+
* provided, a ShowSource toggle. Designed to be a calm, recoverable
|
|
1042
|
+
* surface — not a stack-trace dump that panics users.
|
|
621
1043
|
*/
|
|
622
|
-
|
|
1044
|
+
function DefaultFallback({ error, body, language, kind }) {
|
|
1045
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1046
|
+
role: "alert",
|
|
1047
|
+
"data-pgx": "ArtifactErrorBoundary",
|
|
1048
|
+
"data-pgx-kind": kind,
|
|
1049
|
+
style: {
|
|
1050
|
+
border: "1px solid #fca5a5",
|
|
1051
|
+
background: "#fef2f2",
|
|
1052
|
+
color: "#7f1d1d",
|
|
1053
|
+
borderRadius: "8px",
|
|
1054
|
+
padding: "12px 16px",
|
|
1055
|
+
fontFamily: "ui-sans-serif, system-ui, sans-serif",
|
|
1056
|
+
fontSize: "14px",
|
|
1057
|
+
maxWidth: "720px",
|
|
1058
|
+
margin: "12px 0"
|
|
1059
|
+
},
|
|
1060
|
+
children: [
|
|
1061
|
+
/* @__PURE__ */ jsx("div", {
|
|
1062
|
+
style: {
|
|
1063
|
+
fontWeight: 600,
|
|
1064
|
+
marginBottom: "6px"
|
|
1065
|
+
},
|
|
1066
|
+
children: kind ? `${kind} artifact failed to render` : "Artifact failed to render"
|
|
1067
|
+
}),
|
|
1068
|
+
/* @__PURE__ */ jsx("code", {
|
|
1069
|
+
"data-pgx": "error-message",
|
|
1070
|
+
style: {
|
|
1071
|
+
display: "block",
|
|
1072
|
+
fontFamily: "ui-monospace, Menlo, monospace",
|
|
1073
|
+
fontSize: "12px",
|
|
1074
|
+
background: "rgba(0,0,0,0.04)",
|
|
1075
|
+
padding: "6px 8px",
|
|
1076
|
+
borderRadius: "4px",
|
|
1077
|
+
whiteSpace: "pre-wrap",
|
|
1078
|
+
wordBreak: "break-word"
|
|
1079
|
+
},
|
|
1080
|
+
children: error.message
|
|
1081
|
+
}),
|
|
1082
|
+
body !== void 0 ? /* @__PURE__ */ jsx("div", {
|
|
1083
|
+
style: { marginTop: "8px" },
|
|
1084
|
+
children: /* @__PURE__ */ jsx(ShowSource, {
|
|
1085
|
+
body,
|
|
1086
|
+
language,
|
|
1087
|
+
children: (toggle, showing) => /* @__PURE__ */ jsx("button", {
|
|
1088
|
+
type: "button",
|
|
1089
|
+
onClick: toggle,
|
|
1090
|
+
"data-pgx": "show-source-toggle",
|
|
1091
|
+
style: {
|
|
1092
|
+
background: "transparent",
|
|
1093
|
+
border: "1px solid #fca5a5",
|
|
1094
|
+
color: "#7f1d1d",
|
|
1095
|
+
padding: "4px 10px",
|
|
1096
|
+
fontSize: "12px",
|
|
1097
|
+
borderRadius: "4px",
|
|
1098
|
+
cursor: "pointer"
|
|
1099
|
+
},
|
|
1100
|
+
children: showing ? "Hide source" : "Show source"
|
|
1101
|
+
})
|
|
1102
|
+
})
|
|
1103
|
+
}) : null
|
|
1104
|
+
]
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Error boundary. Catches render-time errors in `children` and shows
|
|
1109
|
+
* the fallback. The error state is reset on prop change of `children`
|
|
1110
|
+
* (or explicit `resetKey` bump) so the consumer can recover by
|
|
1111
|
+
* re-rendering.
|
|
1112
|
+
*/
|
|
1113
|
+
var ArtifactErrorBoundary = class extends React.Component {
|
|
1114
|
+
state = { error: null };
|
|
1115
|
+
static getDerivedStateFromError(error) {
|
|
1116
|
+
return { error };
|
|
1117
|
+
}
|
|
1118
|
+
componentDidCatch(error, info) {
|
|
1119
|
+
if (this.props.onError) try {
|
|
1120
|
+
this.props.onError(error, { componentStack: info.componentStack ?? "" });
|
|
1121
|
+
} catch {}
|
|
1122
|
+
}
|
|
1123
|
+
render() {
|
|
1124
|
+
const { error } = this.state;
|
|
1125
|
+
if (error === null) return this.props.children;
|
|
1126
|
+
const { fallback, body, language, kind } = this.props;
|
|
1127
|
+
if (typeof fallback === "function") return fallback(error);
|
|
1128
|
+
if (fallback !== void 0) return fallback;
|
|
1129
|
+
return /* @__PURE__ */ jsx(DefaultFallback, {
|
|
1130
|
+
error,
|
|
1131
|
+
body,
|
|
1132
|
+
language,
|
|
1133
|
+
kind
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
};
|
|
1137
|
+
//#endregion
|
|
1138
|
+
//#region src/createRegistry.ts
|
|
1139
|
+
const DEFAULT_REGISTRY = Object.freeze({
|
|
623
1140
|
Button,
|
|
624
|
-
|
|
625
|
-
Slider,
|
|
1141
|
+
Card,
|
|
626
1142
|
Chart,
|
|
627
|
-
Container,
|
|
628
1143
|
Code,
|
|
1144
|
+
Container,
|
|
629
1145
|
Heading,
|
|
630
|
-
|
|
1146
|
+
List,
|
|
1147
|
+
Slider,
|
|
631
1148
|
Stepper,
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
};
|
|
1149
|
+
Text,
|
|
1150
|
+
TextField
|
|
1151
|
+
});
|
|
1152
|
+
/**
|
|
1153
|
+
* The full default registry (Button, Card, Chart, Code, Container,
|
|
1154
|
+
* Heading, List, Slider, Stepper, Text, TextField). Frozen. Use
|
|
1155
|
+
* `createRegistry(overrides)` to extend.
|
|
1156
|
+
*/
|
|
1157
|
+
const componentMap = DEFAULT_REGISTRY;
|
|
1158
|
+
/**
|
|
1159
|
+
* Build a new registry by merging the defaults with caller-supplied
|
|
1160
|
+
* overrides. The returned map is frozen (Object.freeze) to prevent
|
|
1161
|
+
* post-hoc mutation that would surprise concurrent renders.
|
|
1162
|
+
*
|
|
1163
|
+
* **Lowercase HTML tags are NOT in this map.** The renderer handles
|
|
1164
|
+
* `div`, `span`, etc. as React intrinsic elements separately. Calling
|
|
1165
|
+
* `createRegistry({ div: StubDiv })` will NOT override the renderer
|
|
1166
|
+
* for `<div>` (the renderer uses the lowercase lookup path first).
|
|
1167
|
+
*
|
|
1168
|
+
* @example
|
|
1169
|
+
* ```ts
|
|
1170
|
+
* const map = createRegistry({
|
|
1171
|
+
* MathExpression: MathExpressionComponent,
|
|
1172
|
+
* LatexBlock: LatexBlockComponent,
|
|
1173
|
+
* });
|
|
1174
|
+
* ```
|
|
1175
|
+
*/
|
|
1176
|
+
function createRegistry(overrides) {
|
|
1177
|
+
const merged = { ...DEFAULT_REGISTRY };
|
|
1178
|
+
for (const key of Object.keys(overrides)) {
|
|
1179
|
+
const value = overrides[key];
|
|
1180
|
+
if (value === void 0) continue;
|
|
1181
|
+
if (key[0] !== key[0].toUpperCase()) continue;
|
|
1182
|
+
merged[key] = value;
|
|
1183
|
+
}
|
|
1184
|
+
return Object.freeze(merged);
|
|
1185
|
+
}
|
|
635
1186
|
//#endregion
|
|
636
|
-
export { Button, Card, Chart, Code, Container, Heading, List, Slider, Stepper, Text, TextField, componentMap };
|
|
1187
|
+
export { ArtifactErrorBoundary, Button, Card, Chart, Code, Container, Heading, List, PlaygroundStateProvider, ShowSource, Slider, Stepper, Text, TextField, batch, clearState, componentMap, createPlaygroundState, createRegistry, diffSnapshots, diffState, dumpState, useBoundValue, useBoundValueOrUndefined, usePlaygroundState, useStateAction, validateStateKey };
|
|
637
1188
|
|
|
638
1189
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/theme.ts","../src/Button.tsx","../src/TextField.tsx","../src/Slider.tsx","../src/Chart.tsx","../src/Container.tsx","../src/Code.tsx","../src/Heading.tsx","../src/Text.tsx","../src/Stepper.tsx","../src/Card.tsx","../src/List.tsx","../src/registry.ts"],"sourcesContent":["/**\n * Shared theme tokens for @playgenx/components.\n *\n * All styling is driven from this file. There is intentionally no\n * \"config\" theme or runtime theme provider — these are the default\n * implementations of the registry, and a downstream host app can swap\n * them out entirely. Tokens live as constants so they're stable\n * across renders and don't allocate per call.\n */\n\nexport const colors = {\n bg: '#ffffff',\n fg: '#0f172a',\n mutedFg: '#475569',\n border: '#e2e8f0',\n primary: '#2563eb',\n primaryFg: '#ffffff',\n secondaryFg: '#0f172a',\n ghostFg: '#1d4ed8',\n cardBg: '#f8fafc',\n cardBorder: '#e2e8f0',\n codeBg: '#f1f5f9',\n codeFg: '#0f172a',\n sliderTrack: '#cbd5e1',\n sliderFill: '#2563eb',\n sliderThumb: '#1d4ed8',\n bar: '#2563eb',\n barLabel: '#0f172a',\n pie: ['#2563eb', '#16a34a', '#f59e0b', '#dc2626'],\n line: '#2563eb',\n stepperPast: '#16a34a',\n stepperCurrent: '#2563eb',\n stepperFuture: '#cbd5e1',\n} as const;\n\nexport const space = {\n none: '0',\n xs: '4px',\n sm: '8px',\n md: '12px',\n lg: '16px',\n xl: '24px',\n} as const;\n\nexport const radius = {\n sm: '4px',\n md: '6px',\n lg: '8px',\n} as const;\n\n/**\n * Common React-friendly prop accepts for content. Components that\n * accept children of arbitrary shape (Heading, Text, Card, Container,\n * ...) use this so the host can pass strings or JSX interchangeably.\n */\nexport type Children = string | number | React.ReactNode;\n\nimport type * as React from 'react';\n","import * as React from 'react';\nimport { colors, radius } from './theme.js';\n\n/**\n * Headless-style wrapper over `<button>` accepting the props defined in\n * `DEFAULT_COMPONENT_SCHEMAS.Button`. Pass `onClick` through; this\n * implementation does NOT execute arbitrary functions (deterministic\n * renderer constraint) so the click handler is rendered inert at mount.\n */\nexport interface ButtonProps {\n /** Visible text. Required. */\n label: string;\n /** Variant. Defaults to 'primary'. */\n variant?: 'primary' | 'secondary' | 'ghost';\n /** Disabled state. Defaults to false. */\n disabled?: boolean;\n /**\n * Optional click handler. The default impl forwards to onClick if\n * present; in a deterministic renderer pass `noEvents` instead.\n */\n onClick?: React.MouseEventHandler<HTMLButtonElement>;\n}\n\nexport function Button({\n label,\n variant = 'primary',\n disabled = false,\n onClick,\n}: ButtonProps): React.JSX.Element {\n const styles: React.CSSProperties = {\n fontFamily: 'inherit',\n fontSize: '14px',\n fontWeight: 500,\n padding: '8px 14px',\n borderRadius: radius.md,\n border: '1px solid transparent',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n background:\n variant === 'primary'\n ? colors.primary\n : variant === 'secondary'\n ? colors.secondaryFg\n : 'transparent',\n color:\n variant === 'ghost' ? colors.ghostFg : variant === 'secondary' ? colors.bg : colors.primaryFg,\n borderColor: variant === 'ghost' ? colors.border : 'transparent',\n };\n return (\n <button type=\"button\" disabled={disabled} onClick={onClick} style={styles} data-pgx=\"Button\">\n {label}\n </button>\n );\n}\n","import * as React from 'react';\nimport { colors, radius } from './theme.js';\n\nexport interface TextFieldProps {\n label?: string;\n value?: string;\n placeholder?: string;\n disabled?: boolean;\n /**\n * onChange is reported as `node` in the schema (it must be a\n * function reference, which the validator cannot statically type).\n * We forward to React's controlled input handler.\n */\n onChange?: React.ChangeEventHandler<HTMLInputElement>;\n}\n\n/**\n * Controlled text input. Without `value`/`onChange`, behaves as\n * uncontrolled (uses its own state via defaultValue). When a stable\n * (deterministic) caller wires both, the input is fully controlled.\n */\nexport function TextField({\n label,\n value,\n placeholder,\n disabled = false,\n onChange,\n}: TextFieldProps): React.JSX.Element {\n const wrapperStyle: React.CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: '4px',\n fontFamily: 'inherit',\n };\n const labelStyle: React.CSSProperties = {\n fontSize: '12px',\n fontWeight: 500,\n color: colors.mutedFg,\n };\n const inputStyle: React.CSSProperties = {\n fontFamily: 'inherit',\n fontSize: '14px',\n padding: '8px 10px',\n border: `1px solid ${colors.border}`,\n borderRadius: radius.md,\n background: colors.bg,\n color: colors.fg,\n outline: 'none',\n };\n const inputProps: React.InputHTMLAttributes<HTMLInputElement> = {\n placeholder,\n disabled,\n onChange,\n };\n // Use uncontrolled defaultValue when caller omits value. We can't\n // set defaultValue AND value without React warning, so branch.\n if (value !== undefined) inputProps.value = value;\n else inputProps.defaultValue = '';\n return (\n <label style={wrapperStyle} data-pgx=\"TextField\">\n {label !== undefined ? <span style={labelStyle}>{label}</span> : null}\n <input {...inputProps} style={inputStyle} />\n </label>\n );\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\n\nexport interface SliderProps {\n /** Lower bound, inclusive. Required. */\n min: number;\n /** Upper bound, inclusive. Required. */\n max: number;\n /** Initial / controlled value. */\n value?: number;\n /** Increment between stops. Defaults to 1. */\n step?: number;\n /** Optional caption rendered above the slider. */\n label?: string;\n}\n\n/**\n * Range input. State: internal `value` mirrors props.value if present\n * (otherwise uncontrolled); clamps to [min, max].\n */\nexport function Slider({ min, max, value, step = 1, label }: SliderProps): React.JSX.Element {\n // Controlled vs uncontrolled: React forbids both at once. Branch.\n const [internal, setInternal] = React.useState(() => clamp(value ?? min, min, max));\n const controlled = value !== undefined;\n const current = controlled ? clamp(value, min, max) : internal;\n const trackStyle: React.CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: '6px',\n fontFamily: 'inherit',\n width: '100%',\n };\n const labelStyle: React.CSSProperties = {\n fontSize: '12px',\n color: colors.mutedFg,\n };\n const wrapStyle: React.CSSProperties = { display: 'flex', alignItems: 'center', gap: '10px' };\n const inputStyle: React.CSSProperties = { flex: 1, accentColor: colors.sliderFill };\n const valueStyle: React.CSSProperties = {\n minWidth: '32px',\n textAlign: 'right',\n fontVariantNumeric: 'tabular-nums',\n fontSize: '14px',\n color: colors.fg,\n };\n return (\n <div style={trackStyle} data-pgx=\"Slider\">\n {label !== undefined ? <span style={labelStyle}>{label}</span> : null}\n <div style={wrapStyle}>\n <input\n type=\"range\"\n min={min}\n max={max}\n step={step}\n value={current}\n disabled={min === max}\n aria-label={label}\n onChange={(e) => {\n const next = Number(e.currentTarget.value);\n if (!controlled) setInternal(next);\n }}\n style={inputStyle}\n />\n <span style={valueStyle}>{current}</span>\n </div>\n </div>\n );\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n if (Number.isNaN(n)) return lo;\n return Math.min(hi, Math.max(lo, n));\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\n\nexport type ChartKind = 'bar' | 'line' | 'pie';\n\n/**\n * The schema lets `data` be any ReactNode, but in practice callers\n * pass JSON-shaped arrays. We narrow at runtime:\n *\n * - bar: `{ labels: string[], values: number[] }`\n * - line: `{ x: number[], y: number[] }` or `{ series: { label, points: {x,y} }[] }`\n * - pie: `{ labels: string[], values: number[] }`\n *\n * Anything that doesn't fit is rendered as a `<pre>` fallback.\n */\nexport interface ChartProps {\n kind: ChartKind;\n data: unknown;\n title?: string;\n}\n\nexport function Chart({ kind, data, title }: ChartProps): React.JSX.Element {\n return (\n <div data-pgx=\"Chart\" data-chart-kind={kind} style={{ fontFamily: 'inherit' }}>\n {title !== undefined ? (\n <div style={{ fontSize: '14px', fontWeight: 600, color: colors.fg, marginBottom: '6px' }}>\n {title}\n </div>\n ) : null}\n {renderChart(kind, data)}\n </div>\n );\n}\n\nfunction renderChart(kind: ChartKind, data: unknown): React.JSX.Element {\n if (kind === 'bar') return renderBar(data);\n if (kind === 'line') return renderLine(data);\n return renderPie(data);\n}\n\nfunction renderBar(data: unknown): React.JSX.Element {\n const parsed = parseLabeled(data);\n if (!parsed) return fallback('bar chart', data);\n const { labels, values } = parsed;\n const max = Math.max(1, ...values);\n const width = 320;\n const height = 160;\n const barWidth = width / Math.max(1, labels.length);\n return (\n <svg\n width={width}\n height={height}\n role=\"img\"\n aria-label=\"Bar chart\"\n style={{ display: 'block' }}\n >\n {values.map((v, i) => {\n const h = (Math.max(0, v) / max) * (height - 24);\n return (\n <g key={i}>\n <rect\n x={i * barWidth + 2}\n y={height - 12 - h}\n width={Math.max(1, barWidth - 4)}\n height={h}\n fill={colors.bar}\n rx={2}\n />\n <text\n x={i * barWidth + barWidth / 2}\n y={height - 2}\n fontSize={10}\n textAnchor=\"middle\"\n fill={colors.barLabel}\n >\n {(labels[i] ?? '').slice(0, 8)}\n </text>\n </g>\n );\n })}\n </svg>\n );\n}\n\nfunction renderLine(data: unknown): React.JSX.Element {\n // Two accepted shapes; pick whichever parses cleanly.\n let points: Array<{ x: number; y: number }> = [];\n if (data && typeof data === 'object' && 'series' in (data as Record<string, unknown>)) {\n const series = (data as { series: Array<{ points?: Array<{ x: number; y: number }> }> }).series;\n const first = series?.[0];\n points = Array.isArray(first?.points) ? first.points : [];\n } else if (\n data &&\n typeof data === 'object' &&\n Array.isArray((data as { x?: unknown }).x) &&\n Array.isArray((data as { y?: unknown }).y)\n ) {\n const xArr = (data as { x: number[] }).x;\n const yArr = (data as { y: number[] }).y;\n points = xArr.map((x, i) => ({ x, y: yArr[i] ?? 0 }));\n }\n if (points.length === 0) return fallback('line chart', data);\n const xs = points.map((p) => p.x);\n const ys = points.map((p) => p.y);\n const xMin = Math.min(...xs);\n const xMax = Math.max(...xs);\n const yMin = Math.min(...ys);\n const yMax = Math.max(...ys);\n const w = 320;\n const h = 160;\n const sx = (x: number) => (xMax === xMin ? w / 2 : ((x - xMin) / (xMax - xMin)) * (w - 8) + 4);\n const sy = (y: number) =>\n yMax === yMin ? h / 2 : h - ((y - yMin) / (yMax - yMin)) * (h - 24) - 12;\n const path = points\n .map((p, i) => `${i === 0 ? 'M' : 'L'}${sx(p.x).toFixed(2)},${sy(p.y).toFixed(2)}`)\n .join(' ');\n return (\n <svg width={w} height={h} role=\"img\" aria-label=\"Line chart\" style={{ display: 'block' }}>\n <path d={path} fill=\"none\" stroke={colors.line} strokeWidth={2} />\n {points.map((p, i) => (\n <circle key={i} cx={sx(p.x)} cy={sy(p.y)} r={2} fill={colors.line} />\n ))}\n </svg>\n );\n}\n\nfunction renderPie(data: unknown): React.JSX.Element {\n const parsed = parseLabeled(data);\n if (!parsed) return fallback('pie chart', data);\n const total = parsed.values.reduce((a, b) => a + Math.max(0, b), 0) || 1;\n const cx = 80;\n const cy = 80;\n const r = 60;\n let angle = -Math.PI / 2;\n return (\n <svg width={160} height={160} role=\"img\" aria-label=\"Pie chart\" style={{ display: 'block' }}>\n {parsed.values.map((v, i) => {\n const slice = (Math.max(0, v) / total) * Math.PI * 2;\n const x1 = cx + r * Math.cos(angle);\n const y1 = cy + r * Math.sin(angle);\n const x2 = cx + r * Math.cos(angle + slice);\n const y2 = cy + r * Math.sin(angle + slice);\n const large = slice > Math.PI ? 1 : 0;\n const path =\n slice >= Math.PI * 2 - 1e-6\n ? `M ${cx - r} ${cy} A ${r} ${r} 0 1 1 ${cx + r} ${cy} A ${r} ${r} 0 1 1 ${cx - r} ${cy}`\n : `M ${cx} ${cy} L ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2} Z`;\n angle += slice;\n const fill = colors.pie[i % colors.pie.length] ?? colors.bar;\n return <path key={i} d={path} fill={fill} />;\n })}\n </svg>\n );\n}\n\nfunction parseLabeled(data: unknown): { labels: string[]; values: number[] } | null {\n if (!data || typeof data !== 'object') return null;\n const labels = (data as { labels?: unknown }).labels;\n const values = (data as { values?: unknown }).values;\n if (!Array.isArray(labels) || !Array.isArray(values)) return null;\n if (labels.length !== values.length) return null;\n if (!labels.every((l) => typeof l === 'string' || typeof l === 'number')) return null;\n if (!values.every((v) => typeof v === 'number' || typeof v === 'string')) return null;\n return {\n labels: labels.map(String),\n values: values.map((v) => (typeof v === 'number' ? v : Number(v))),\n };\n}\n\nfunction fallback(label: string, data: unknown): React.JSX.Element {\n return (\n <pre\n style={{\n margin: 0,\n fontSize: '12px',\n background: colors.codeBg,\n color: colors.codeFg,\n padding: '8px',\n borderRadius: '4px',\n overflow: 'auto',\n }}\n >\n {label} data: {JSON.stringify(data, null, 2)}\n </pre>\n );\n}\n","import * as React from 'react';\n\nexport interface ContainerProps {\n padding?: string;\n gap?: string;\n children?: React.ReactNode;\n}\n\n/**\n * Layout wrapper. Defaults to a vertical flex container with 16px\n * padding and 12px gap when callers omit props.\n */\nexport function Container({ padding, gap, children }: ContainerProps): React.JSX.Element {\n const style: React.CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n padding: padding ?? '16px',\n gap: gap ?? '12px',\n fontFamily: 'inherit',\n color: 'inherit',\n boxSizing: 'border-box',\n };\n return (\n <div style={style} data-pgx=\"Container\">\n {children}\n </div>\n );\n}\n","import * as React from 'react';\nimport { colors, radius } from './theme.js';\n\nexport interface CodeProps {\n language?: string;\n children?: React.ReactNode;\n}\n\n/**\n * Inline code formatter. Renders children as a `<code>` element inside\n * a styled wrapper. If `children` is a single string, it displays\n * verbatim; for multi-line content we fall back to a `<pre>`-styled\n * block.\n */\nexport function Code({ language, children }: CodeProps): React.JSX.Element {\n const isString = typeof children === 'string';\n const isMulti = isString && (children as string).includes('\\n');\n if (isMulti) {\n return (\n <pre\n data-pgx=\"Code\"\n data-language={language ?? 'text'}\n style={{\n background: colors.codeBg,\n color: colors.codeFg,\n padding: '12px',\n borderRadius: radius.md,\n fontSize: '12px',\n overflow: 'auto',\n margin: 0,\n }}\n >\n <code>{children}</code>\n </pre>\n );\n }\n return (\n <code\n data-pgx=\"Code-inline\"\n data-language={language ?? 'text'}\n style={{\n background: colors.codeBg,\n color: colors.codeFg,\n padding: '2px 6px',\n borderRadius: radius.sm,\n fontSize: '0.9em',\n }}\n >\n {children}\n </code>\n );\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\n\nexport interface HeadingProps {\n /**\n * Heading level (1..6). Out-of-range or undefined falls back to 2.\n * The renderer should never see an `Undefined` value here unless the\n * schema validator said optional props can be omitted.\n */\n level?: number;\n color?: string;\n children?: React.ReactNode;\n}\n\nconst sizes: Record<number, string> = {\n 1: '24px',\n 2: '20px',\n 3: '18px',\n 4: '16px',\n 5: '14px',\n 6: '13px',\n};\n\nexport function Heading({ level, color, children }: HeadingProps): React.JSX.Element {\n const safeLevel = clampLevel(level);\n const size = sizes[safeLevel] ?? sizes[2]!;\n const style: React.CSSProperties = {\n fontFamily: 'inherit',\n fontSize: size,\n fontWeight: 700,\n color: color ?? colors.fg,\n margin: 0,\n lineHeight: 1.3,\n };\n // Render an element corresponding to the level. React enforces\n // TagName = 'h1' | 'h2' ... via JSX, so we switch dynamically via\n // createElement.\n return React.createElement(`h${safeLevel}`, { style, 'data-pgx': `Heading` }, children);\n}\n\nfunction clampLevel(level: number | undefined): 1 | 2 | 3 | 4 | 5 | 6 {\n if (typeof level !== 'number' || !Number.isFinite(level)) return 2;\n const i = Math.floor(level);\n if (i < 1) return 1;\n if (i > 6) return 6;\n return i as 1 | 2 | 3 | 4 | 5 | 6;\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\n\nexport interface TextProps {\n weight?: 'normal' | 'bold';\n size?: string;\n color?: string;\n children?: React.ReactNode;\n}\n\nexport function Text({ weight = 'normal', size, color, children }: TextProps): React.JSX.Element {\n const style: React.CSSProperties = {\n fontFamily: 'inherit',\n fontSize: size ?? '14px',\n fontWeight: weight === 'bold' ? 600 : 400,\n color: color ?? colors.fg,\n margin: 0,\n lineHeight: 1.5,\n };\n return (\n <p style={style} data-pgx=\"Text\">\n {children}\n </p>\n );\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\n\nexport interface Step {\n id: string;\n title: string;\n body?: React.ReactNode;\n}\n\nexport interface StepperProps {\n steps: Step[];\n /** 0-indexed initial step. Out-of-range falls back to 0. */\n initial?: number;\n}\n\n/**\n * Multi-step reveal. State: the active index. Previous steps can be\n * revisited; future steps are disabled until reached.\n */\nexport function Stepper({ steps, initial }: StepperProps): React.JSX.Element {\n const initialIdx = clampIndex(initial ?? 0, steps.length);\n const [active, setActive] = React.useState(initialIdx);\n const wrapStyle: React.CSSProperties = {\n fontFamily: 'inherit',\n display: 'flex',\n flexDirection: 'column',\n gap: '12px',\n };\n const listStyle: React.CSSProperties = {\n display: 'flex',\n gap: '6px',\n listStyle: 'none',\n padding: 0,\n margin: 0,\n };\n const stepBtnBase: React.CSSProperties = {\n width: '28px',\n height: '28px',\n borderRadius: '50%',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontFamily: 'inherit',\n fontSize: '12px',\n fontWeight: 600,\n border: '1px solid transparent',\n };\n const bodyStyle: React.CSSProperties = {\n border: `1px solid ${colors.border}`,\n borderRadius: '6px',\n padding: '10px',\n background: colors.bg,\n color: colors.fg,\n };\n return (\n <div data-pgx=\"Stepper\" style={wrapStyle}>\n <ol style={listStyle}>\n {steps.map((s, i) => {\n const isPast = i < active;\n const isCurrent = i === active;\n const future = i > active;\n const stepStyle: React.CSSProperties = {\n ...stepBtnBase,\n background: isCurrent\n ? colors.stepperCurrent\n : isPast\n ? colors.stepperPast\n : colors.stepperFuture,\n color: isCurrent || isPast ? '#fff' : colors.fg,\n opacity: future ? 0.6 : 1,\n cursor: future ? 'not-allowed' : 'pointer',\n };\n return (\n <li key={s.id}>\n <button\n type=\"button\"\n aria-label={`Step ${i + 1}: ${s.title}`}\n aria-current={isCurrent ? 'step' : undefined}\n disabled={future}\n onClick={() => setActive(i)}\n style={stepStyle}\n >\n {i + 1}\n </button>\n </li>\n );\n })}\n </ol>\n <div style={bodyStyle}>\n <div style={{ fontWeight: 600, marginBottom: '4px', fontSize: '14px' }}>\n {steps[active]?.title ?? ''}\n </div>\n <div style={{ fontSize: '13px' }}>{steps[active]?.body ?? null}</div>\n </div>\n </div>\n );\n}\n\nfunction clampIndex(n: number, len: number): number {\n if (len === 0) return 0;\n if (!Number.isFinite(n)) return 0;\n return Math.min(len - 1, Math.max(0, Math.floor(n)));\n}\n","import * as React from 'react';\nimport { colors, radius, space } from './theme.js';\n\nexport interface CardProps {\n title?: string;\n /**\n * Visual elevation. 0 = flat (border only), 1+ = heavier shadow.\n * Defaults to 1.\n */\n elevation?: number;\n children?: React.ReactNode;\n}\n\nexport function Card({ title, elevation = 1, children }: CardProps): React.JSX.Element {\n const shadow =\n elevation >= 3\n ? '0 4px 12px rgba(15, 23, 42, 0.18)'\n : elevation === 2\n ? '0 2px 8px rgba(15, 23, 42, 0.12)'\n : elevation === 1\n ? '0 1px 2px rgba(15, 23, 42, 0.06)'\n : 'none';\n const cardStyle: React.CSSProperties = {\n background: colors.cardBg,\n border: `1px solid ${colors.cardBorder}`,\n borderRadius: radius.md,\n boxShadow: shadow,\n padding: space.md,\n fontFamily: 'inherit',\n color: colors.fg,\n display: 'flex',\n flexDirection: 'column',\n gap: space.sm,\n };\n const titleStyle: React.CSSProperties = {\n fontSize: '14px',\n fontWeight: 600,\n margin: 0,\n };\n return (\n <section data-pgx=\"Card\" style={cardStyle}>\n {title !== undefined ? <h3 style={titleStyle}>{title}</h3> : null}\n {children}\n </section>\n );\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\n\nexport interface ListProps {\n /**\n * Items to render. Schema marks this as `node`; in practice callers\n * pass arrays of strings/numbers/ReactNodes. Anything else is\n * rendered as a single bullet containing the value.\n */\n items?: unknown;\n /** When true, renders as `<ol>` instead of `<ul>`. */\n ordered?: boolean;\n /** Override the wrapper element for full control. */\n children?: React.ReactNode;\n}\n\n/**\n * List of items. Renders `items` as a list of strings/numbers (the\n * common artifact prompt output). If `children` are passed they take\n * precedence over `items`.\n */\nexport function List({ items, ordered, children }: ListProps): React.JSX.Element {\n const style: React.CSSProperties = {\n margin: 0,\n paddingLeft: '20px',\n fontFamily: 'inherit',\n color: colors.fg,\n fontSize: '14px',\n lineHeight: 1.6,\n };\n if (children !== undefined && children !== null) {\n return React.createElement(ordered ? 'ol' : 'ul', { style, 'data-pgx': 'List' }, children);\n }\n const arr = Array.isArray(items) ? items : items === undefined ? [] : [items];\n return React.createElement(\n ordered ? 'ol' : 'ul',\n { style, 'data-pgx': 'List' },\n arr.map((item, i) => {\n const node: React.ReactNode =\n typeof item === 'string' || typeof item === 'number' ? String(item) : JSON.stringify(item);\n return <li key={i}>{node}</li>;\n }),\n );\n}\n","/**\n * Default @playgenx/components barrel.\n *\n * Re-exports every component named in `DEFAULT_REGISTRY` and a\n * `componentMap` keyed by PascalCase component name, so render-time\n * code can do `ComponentMap[tagName](props)`.\n *\n * @packageDocumentation\n */\n\nexport { Button, type ButtonProps } from './Button.js';\nexport { TextField, type TextFieldProps } from './TextField.js';\nexport { Slider, type SliderProps } from './Slider.js';\nexport { Chart, type ChartProps, type ChartKind } from './Chart.js';\nexport { Container, type ContainerProps } from './Container.js';\nexport { Code, type CodeProps } from './Code.js';\nexport { Heading, type HeadingProps } from './Heading.js';\nexport { Text, type TextProps } from './Text.js';\nexport { Stepper, type StepperProps, type Step } from './Stepper.js';\nexport { Card, type CardProps } from './Card.js';\nexport { List, type ListProps } from './List.js';\n\nimport type { ComponentType } from 'react';\nimport { Button } from './Button.js';\nimport { TextField } from './TextField.js';\nimport { Slider } from './Slider.js';\nimport { Chart } from './Chart.js';\nimport { Container } from './Container.js';\nimport { Code } from './Code.js';\nimport { Heading } from './Heading.js';\nimport { Text } from './Text.js';\nimport { Stepper } from './Stepper.js';\nimport { Card } from './Card.js';\nimport { List } from './List.js';\n\n/**\n * Map of PascalCase component name to React component. Use this when\n * you have a renderer that walks an AST and needs to pick an\n * implementation at runtime:\n *\n * const C = componentMap[tagName];\n * if (C) el = <C {...props}>{children}</C>;\n *\n * Keys mirror `DEFAULT_REGISTRY.list()`.\n *\n * Type note: components vary in their props shape, so the map is\n * typed permissively (`Record<string, ComponentType<any>>`). Each\n * individual export above is still strictly typed.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport const componentMap: Record<string, ComponentType<any>> = {\n Button,\n TextField,\n Slider,\n Chart,\n Container,\n Code,\n Heading,\n Text,\n Stepper,\n Card,\n List,\n};\n\nexport type ComponentMapKey = keyof typeof componentMap;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["internal"],"sources":["../src/theme.ts","../src/Button.tsx","../src/TextField.tsx","../src/state.tsx","../src/Slider.tsx","../src/Chart.tsx","../src/Container.tsx","../src/Code.tsx","../src/Heading.tsx","../src/Text.tsx","../src/Stepper.tsx","../src/Card.tsx","../src/List.tsx","../src/stateHelpers.ts","../src/ShowSource.tsx","../src/ArtifactErrorBoundary.tsx","../src/createRegistry.ts"],"sourcesContent":["/**\n * Shared theme tokens for @playgenx/components.\n *\n * All styling is driven from this file. There is intentionally no\n * \"config\" theme or runtime theme provider — these are the default\n * implementations of the registry, and a downstream host app can swap\n * them out entirely. Tokens live as constants so they're stable\n * across renders and don't allocate per call.\n */\n\nexport const colors = {\n bg: '#ffffff',\n fg: '#0f172a',\n mutedFg: '#475569',\n border: '#e2e8f0',\n primary: '#2563eb',\n primaryFg: '#ffffff',\n secondaryFg: '#0f172a',\n ghostFg: '#1d4ed8',\n cardBg: '#f8fafc',\n cardBorder: '#e2e8f0',\n codeBg: '#f1f5f9',\n codeFg: '#0f172a',\n sliderTrack: '#cbd5e1',\n sliderFill: '#2563eb',\n sliderThumb: '#1d4ed8',\n bar: '#2563eb',\n barLabel: '#0f172a',\n pie: ['#2563eb', '#16a34a', '#f59e0b', '#dc2626'],\n line: '#2563eb',\n stepperPast: '#16a34a',\n stepperCurrent: '#2563eb',\n stepperFuture: '#cbd5e1',\n} as const;\n\nexport const space = {\n none: '0',\n xs: '4px',\n sm: '8px',\n md: '12px',\n lg: '16px',\n xl: '24px',\n} as const;\n\nexport const radius = {\n sm: '4px',\n md: '6px',\n lg: '8px',\n} as const;\n\n/**\n * Common React-friendly prop accepts for content. Components that\n * accept children of arbitrary shape (Heading, Text, Card, Container,\n * ...) use this so the host can pass strings or JSX interchangeably.\n */\nexport type Children = string | number | React.ReactNode;\n\nimport type * as React from 'react';\n","import * as React from 'react';\nimport { colors, radius } from './theme.js';\n\n/**\n * Headless-style wrapper over `<button>` accepting the props defined in\n * `DEFAULT_COMPONENT_SCHEMAS.Button`. Pass `onClick` through; this\n * implementation does NOT execute arbitrary functions (deterministic\n * renderer constraint) so the click handler is rendered inert at mount.\n */\nexport interface ButtonProps {\n /** Visible text. Required. */\n label: string;\n /** Variant. Defaults to 'primary'. */\n variant?: 'primary' | 'secondary' | 'ghost';\n /** Disabled state. Defaults to false. */\n disabled?: boolean;\n /**\n * Optional click handler. The default impl forwards to onClick if\n * present; in a deterministic renderer pass `noEvents` instead.\n */\n onClick?: React.MouseEventHandler<HTMLButtonElement>;\n}\n\nexport function Button({\n label,\n variant = 'primary',\n disabled = false,\n onClick,\n}: ButtonProps): React.JSX.Element {\n const styles: React.CSSProperties = {\n fontFamily: 'inherit',\n fontSize: '14px',\n fontWeight: 500,\n padding: '8px 14px',\n borderRadius: radius.md,\n border: '1px solid transparent',\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n background:\n variant === 'primary'\n ? colors.primary\n : variant === 'secondary'\n ? colors.secondaryFg\n : 'transparent',\n color:\n variant === 'ghost' ? colors.ghostFg : variant === 'secondary' ? colors.bg : colors.primaryFg,\n borderColor: variant === 'ghost' ? colors.border : 'transparent',\n };\n return (\n <button type=\"button\" disabled={disabled} onClick={onClick} style={styles} data-pgx=\"Button\">\n {label}\n </button>\n );\n}\n","import * as React from 'react';\nimport { colors, radius } from './theme.js';\n\nexport interface TextFieldProps {\n label?: string;\n value?: string;\n placeholder?: string;\n disabled?: boolean;\n /**\n * onChange is reported as `node` in the schema (it must be a\n * function reference, which the validator cannot statically type).\n * We forward to React's controlled input handler.\n */\n onChange?: React.ChangeEventHandler<HTMLInputElement>;\n}\n\n/**\n * Controlled text input. Without `value`/`onChange`, behaves as\n * uncontrolled (uses its own state via defaultValue). When a stable\n * (deterministic) caller wires both, the input is fully controlled.\n */\nexport function TextField({\n label,\n value,\n placeholder,\n disabled = false,\n onChange,\n}: TextFieldProps): React.JSX.Element {\n const wrapperStyle: React.CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: '4px',\n fontFamily: 'inherit',\n };\n const labelStyle: React.CSSProperties = {\n fontSize: '12px',\n fontWeight: 500,\n color: colors.mutedFg,\n };\n const inputStyle: React.CSSProperties = {\n fontFamily: 'inherit',\n fontSize: '14px',\n padding: '8px 10px',\n border: `1px solid ${colors.border}`,\n borderRadius: radius.md,\n background: colors.bg,\n color: colors.fg,\n outline: 'none',\n };\n const inputProps: React.InputHTMLAttributes<HTMLInputElement> = {\n placeholder,\n disabled,\n onChange,\n };\n // Use uncontrolled defaultValue when caller omits value. We can't\n // set defaultValue AND value without React warning, so branch.\n if (value !== undefined) inputProps.value = value;\n else inputProps.defaultValue = '';\n return (\n <label style={wrapperStyle} data-pgx=\"TextField\">\n {label !== undefined ? <span style={labelStyle}>{label}</span> : null}\n <input {...inputProps} style={inputStyle} />\n </label>\n );\n}\n","/**\n * Per-render interactive state store. Used by PlaygroundStateProvider to\n * back interactive components (Slider, TextField, Button, Chart) without\n * requiring them to be fully controlled.\n *\n * Design notes:\n * - The store is a singleton instance per Provider, NOT React context.\n * Components consume it via usePlaygroundState() inside React renders,\n * and via .subscribe() / .get() / .set() from non-React callers.\n * - `set` is synchronous; subscribers fire in registration order on the\n * next microtask (so multiple sets in a single tick produce a single\n * re-render per subscriber).\n * - `set` with `===` equal value is a no-op (no subscriber fires).\n * - Throws if used outside a PlaygroundStateProvider (when required by\n * the caller's contract).\n *\n * @packageDocumentation\n */\n\nimport * as React from 'react';\n\n/**\n * Per-render state store. Components read/write values keyed by string.\n * One store per Provider instance; nested Providers create nested scopes.\n */\nexport interface PlaygroundState {\n /** Read a value by key. Returns undefined if unset. */\n get<T = unknown>(key: string): T | undefined;\n /**\n * Set a value. Triggers re-render of subscribers. No-op if the new\n * value is `===` to the current value.\n */\n set<T = unknown>(key: string, value: T): void;\n /**\n * Subscribe to changes on a single key. The callback fires on every\n * `set(key, ...)` with a different value. Returns an unsubscribe\n * function. Non-React callers can use this directly.\n */\n subscribe(key: string, cb: (value: unknown) => void): () => void;\n /**\n * Bulk-read: returns a shallow snapshot of all keys. Useful for\n * snapshotting state at a moment in time (e.g., for undo).\n */\n snapshot(): Readonly<Record<string, unknown>>;\n /** Bulk-write: replace all values. Subscribers fire for changed keys. */\n replaceAll(values: Record<string, unknown>): void;\n}\n\ninterface InternalState extends PlaygroundState {\n /** Test seam: forces sync-fire for unit tests. */\n _flush(): void;\n /**\n * Test seam: run `fn` with batch dedup enabled. While inside the\n * batch, repeated `set(key, ...)` calls collapse to a single\n * subscriber fire per key with the LAST value set. Returns\n * whatever `fn` returns.\n *\n * This is the same primitive as the `batch(state, fn)` helper in\n * stateHelpers.ts but exposed as a method for callers that already\n * hold a state ref.\n */\n _batch<T>(fn: () => T): T;\n /**\n * Internal seam: drop every subscriber from every key. Used by\n * PlaygroundStateProvider's unmount cleanup to prevent HMR\n * subscriber leaks. Not part of the public PlaygroundState\n * surface — do not call from app code (you have `subscribe()`'s\n * returned unsub function for the public path).\n */\n _clearAllSubscribers(): void;\n}\n\n/**\n * Create a fresh state store. Public so PlaygroundStateProvider can\n * construct one, but consumers should use Provider/usePlaygroundState\n * to access stores (not direct construction).\n *\n * The optional type parameter is the \"shape\" of values you intend to\n * store. It's purely a documentation convenience — the store does\n * not enforce it (so you can store heterogeneous values for\n * playgrounds that mix kinds), but `get<T>()` and `set<T>()` will\n * infer correctly when you pass it here.\n */\nexport function createPlaygroundState<T = unknown>(\n initial: Record<string, T> = {},\n): PlaygroundState {\n const values = new Map<string, unknown>(Object.entries(initial));\n // key -> Set of subscribers. We use Set for O(1) add/remove and\n // dedup of identical subscribers.\n const subs = new Map<string, Set<(value: unknown) => void>>();\n\n // Pending (key, value) pairs to fire on the next microtask. We\n // batch so a sequence of `set()` calls in the same tick produces a\n // single subscriber fire per affected key.\n let pending: Array<{ key: string; value: unknown }> = [];\n let scheduled = false;\n // Transactional batching: when `batchDepth > 0`, set() pushes to\n // `batchedPending` instead of `pending`. flush() flushes the\n // NON-batched pending queue (preserving history) but the batched\n // queue is collapsed: only the LAST value per key is kept. This\n // is what `batch(state, fn)` enables — atomic, deduplicated commits.\n let batchDepth = 0;\n let batchedPending: Array<{ key: string; value: unknown }> = [];\n\n const flush = (): void => {\n if (pending.length === 0) return;\n const batch = pending;\n pending = [];\n scheduled = false;\n // Fire every pending (key, value) pair, in order. We do NOT\n // dedupe — each `set()` call is a distinct event and subscribers\n // want to see the full history (e.g. for analytics). React\n // batches re-renders via the microtask flush anyway, so multiple\n // fires for the same key collapse into one render.\n for (const { key, value } of batch) {\n const list = subs.get(key);\n if (list) {\n // Snapshot the subscriber set so callbacks that unsubscribe\n // themselves mid-fire don't mutate the iteration source.\n for (const cb of [...list]) {\n try {\n cb(value);\n } catch (err) {\n // Swallow subscriber errors so one bad subscriber doesn't\n // take down the whole tree. In development we re-raise\n // the error to console.error so the bad subscriber is\n // diagnosable from the browser devtools; in production\n // we stay silent to avoid log spam.\n if (process.env.NODE_ENV !== 'production') {\n console.error(\n '[playgenx] subscriber for key %s threw:',\n key,\n err,\n );\n }\n }\n }\n }\n }\n };\n\n const schedule = (): void => {\n if (scheduled) return;\n scheduled = true;\n // queueMicrotask is available in Node 22+ and all modern browsers.\n // Falls back to Promise.resolve().then for older runtimes.\n if (typeof queueMicrotask === 'function') {\n queueMicrotask(flush);\n } else {\n Promise.resolve().then(flush);\n }\n };\n\n const state: InternalState = {\n get<T = unknown>(key: string): T | undefined {\n return values.get(key) as T | undefined;\n },\n set<T = unknown>(key: string, value: T): void {\n if (Object.is(values.get(key), value)) return; // no-op\n values.set(key, value);\n // Inside a batch: push to the dedupe queue instead of pending.\n // Outside a batch: push to pending (full-history mode).\n if (batchDepth > 0) {\n batchedPending.push({ key, value });\n } else {\n pending.push({ key, value });\n }\n schedule();\n },\n subscribe(key: string, cb: (value: unknown) => void): () => void {\n let list = subs.get(key);\n if (!list) {\n list = new Set();\n subs.set(key, list);\n }\n list.add(cb);\n return () => {\n const l = subs.get(key);\n if (l) {\n l.delete(cb);\n if (l.size === 0) subs.delete(key);\n }\n };\n },\n snapshot(): Readonly<Record<string, unknown>> {\n return Object.fromEntries(values);\n },\n replaceAll(values: Record<string, unknown>): void {\n for (const [k, v] of Object.entries(values)) {\n state.set(k, v);\n }\n },\n _flush(): void {\n flush();\n },\n _batch<T>(fn: () => T): T {\n batchDepth++;\n try {\n return fn();\n } finally {\n batchDepth--;\n // After the batch: if anything landed in batchedPending,\n // move it to pending AS A SINGLE EVENT PER KEY. Walk in\n // reverse so the LAST set wins (later writes to the same\n // key override earlier ones).\n if (batchDepth === 0 && batchedPending.length > 0) {\n const lastPerKey = new Map<string, unknown>();\n for (let i = batchedPending.length - 1; i >= 0; i--) {\n const { key, value } = batchedPending[i]!;\n if (!lastPerKey.has(key)) {\n lastPerKey.set(key, value);\n pending.push({ key, value });\n }\n }\n batchedPending = [];\n }\n }\n },\n _clearAllSubscribers(): void {\n // Replace with fresh maps; old subscribers are dropped on the\n // GC pass once their closure references go out of scope. This\n // is intentionally a hard reset — used only on Provider\n // unmount, where every subscriber in the tree is going away.\n subs.clear();\n // Also drop any pending fires that would have been delivered\n // to those now-dead subscribers. Without this, a stale set\n // queued just before unmount would still fire `cb(value)` on\n // a callback that has no consumer.\n pending = [];\n batchedPending = [];\n scheduled = false;\n },\n };\n return state;\n}\n\ninterface ProviderContextValue {\n state: PlaygroundState;\n}\n\n/**\n * React context. Exposed as a type only — consumers should use\n * PlaygroundStateProvider and usePlaygroundState rather than reading\n * the context directly.\n */\nconst PlaygroundStateContext = React.createContext<ProviderContextValue | null>(\n null,\n);\n\nexport interface PlaygroundStateProviderProps {\n /** Initial state values keyed by name. */\n initial?: Record<string, unknown>;\n /**\n * Optional pre-existing state. If supplied, `initial` is merged on\n * top (initial wins for overlapping keys). Use this for SSR\n * rehydration or seeded values.\n */\n seed?: Record<string, unknown>;\n children: React.ReactNode;\n}\n\n/**\n * Provide a PlaygroundState scope to descendant components.\n *\n * Each Provider instance has its own store; nested Providers create\n * independent scopes. Components rendered outside any Provider will\n * throw a clear error if they call usePlaygroundState.\n */\nexport function PlaygroundStateProvider(\n props: PlaygroundStateProviderProps,\n): React.JSX.Element {\n // Create the store once per Provider instance. The lazy initializer\n // ensures re-mounting (e.g. HMR) doesn't carry stale state across\n // refreshes.\n const [state] = React.useState(() => {\n const s = createPlaygroundState({ ...props.seed, ...props.initial });\n return s;\n });\n\n // Cleanup on unmount: clear all subscribers so a hot-reload doesn't\n // leak subscriptions across renders. Without this, every HMR cycle\n // would leave the OLD store with stale subscribers that fire on\n // every set — even though no component is listening.\n //\n // Why we don't drop the values Map: by the time this cleanup runs,\n // the closure over the store is unreachable. Letting the GC collect\n // it is enough. Subscribers are the leak risk because they're\n // closures that reference component-local state (setValue, force).\n React.useEffect(() => {\n return () => {\n // Clear all subscribers via the test seam. (The store doesn't\n // expose a public clear-all API because removing subscribers\n // without notifying them is almost always a bug. Unmount is\n // the one legitimate case.)\n (state as unknown as { _clearAllSubscribers?: () => void })\n ._clearAllSubscribers?.();\n };\n }, [state]);\n\n const value = React.useMemo(() => ({ state }), [state]);\n\n return React.createElement(\n PlaygroundStateContext.Provider,\n { value },\n props.children,\n );\n}\n\n/**\n * Hook to access the nearest PlaygroundState store. Throws a clear\n * error if called outside a Provider so consumers get a usable\n * diagnostic instead of a `Cannot read properties of undefined`.\n *\n * Note for @playgenx/renderer users: the renderer's `renderBody`\n * does NOT yet auto-wrap with a PlaygroundStateProvider. You'll\n * need to wrap your own tree with `<PlaygroundStateProvider>` (or\n * use the lower-level `renderNodes` API) before state-bound\n * components can resolve. The `withState` option on `renderBody`\n * is planned for a future renderer release (tracked in v0.6.0).\n */\nexport function usePlaygroundState(): PlaygroundState {\n const ctx = React.useContext(PlaygroundStateContext);\n if (ctx === null) {\n throw new Error(\n 'usePlaygroundState() must be called inside a <PlaygroundStateProvider>. ' +\n 'Wrap your component tree with the Provider. (A future renderer ' +\n 'release will auto-wrap; for now, do it explicitly.)',\n );\n }\n return ctx.state;\n}\n\n/**\n * Optional helper for binding a component prop to a state key. Returns\n * the live value (re-renders subscribers on change) and a setter. Pass\n * `undefined` for `key` to disable the binding — the component behaves\n * as if it had no stateKey, and `set` becomes a no-op. Safe to call\n * outside a Provider.\n *\n * Implementation note: this hook ALWAYS calls the same number of\n * React hooks regardless of whether `key` is set or a Provider is in\n * scope. The \"no binding\" / \"no Provider\" branch uses dummy local\n * state so the hook count stays aligned across renders. That means a\n * component can flip `stateKey` from defined to undefined (or move in\n * or out of a Provider) without crashing with a hooks-order error.\n *\n * Components that accept a `stateKey` prop should call this internally\n * and use the returned tuple as a drop-in for the underlying\n * value/onChange pair. The return type widens to `readonly [...] | undefined`\n * so the caller can still branch on `if (bound)` — but they don't have\n * to (the tuple is always safe to index).\n *\n * @example\n * const bound = useBoundValueOrUndefined<number>(stateKey, 50);\n * // ... onChange={(e) => bound?.[1](Number(e.currentTarget.value))}\n */\nexport function useBoundValueOrUndefined<T>(\n key: string | undefined,\n fallback: T,\n): readonly [T, (next: T) => void] | undefined {\n // Read the Provider context. `useContext` is unconditional and counts\n // as a hook, but its *value* can be null — that's fine. The point\n // is we never skip hooks based on a runtime condition.\n const ctx = React.useContext(PlaygroundStateContext);\n const bound = key !== undefined && ctx !== null;\n\n // Always allocate the same hook slots so the count is constant.\n // We use `fallback` as the initial value in both branches; if a\n // Provider + key are present the effect below pulls the live value.\n const [value, setValue] = React.useState<T>(fallback);\n const [, force] = React.useState(0);\n React.useEffect(() => {\n if (!bound || key === undefined || ctx === null) return;\n const initial = (ctx.state.get<T>(key) ?? fallback) as T;\n setValue(initial);\n const unsub = ctx.state.subscribe(key, (next) => {\n setValue(next as T);\n force((n) => n + 1);\n });\n return unsub;\n // We deliberately exclude `fallback` from deps: a changing fallback\n // would re-subscribe on every render. The cost is that if `fallback`\n // changes the visible value won't refresh — but that mirrors how\n // useBoundValue behaves (no refresh on fallback change either).\n }, [bound, ctx, key]);\n const set = React.useCallback(\n (next: T) => {\n if (!bound || key === undefined || ctx === null) return; // no-op\n ctx.state.set<T>(key, next);\n },\n [bound, ctx, key],\n );\n // When no binding is active, callers get an explicit `undefined`\n // tuple so they can branch on `if (bound)` without falsey\n // confusion (a `0` or `''` value would otherwise hide the binding\n // state).\n if (!bound) return undefined;\n return [value, set] as const;\n}\n\nexport function useBoundValue<T>(\n key: string,\n fallback: T,\n): readonly [T, (next: T) => void] {\n const bound = useBoundValueOrUndefined<T>(key, fallback);\n if (bound === undefined) {\n throw new Error(\n 'useBoundValue(key, ...) requires both a non-undefined `key` and a ' +\n '<PlaygroundStateProvider> ancestor. Use useBoundValueOrUndefined ' +\n 'if you want optional binding.',\n );\n }\n return bound;\n}\n\n/**\n * State-binding action shape. Used by `<Button onClickAction={...}>`.\n * { set: { count: 1 } } -> sets state.count = 1\n * { toggle: 'open' } -> sets state.open = !state.open\n */\nexport type StateAction =\n | { set?: Record<string, unknown>; toggle?: string }\n | undefined;\n\n/**\n * Apply a StateAction to the nearest PlaygroundState. Returns an\n * onClick handler that fires the action. If action is undefined\n * the handler is a no-op.\n */\nexport function useStateAction(\n action: StateAction,\n): (event: unknown) => void {\n const state = usePlaygroundState();\n // The `event` parameter (from onClick / onSubmit) is the React\n // event the caller hands us. We don't introspect it — actions\n // are identified purely by their `action` config. The eslint\n // config's `argsIgnorePattern` doesn't apply to callback params\n // inside `React.useCallback` closures (it only sees the outer\n // function's params), so we use a void cast to silence the lint\n // while keeping the type signature for callers.\n return React.useCallback(\n (event: unknown) => {\n void event; // explicitly unused\n if (action === undefined) return;\n if (action.set) {\n for (const [k, v] of Object.entries(action.set)) {\n state.set(k, v);\n }\n }\n if (action.toggle !== undefined) {\n const key = action.toggle;\n const current = state.get<unknown>(key);\n state.set(key, !current);\n }\n },\n [action, state],\n );\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\nimport { useBoundValueOrUndefined } from './state.jsx';\n\nexport interface SliderProps {\n /** Lower bound, inclusive. Required. */\n min: number;\n /** Upper bound, inclusive. Required. */\n max: number;\n /** Initial / controlled value. */\n value?: number;\n /** Increment between stops. Defaults to 1. */\n step?: number;\n /** Optional caption rendered above the slider. */\n label?: string;\n /**\n * When set, the slider is \"bound\" to the playground state: initial\n * value comes from state.get(stateKey), onChange writes state.set.\n * `value` prop is ignored when stateKey is present.\n */\n stateKey?: string;\n}\n\n/**\n * Range input. Three operating modes:\n *\n * 1. **Uncontrolled** (no `value`, no `stateKey`): the slider owns\n * its value as local React state. The onChange mutates that state.\n * Use this when nothing else needs to observe the value.\n *\n * 2. **Controlled by `value` prop**: the slider is read-only —\n * user input is ignored because there is no `onChange` callback\n * wired by the deterministic renderer. Use this for \"static\"\n * sliders in artifacts. (If you want fully interactive control,\n * use mode 3.)\n *\n * 3. **Bound to PlaygroundState via `stateKey`**: the slider reads\n * its value from the nearest Provider's store (initial: either\n * the store's current value OR `value` prop, whichever is\n * present; the live store wins when bound). On change, the\n * slider writes to the store. Other components can subscribe\n * to the same key.\n *\n * In all modes the displayed value is clamped to `[min, max]`. NaN\n * inputs clamp to `min`. When `min === max` the input is disabled.\n */\nexport function Slider({ min, max, value, step = 1, label, stateKey }: SliderProps): React.JSX.Element {\n // When stateKey is set, bind to playground state. useBoundValue\n // throws if no Provider is in scope; useBoundValueOrUndefined\n // returns undefined when stateKey is undefined so the rest of the\n // component can use the local-state behaviour.\n const [internal, setInternal] = React.useState(() => clamp(value ?? min, min, max));\n const bound = useBoundValueOrUndefined<number>(stateKey, internal);\n const controlled = value !== undefined;\n // When stateKey is bound, the bound value is the source of truth.\n const current = stateKey !== undefined && bound\n ? clamp(bound[0], min, max)\n : controlled\n ? clamp(value, min, max)\n : internal;\n const trackStyle: React.CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n gap: '6px',\n fontFamily: 'inherit',\n width: '100%',\n };\n const labelStyle: React.CSSProperties = {\n fontSize: '12px',\n color: colors.mutedFg,\n };\n const wrapStyle: React.CSSProperties = { display: 'flex', alignItems: 'center', gap: '10px' };\n const inputStyle: React.CSSProperties = { flex: 1, accentColor: colors.sliderFill };\n const valueStyle: React.CSSProperties = {\n minWidth: '32px',\n textAlign: 'right',\n fontVariantNumeric: 'tabular-nums',\n fontSize: '14px',\n color: colors.fg,\n };\n return (\n <div style={trackStyle} data-pgx=\"Slider\">\n {label !== undefined ? <span style={labelStyle}>{label}</span> : null}\n <div style={wrapStyle}>\n <input\n type=\"range\"\n min={min}\n max={max}\n step={step}\n value={current}\n disabled={min === max}\n aria-label={label}\n onChange={(e) => {\n const next = Number(e.currentTarget.value);\n if (stateKey !== undefined && bound) {\n bound[1](next);\n } else if (!controlled) {\n setInternal(next);\n }\n }}\n style={inputStyle}\n />\n <span style={valueStyle}>{current}</span>\n </div>\n </div>\n );\n}\n\nfunction clamp(n: number, lo: number, hi: number): number {\n if (Number.isNaN(n)) return lo;\n return Math.min(hi, Math.max(lo, n));\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\n\nexport type ChartKind = 'bar' | 'line' | 'pie';\n\n/**\n * The schema lets `data` be any ReactNode, but in practice callers\n * pass JSON-shaped arrays. We narrow at runtime:\n *\n * - bar: `{ labels: string[], values: number[] }`\n * - line: `{ x: number[], y: number[] }` or `{ series: { label, points: {x,y} }[] }`\n * - pie: `{ labels: string[], values: number[] }`\n *\n * Anything that doesn't fit is rendered as a `<pre>` fallback.\n */\nexport interface ChartProps {\n kind: ChartKind;\n data: unknown;\n title?: string;\n}\n\nexport function Chart({ kind, data, title }: ChartProps): React.JSX.Element {\n return (\n <div data-pgx=\"Chart\" data-chart-kind={kind} style={{ fontFamily: 'inherit' }}>\n {title !== undefined ? (\n <div style={{ fontSize: '14px', fontWeight: 600, color: colors.fg, marginBottom: '6px' }}>\n {title}\n </div>\n ) : null}\n {renderChart(kind, data)}\n </div>\n );\n}\n\nfunction renderChart(kind: ChartKind, data: unknown): React.JSX.Element {\n if (kind === 'bar') return renderBar(data);\n if (kind === 'line') return renderLine(data);\n return renderPie(data);\n}\n\nfunction renderBar(data: unknown): React.JSX.Element {\n const parsed = parseLabeled(data);\n if (!parsed) return fallback('bar chart', data);\n const { labels, values } = parsed;\n const max = Math.max(1, ...values);\n const width = 320;\n const height = 160;\n const barWidth = width / Math.max(1, labels.length);\n return (\n <svg\n width={width}\n height={height}\n role=\"img\"\n aria-label=\"Bar chart\"\n style={{ display: 'block' }}\n >\n {values.map((v, i) => {\n const h = (Math.max(0, v) / max) * (height - 24);\n return (\n <g key={i}>\n <rect\n x={i * barWidth + 2}\n y={height - 12 - h}\n width={Math.max(1, barWidth - 4)}\n height={h}\n fill={colors.bar}\n rx={2}\n />\n <text\n x={i * barWidth + barWidth / 2}\n y={height - 2}\n fontSize={10}\n textAnchor=\"middle\"\n fill={colors.barLabel}\n >\n {(labels[i] ?? '').slice(0, 8)}\n </text>\n </g>\n );\n })}\n </svg>\n );\n}\n\nfunction renderLine(data: unknown): React.JSX.Element {\n // Two accepted shapes; pick whichever parses cleanly.\n let points: Array<{ x: number; y: number }> = [];\n if (data && typeof data === 'object' && 'series' in (data as Record<string, unknown>)) {\n const series = (data as { series: Array<{ points?: Array<{ x: number; y: number }> }> }).series;\n const first = series?.[0];\n points = Array.isArray(first?.points) ? first.points : [];\n } else if (\n data &&\n typeof data === 'object' &&\n Array.isArray((data as { x?: unknown }).x) &&\n Array.isArray((data as { y?: unknown }).y)\n ) {\n const xArr = (data as { x: number[] }).x;\n const yArr = (data as { y: number[] }).y;\n points = xArr.map((x, i) => ({ x, y: yArr[i] ?? 0 }));\n }\n if (points.length === 0) return fallback('line chart', data);\n const xs = points.map((p) => p.x);\n const ys = points.map((p) => p.y);\n const xMin = Math.min(...xs);\n const xMax = Math.max(...xs);\n const yMin = Math.min(...ys);\n const yMax = Math.max(...ys);\n const w = 320;\n const h = 160;\n const sx = (x: number) => (xMax === xMin ? w / 2 : ((x - xMin) / (xMax - xMin)) * (w - 8) + 4);\n const sy = (y: number) =>\n yMax === yMin ? h / 2 : h - ((y - yMin) / (yMax - yMin)) * (h - 24) - 12;\n const path = points\n .map((p, i) => `${i === 0 ? 'M' : 'L'}${sx(p.x).toFixed(2)},${sy(p.y).toFixed(2)}`)\n .join(' ');\n return (\n <svg width={w} height={h} role=\"img\" aria-label=\"Line chart\" style={{ display: 'block' }}>\n <path d={path} fill=\"none\" stroke={colors.line} strokeWidth={2} />\n {points.map((p, i) => (\n <circle key={i} cx={sx(p.x)} cy={sy(p.y)} r={2} fill={colors.line} />\n ))}\n </svg>\n );\n}\n\nfunction renderPie(data: unknown): React.JSX.Element {\n const parsed = parseLabeled(data);\n if (!parsed) return fallback('pie chart', data);\n const total = parsed.values.reduce((a, b) => a + Math.max(0, b), 0) || 1;\n const cx = 80;\n const cy = 80;\n const r = 60;\n let angle = -Math.PI / 2;\n return (\n <svg width={160} height={160} role=\"img\" aria-label=\"Pie chart\" style={{ display: 'block' }}>\n {parsed.values.map((v, i) => {\n const slice = (Math.max(0, v) / total) * Math.PI * 2;\n const x1 = cx + r * Math.cos(angle);\n const y1 = cy + r * Math.sin(angle);\n const x2 = cx + r * Math.cos(angle + slice);\n const y2 = cy + r * Math.sin(angle + slice);\n const large = slice > Math.PI ? 1 : 0;\n const path =\n slice >= Math.PI * 2 - 1e-6\n ? `M ${cx - r} ${cy} A ${r} ${r} 0 1 1 ${cx + r} ${cy} A ${r} ${r} 0 1 1 ${cx - r} ${cy}`\n : `M ${cx} ${cy} L ${x1} ${y1} A ${r} ${r} 0 ${large} 1 ${x2} ${y2} Z`;\n angle += slice;\n const fill = colors.pie[i % colors.pie.length] ?? colors.bar;\n return <path key={i} d={path} fill={fill} />;\n })}\n </svg>\n );\n}\n\nfunction parseLabeled(data: unknown): { labels: string[]; values: number[] } | null {\n if (!data || typeof data !== 'object') return null;\n const labels = (data as { labels?: unknown }).labels;\n const values = (data as { values?: unknown }).values;\n if (!Array.isArray(labels) || !Array.isArray(values)) return null;\n if (labels.length !== values.length) return null;\n if (!labels.every((l) => typeof l === 'string' || typeof l === 'number')) return null;\n if (!values.every((v) => typeof v === 'number' || typeof v === 'string')) return null;\n return {\n labels: labels.map(String),\n values: values.map((v) => (typeof v === 'number' ? v : Number(v))),\n };\n}\n\nfunction fallback(label: string, data: unknown): React.JSX.Element {\n return (\n <pre\n style={{\n margin: 0,\n fontSize: '12px',\n background: colors.codeBg,\n color: colors.codeFg,\n padding: '8px',\n borderRadius: '4px',\n overflow: 'auto',\n }}\n >\n {label} data: {JSON.stringify(data, null, 2)}\n </pre>\n );\n}\n","import * as React from 'react';\n\nexport interface ContainerProps {\n padding?: string;\n gap?: string;\n children?: React.ReactNode;\n}\n\n/**\n * Layout wrapper. Defaults to a vertical flex container with 16px\n * padding and 12px gap when callers omit props.\n */\nexport function Container({ padding, gap, children }: ContainerProps): React.JSX.Element {\n const style: React.CSSProperties = {\n display: 'flex',\n flexDirection: 'column',\n padding: padding ?? '16px',\n gap: gap ?? '12px',\n fontFamily: 'inherit',\n color: 'inherit',\n boxSizing: 'border-box',\n };\n return (\n <div style={style} data-pgx=\"Container\">\n {children}\n </div>\n );\n}\n","import * as React from 'react';\nimport { colors, radius } from './theme.js';\n\nexport interface CodeProps {\n language?: string;\n children?: React.ReactNode;\n}\n\n/**\n * Inline code formatter. Renders children as a `<code>` element inside\n * a styled wrapper. If `children` is a single string, it displays\n * verbatim; for multi-line content we fall back to a `<pre>`-styled\n * block.\n */\nexport function Code({ language, children }: CodeProps): React.JSX.Element {\n const isString = typeof children === 'string';\n const isMulti = isString && (children as string).includes('\\n');\n if (isMulti) {\n return (\n <pre\n data-pgx=\"Code\"\n data-language={language ?? 'text'}\n style={{\n background: colors.codeBg,\n color: colors.codeFg,\n padding: '12px',\n borderRadius: radius.md,\n fontSize: '12px',\n overflow: 'auto',\n margin: 0,\n }}\n >\n <code>{children}</code>\n </pre>\n );\n }\n return (\n <code\n data-pgx=\"Code-inline\"\n data-language={language ?? 'text'}\n style={{\n background: colors.codeBg,\n color: colors.codeFg,\n padding: '2px 6px',\n borderRadius: radius.sm,\n fontSize: '0.9em',\n }}\n >\n {children}\n </code>\n );\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\n\nexport interface HeadingProps {\n /**\n * Heading level (1..6). Out-of-range or undefined falls back to 2.\n * The renderer should never see an `Undefined` value here unless the\n * schema validator said optional props can be omitted.\n */\n level?: number;\n color?: string;\n children?: React.ReactNode;\n}\n\nconst sizes: Record<number, string> = {\n 1: '24px',\n 2: '20px',\n 3: '18px',\n 4: '16px',\n 5: '14px',\n 6: '13px',\n};\n\nexport function Heading({ level, color, children }: HeadingProps): React.JSX.Element {\n const safeLevel = clampLevel(level);\n const size = sizes[safeLevel] ?? sizes[2]!;\n const style: React.CSSProperties = {\n fontFamily: 'inherit',\n fontSize: size,\n fontWeight: 700,\n color: color ?? colors.fg,\n margin: 0,\n lineHeight: 1.3,\n };\n // Render an element corresponding to the level. React enforces\n // TagName = 'h1' | 'h2' ... via JSX, so we switch dynamically via\n // createElement.\n return React.createElement(`h${safeLevel}`, { style, 'data-pgx': `Heading` }, children);\n}\n\nfunction clampLevel(level: number | undefined): 1 | 2 | 3 | 4 | 5 | 6 {\n if (typeof level !== 'number' || !Number.isFinite(level)) return 2;\n const i = Math.floor(level);\n if (i < 1) return 1;\n if (i > 6) return 6;\n return i as 1 | 2 | 3 | 4 | 5 | 6;\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\n\nexport interface TextProps {\n weight?: 'normal' | 'bold';\n size?: string;\n color?: string;\n children?: React.ReactNode;\n}\n\nexport function Text({ weight = 'normal', size, color, children }: TextProps): React.JSX.Element {\n const style: React.CSSProperties = {\n fontFamily: 'inherit',\n fontSize: size ?? '14px',\n fontWeight: weight === 'bold' ? 600 : 400,\n color: color ?? colors.fg,\n margin: 0,\n lineHeight: 1.5,\n };\n return (\n <p style={style} data-pgx=\"Text\">\n {children}\n </p>\n );\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\n\nexport interface Step {\n id: string;\n title: string;\n body?: React.ReactNode;\n}\n\nexport interface StepperProps {\n steps: Step[];\n /** 0-indexed initial step. Out-of-range falls back to 0. */\n initial?: number;\n}\n\n/**\n * Multi-step reveal. State: the active index. Previous steps can be\n * revisited; future steps are disabled until reached.\n */\nexport function Stepper({ steps, initial }: StepperProps): React.JSX.Element {\n const initialIdx = clampIndex(initial ?? 0, steps.length);\n const [active, setActive] = React.useState(initialIdx);\n const wrapStyle: React.CSSProperties = {\n fontFamily: 'inherit',\n display: 'flex',\n flexDirection: 'column',\n gap: '12px',\n };\n const listStyle: React.CSSProperties = {\n display: 'flex',\n gap: '6px',\n listStyle: 'none',\n padding: 0,\n margin: 0,\n };\n const stepBtnBase: React.CSSProperties = {\n width: '28px',\n height: '28px',\n borderRadius: '50%',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n fontFamily: 'inherit',\n fontSize: '12px',\n fontWeight: 600,\n border: '1px solid transparent',\n };\n const bodyStyle: React.CSSProperties = {\n border: `1px solid ${colors.border}`,\n borderRadius: '6px',\n padding: '10px',\n background: colors.bg,\n color: colors.fg,\n };\n return (\n <div data-pgx=\"Stepper\" style={wrapStyle}>\n <ol style={listStyle}>\n {steps.map((s, i) => {\n const isPast = i < active;\n const isCurrent = i === active;\n const future = i > active;\n const stepStyle: React.CSSProperties = {\n ...stepBtnBase,\n background: isCurrent\n ? colors.stepperCurrent\n : isPast\n ? colors.stepperPast\n : colors.stepperFuture,\n color: isCurrent || isPast ? '#fff' : colors.fg,\n opacity: future ? 0.6 : 1,\n cursor: future ? 'not-allowed' : 'pointer',\n };\n return (\n <li key={s.id}>\n <button\n type=\"button\"\n aria-label={`Step ${i + 1}: ${s.title}`}\n aria-current={isCurrent ? 'step' : undefined}\n disabled={future}\n onClick={() => setActive(i)}\n style={stepStyle}\n >\n {i + 1}\n </button>\n </li>\n );\n })}\n </ol>\n <div style={bodyStyle}>\n <div style={{ fontWeight: 600, marginBottom: '4px', fontSize: '14px' }}>\n {steps[active]?.title ?? ''}\n </div>\n <div style={{ fontSize: '13px' }}>{steps[active]?.body ?? null}</div>\n </div>\n </div>\n );\n}\n\nfunction clampIndex(n: number, len: number): number {\n if (len === 0) return 0;\n if (!Number.isFinite(n)) return 0;\n return Math.min(len - 1, Math.max(0, Math.floor(n)));\n}\n","import * as React from 'react';\nimport { colors, radius, space } from './theme.js';\n\nexport interface CardProps {\n title?: string;\n /**\n * Visual elevation. 0 = flat (border only), 1+ = heavier shadow.\n * Defaults to 1.\n */\n elevation?: number;\n children?: React.ReactNode;\n}\n\nexport function Card({ title, elevation = 1, children }: CardProps): React.JSX.Element {\n const shadow =\n elevation >= 3\n ? '0 4px 12px rgba(15, 23, 42, 0.18)'\n : elevation === 2\n ? '0 2px 8px rgba(15, 23, 42, 0.12)'\n : elevation === 1\n ? '0 1px 2px rgba(15, 23, 42, 0.06)'\n : 'none';\n const cardStyle: React.CSSProperties = {\n background: colors.cardBg,\n border: `1px solid ${colors.cardBorder}`,\n borderRadius: radius.md,\n boxShadow: shadow,\n padding: space.md,\n fontFamily: 'inherit',\n color: colors.fg,\n display: 'flex',\n flexDirection: 'column',\n gap: space.sm,\n };\n const titleStyle: React.CSSProperties = {\n fontSize: '14px',\n fontWeight: 600,\n margin: 0,\n };\n return (\n <section data-pgx=\"Card\" style={cardStyle}>\n {title !== undefined ? <h3 style={titleStyle}>{title}</h3> : null}\n {children}\n </section>\n );\n}\n","import * as React from 'react';\nimport { colors } from './theme.js';\n\nexport interface ListProps {\n /**\n * Items to render. Schema marks this as `node`; in practice callers\n * pass arrays of strings/numbers/ReactNodes. Anything else is\n * rendered as a single bullet containing the value.\n */\n items?: unknown;\n /** When true, renders as `<ol>` instead of `<ul>`. */\n ordered?: boolean;\n /** Override the wrapper element for full control. */\n children?: React.ReactNode;\n}\n\n/**\n * List of items. Renders `items` as a list of strings/numbers (the\n * common artifact prompt output). If `children` are passed they take\n * precedence over `items`.\n */\nexport function List({ items, ordered, children }: ListProps): React.JSX.Element {\n const style: React.CSSProperties = {\n margin: 0,\n paddingLeft: '20px',\n fontFamily: 'inherit',\n color: colors.fg,\n fontSize: '14px',\n lineHeight: 1.6,\n };\n if (children !== undefined && children !== null) {\n return React.createElement(ordered ? 'ol' : 'ul', { style, 'data-pgx': 'List' }, children);\n }\n const arr = Array.isArray(items) ? items : items === undefined ? [] : [items];\n return React.createElement(\n ordered ? 'ol' : 'ul',\n { style, 'data-pgx': 'List' },\n arr.map((item, i) => {\n const node: React.ReactNode =\n typeof item === 'string' || typeof item === 'number' ? String(item) : JSON.stringify(item);\n return <li key={i}>{node}</li>;\n }),\n );\n}\n","/**\n * Helper functions for working with {@link PlaygroundState}.\n *\n * These are pure operations over a `PlaygroundState` — no React, no\n * hooks, no I/O. They live alongside the store so that:\n * - Devtools / CLI scripts can operate on a state without React\n * (the bin/state-cli.ts script uses these directly).\n * - Test code can drive a store through a thin façade.\n * - Server-side renderers can dump, validate, and diff snapshots\n * without needing a DOM.\n *\n * @packageDocumentation\n */\n\nimport type { PlaygroundState } from './state.jsx';\n\n/**\n * Structured diff between two state snapshots. Pure function — does\n * not touch the stores. Returns `null` when the snapshots are\n * shallowly equal (no additions, no changes, no removals).\n *\n * @example\n * const a = { x: 1, y: 2 };\n * const b = { x: 1, y: 3, z: 4 };\n * diffSnapshots(a, b); // { added: ['z'], changed: ['y'], removed: [] }\n */\nexport interface StateDiff {\n /** Keys present in `next` but absent from `prev`. */\n added: string[];\n /** Keys whose value changed (using `Object.is` to compare). */\n changed: string[];\n /** Keys present in `prev` but absent from `next`. */\n removed: string[];\n}\n\nexport function diffSnapshots(\n prev: Readonly<Record<string, unknown>>,\n next: Readonly<Record<string, unknown>>,\n): StateDiff | null {\n const added: string[] = [];\n const changed: string[] = [];\n const removed: string[] = [];\n const prevKeys = Object.keys(prev);\n const nextKeys = Object.keys(next);\n const prevSet = new Set(prevKeys);\n const nextSet = new Set(nextKeys);\n for (const k of nextKeys) {\n if (!prevSet.has(k)) {\n added.push(k);\n continue;\n }\n if (!Object.is(prev[k], next[k])) {\n changed.push(k);\n }\n }\n for (const k of prevKeys) {\n if (!nextSet.has(k)) removed.push(k);\n }\n added.sort();\n changed.sort();\n removed.sort();\n if (added.length === 0 && changed.length === 0 && removed.length === 0) {\n return null;\n }\n return { added, changed, removed };\n}\n\n/**\n * Convenience: take snapshots of both stores and diff them. Returns\n * `null` when equal. Useful in tests and debug endpoints.\n */\nexport function diffState(\n _a: PlaygroundState,\n _b: PlaygroundState,\n prev?: Readonly<Record<string, unknown>>,\n next?: Readonly<Record<string, unknown>>,\n): StateDiff | null {\n const aSnap = prev ?? _a.snapshot();\n const bSnap = next ?? _b.snapshot();\n return diffSnapshots(aSnap, bSnap);\n}\n\n/**\n * Validate a state-key string. Returns `null` on success, or a\n * human-readable error message on failure. The rules:\n *\n * - 1..64 characters\n * - No whitespace\n * - Only alphanumerics, dots, dashes, underscores\n * - Must start with an alphabetic character (so a parser can\n * safely infer `<Slider stateKey=\"...\">` from a body without\n * swallowing accidental numeric keys).\n *\n * Note: keys like `with/slash` or `with spaces` are rejected because\n * the schema-driven body parser uses them as attribute names —\n * a key with weird characters would break the round-trip.\n */\nexport function validateStateKey(key: string): string | null {\n if (key.length === 0) return 'stateKey must be a non-empty string';\n if (key.length > 64) return 'stateKey must be at most 64 characters';\n if (/\\s/.test(key)) return 'stateKey must not contain whitespace';\n if (!/^[A-Za-z][A-Za-z0-9._-]*$/.test(key)) {\n return (\n 'stateKey contains forbidden characters (allowed: A-Z, a-z, ' +\n '0-9, dot, dash, underscore; must start with a letter)'\n );\n }\n return null;\n}\n\n/**\n * Remove every key from the store, firing subscribers with `undefined`.\n * After this call, `state.snapshot()` is `{}`.\n */\nexport function clearState(state: PlaygroundState): void {\n // Snapshot first to avoid mutating the iteration source.\n for (const k of Object.keys(state.snapshot())) {\n state.set(k, undefined);\n }\n}\n\n/**\n * JSON-envelope snapshot for offline inspection. Useful for:\n * - piping into `jq` / debug endpoints\n * - attaching to error reports\n * - round-tripping through `JSON.parse(state.snapshot)`.\n *\n * The `version` field is bumped if the envelope shape ever changes,\n * so consumers can branch on it.\n */\nexport interface StateEnvelope {\n /** Always 1 for now. */\n version: 1;\n /** Sorted list of keys (stable for diffing). */\n keys: string[];\n /** Shallow key→value map. */\n values: Record<string, unknown>;\n}\n\nexport function dumpState(state: PlaygroundState): string {\n const snap = state.snapshot();\n const envelope: StateEnvelope = {\n version: 1,\n keys: Object.keys(snap).sort(),\n values: { ...snap },\n };\n return JSON.stringify(envelope, null, 2);\n}\n\n/**\n * Apply multiple `set` calls as a single atomic batch. Each\n * subscriber sees only the LAST value written to its key in this\n * batch. The store still batches subscriber fires via the microtask\n * scheduler — `batch` does not flush synchronously, it only ensures\n * that within the batch, repeated `set` calls to the same key don't\n * queue redundant fires.\n *\n * Useful for: undo/redo, multi-field form commits, hydrated state\n * restore where you want each subscriber to fire exactly once with\n * the final value.\n *\n * @example\n * batch(state, (s) => {\n * s.set('x', 1);\n * s.set('y', 2);\n * s.set('x', 3); // overwrites; subscriber sees only 3\n * });\n */\nexport function batch<T>(\n state: PlaygroundState,\n fn: (state: PlaygroundState) => T,\n): T {\n // `_batch` is the underlying transactional primitive on the store.\n // We type-cast because the public PlaygroundState interface doesn't\n // expose this test seam — only the helper does.\n const internal = state as unknown as {\n _batch: (fn: () => T) => T;\n };\n return internal._batch(() => fn(state));\n}","/**\n * Render-prop component that shows a toggle-able raw-source panel.\n * Consumer controls the trigger UI via a children render prop.\n *\n * Design notes:\n * - Render-prop pattern (not props.children-as-trigger) because the\n * trigger UI varies wildly across consumers (button, link, icon,\n * inline text). The consumer gets `(toggle, showing)` and decides\n * what to render.\n * - The source panel is a `<pre>` with `<code>` inside. Plain-text;\n * no syntax highlighting (avoiding a 50KB highlight.js dep).\n * Consumers who need highlighting can compose with their own\n * syntax-highlighter.\n * - The panel is rendered INLINE (not in a portal/tooltip) so the\n * toggle's visual feedback is immediate.\n *\n * @packageDocumentation\n */\n\nimport * as React from 'react';\n\nexport interface ShowSourceProps {\n /** The raw body to display when the user expands the source. */\n body: string;\n /** Source language hint. Used in `data-language` for styling hooks. */\n language?: 'tsx' | 'json';\n /**\n * Children is a render prop that receives `(toggle, showing)`.\n * The consumer is expected to render a trigger element (button,\n * link, etc.) and call `toggle` on user interaction.\n */\n children: (toggle: () => void, showing: boolean) => React.ReactNode;\n}\n\n/**\n * Toggle button + expandable source panel. The trigger UI is the\n * consumer's choice; the panel is plain `<pre><code>`.\n */\nexport function ShowSource(props: ShowSourceProps): React.JSX.Element {\n const { body, language, children } = props;\n const [showing, setShowing] = React.useState(false);\n // Wrap setShowing to keep the toggle reference stable. Consumers\n // can pass `toggle` to a `useEffect` dep array without retriggering.\n const toggle = React.useCallback(() => {\n setShowing((s) => !s);\n }, []);\n\n return (\n <>\n {children(toggle, showing)}\n {showing ? (\n <pre\n data-pgx=\"ShowSource\"\n data-language={language ?? 'text'}\n style={{\n background: '#0f172a',\n color: '#e2e8f0',\n padding: '12px',\n borderRadius: '6px',\n overflowX: 'auto',\n fontFamily: 'ui-monospace, Menlo, monospace',\n fontSize: '12px',\n lineHeight: 1.5,\n margin: '8px 0 0',\n maxHeight: '320px',\n whiteSpace: 'pre',\n }}\n >\n <code>{body}</code>\n </pre>\n ) : null}\n </>\n );\n}\n","/**\n * Error boundary for rendered artifacts. Catches render-time errors\n * (malformed body, throw from a component, etc.) and shows a recoverable\n * fallback. Without this, a single bad artifact blanks the student's\n * whole page.\n *\n * Design notes:\n * - Uses React's class-component error boundary API (the only\n * first-class option in React 18/19).\n * - Default fallback shows the error message + an optional \"Show source\"\n * toggle that reveals the raw body. Consumer can override with\n * `fallback` for custom UX.\n * - `onError` fires before the fallback renders so consumers can log\n * to Sentry / Datadog / etc.\n *\n * @packageDocumentation\n */\n\nimport * as React from 'react';\n\nimport { ShowSource } from './ShowSource.js';\n\nexport interface ArtifactErrorBoundaryProps {\n children: React.ReactNode;\n /**\n * Called when a render error is caught. The `info.componentStack`\n * string is the React component stack at the point of error. Log\n * to your observability backend here.\n */\n onError?: (error: Error, info: { componentStack: string }) => void;\n /**\n * Optional body string. If present and the consumer renders the\n * default fallback, a \"Show source\" toggle will be available that\n * reveals this body verbatim. Ignored if a custom `fallback` is\n * supplied (the consumer is responsible for surfacing source).\n */\n body?: string;\n /**\n * Source language hint for the ShowSource toggle in the default\n * fallback. Defaults to 'tsx' (most artifacts are TSX).\n */\n language?: 'tsx' | 'json';\n /**\n * Optional custom fallback. Pass a React element for a static fallback\n * or a function that receives the error for a dynamic one. If\n * omitted, the default fallback renders a panel with the error\n * message and (if `body` is supplied) a ShowSource toggle.\n */\n fallback?: React.ReactNode | ((error: Error) => React.ReactNode);\n /**\n * Label shown in the default fallback header. Defaults to the\n * artifact kind identifier (e.g. \"playground artifact failed to\n * render\"). The consumer is expected to pass the kind.\n */\n kind?: string;\n}\n\ninterface State {\n error: Error | null;\n}\n\n/**\n * Default fallback panel. Renders the error message and, if `body` is\n * provided, a ShowSource toggle. Designed to be a calm, recoverable\n * surface — not a stack-trace dump that panics users.\n */\nfunction DefaultFallback({\n error,\n body,\n language,\n kind,\n}: {\n error: Error;\n body?: string;\n language?: 'tsx' | 'json';\n kind?: string;\n}): React.JSX.Element {\n const label = kind ? `${kind} artifact failed to render` : 'Artifact failed to render';\n return (\n <div\n role=\"alert\"\n data-pgx=\"ArtifactErrorBoundary\"\n data-pgx-kind={kind}\n style={{\n border: '1px solid #fca5a5',\n background: '#fef2f2',\n color: '#7f1d1d',\n borderRadius: '8px',\n padding: '12px 16px',\n fontFamily: 'ui-sans-serif, system-ui, sans-serif',\n fontSize: '14px',\n maxWidth: '720px',\n margin: '12px 0',\n }}\n >\n <div style={{ fontWeight: 600, marginBottom: '6px' }}>{label}</div>\n <code\n data-pgx=\"error-message\"\n style={{\n display: 'block',\n fontFamily: 'ui-monospace, Menlo, monospace',\n fontSize: '12px',\n background: 'rgba(0,0,0,0.04)',\n padding: '6px 8px',\n borderRadius: '4px',\n whiteSpace: 'pre-wrap',\n wordBreak: 'break-word',\n }}\n >\n {error.message}\n </code>\n {body !== undefined ? (\n <div style={{ marginTop: '8px' }}>\n <ShowSource body={body} language={language}>\n {(toggle, showing) => (\n <button\n type=\"button\"\n onClick={toggle}\n data-pgx=\"show-source-toggle\"\n style={{\n background: 'transparent',\n border: '1px solid #fca5a5',\n color: '#7f1d1d',\n padding: '4px 10px',\n fontSize: '12px',\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n >\n {showing ? 'Hide source' : 'Show source'}\n </button>\n )}\n </ShowSource>\n </div>\n ) : null}\n </div>\n );\n}\n\n/**\n * Error boundary. Catches render-time errors in `children` and shows\n * the fallback. The error state is reset on prop change of `children`\n * (or explicit `resetKey` bump) so the consumer can recover by\n * re-rendering.\n */\nexport class ArtifactErrorBoundary extends React.Component<\n ArtifactErrorBoundaryProps,\n State\n> {\n state: State = { error: null };\n\n static getDerivedStateFromError(error: Error): State {\n return { error };\n }\n\n override componentDidCatch(error: Error, info: React.ErrorInfo): void {\n if (this.props.onError) {\n try {\n this.props.onError(error, { componentStack: info.componentStack ?? '' });\n } catch {\n // Swallow — onError callback errors must not break rendering.\n }\n }\n }\n\n override render(): React.ReactNode {\n const { error } = this.state;\n if (error === null) return this.props.children;\n\n const { fallback, body, language, kind } = this.props;\n if (typeof fallback === 'function') return fallback(error);\n if (fallback !== undefined) return fallback;\n return (\n <DefaultFallback\n error={error}\n body={body}\n language={language}\n kind={kind}\n />\n );\n }\n}\n","/**\n * `createRegistry(overrides)` — build a component map with the default\n * 11 components plus caller overrides. Returns a frozen map so post-hoc\n * mutation can't surprise concurrent renders.\n *\n * Lowercase HTML tags (div, span, etc.) are NOT in this map; they're\n * intrinsic to React and handled by `renderBody` directly.\n *\n * @packageDocumentation\n */\n\nimport type { ComponentType } from 'react';\n\nimport { Button } from './Button.js';\nimport { Card } from './Card.js';\nimport { Chart } from './Chart.js';\nimport { Code } from './Code.js';\nimport { Container } from './Container.js';\nimport { Heading } from './Heading.js';\nimport { List } from './List.js';\nimport { Slider } from './Slider.js';\nimport { Stepper } from './Stepper.js';\nimport { Text } from './Text.js';\nimport { TextField } from './TextField.js';\n\n/**\n * Map of PascalCase component name to React component. Loose typing\n * (`ComponentType<any>`) because each component's prop shape differs and\n * the runtime doesn't know which one it's looking up. Consumers who\n * need stricter types can build their own typed wrapper around\n * `renderBody`.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type ComponentMap = Record<string, ComponentType<any>>;\n\nconst DEFAULT_REGISTRY: ComponentMap = Object.freeze({\n Button,\n Card,\n Chart,\n Code,\n Container,\n Heading,\n List,\n Slider,\n Stepper,\n Text,\n TextField,\n});\n\n/**\n * The full default registry (Button, Card, Chart, Code, Container,\n * Heading, List, Slider, Stepper, Text, TextField). Frozen. Use\n * `createRegistry(overrides)` to extend.\n */\nexport const componentMap: ComponentMap = DEFAULT_REGISTRY;\n\n/**\n * Build a new registry by merging the defaults with caller-supplied\n * overrides. The returned map is frozen (Object.freeze) to prevent\n * post-hoc mutation that would surprise concurrent renders.\n *\n * **Lowercase HTML tags are NOT in this map.** The renderer handles\n * `div`, `span`, etc. as React intrinsic elements separately. Calling\n * `createRegistry({ div: StubDiv })` will NOT override the renderer\n * for `<div>` (the renderer uses the lowercase lookup path first).\n *\n * @example\n * ```ts\n * const map = createRegistry({\n * MathExpression: MathExpressionComponent,\n * LatexBlock: LatexBlockComponent,\n * });\n * ```\n */\nexport function createRegistry(overrides: Partial<ComponentMap>): ComponentMap {\n // Merge: defaults are the base; overrides win. Lowercase keys are\n // filtered out because they refer to React intrinsic HTML tags\n // (div, span, etc.) that renderBody handles intrinsically — the\n // caller cannot override intrinsic rendering via the registry.\n // Object.freeze on the result prevents consumers from accidentally\n // mutating the global componentMap by mutating their copy.\n const merged: ComponentMap = { ...DEFAULT_REGISTRY };\n for (const key of Object.keys(overrides) as (keyof ComponentMap)[]) {\n const value = overrides[key];\n if (value === undefined) continue;\n if (key[0] !== key[0]!.toUpperCase()) continue; // skip lowercase\n merged[key] = value;\n }\n return Object.freeze(merged);\n}\n"],"mappings":";;;;;;;;;;;;AAUA,MAAa,SAAS;CACpB,IAAI;CACJ,IAAI;CACJ,SAAS;CACT,QAAQ;CACR,SAAS;CACT,WAAW;CACX,aAAa;CACb,SAAS;CACT,QAAQ;CACR,YAAY;CACZ,QAAQ;CACR,QAAQ;CACR,aAAa;CACb,YAAY;CACZ,aAAa;CACb,KAAK;CACL,UAAU;CACV,KAAK;EAAC;EAAW;EAAW;EAAW;CAAS;CAChD,MAAM;CACN,aAAa;CACb,gBAAgB;CAChB,eAAe;AACjB;AAEA,MAAa,QAAQ;CACnB,MAAM;CACN,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;AAEA,MAAa,SAAS;CACpB,IAAI;CACJ,IAAI;CACJ,IAAI;AACN;;;ACzBA,SAAgB,OAAO,EACrB,OACA,UAAU,WACV,WAAW,OACX,WACiC;CAoBjC,OACE,oBAAC,UAAD;EAAQ,MAAK;EAAmB;EAAmB;EAAS,OAAO;GAnBnE,YAAY;GACZ,UAAU;GACV,YAAY;GACZ,SAAS;GACT,cAAc,OAAO;GACrB,QAAQ;GACR,QAAQ,WAAW,gBAAgB;GACnC,SAAS,WAAW,KAAM;GAC1B,YACE,YAAY,YACR,OAAO,UACP,YAAY,cACV,OAAO,cACP;GACR,OACE,YAAY,UAAU,OAAO,UAAU,YAAY,cAAc,OAAO,KAAK,OAAO;GACtF,aAAa,YAAY,UAAU,OAAO,SAAS;EAGqB;EAAG,YAAS;YACjF;CACK,CAAA;AAEZ;;;;;;;;AChCA,SAAgB,UAAU,EACxB,OACA,OACA,aACA,WAAW,OACX,YACoC;CACpC,MAAM,eAAoC;EACxC,SAAS;EACT,eAAe;EACf,KAAK;EACL,YAAY;CACd;CACA,MAAM,aAAkC;EACtC,UAAU;EACV,YAAY;EACZ,OAAO,OAAO;CAChB;CACA,MAAM,aAAkC;EACtC,YAAY;EACZ,UAAU;EACV,SAAS;EACT,QAAQ,aAAa,OAAO;EAC5B,cAAc,OAAO;EACrB,YAAY,OAAO;EACnB,OAAO,OAAO;EACd,SAAS;CACX;CACA,MAAM,aAA0D;EAC9D;EACA;EACA;CACF;CAGA,IAAI,UAAU,KAAA,GAAW,WAAW,QAAQ;MACvC,WAAW,eAAe;CAC/B,OACE,qBAAC,SAAD;EAAO,OAAO;EAAc,YAAS;YAArC,CACG,UAAU,KAAA,IAAY,oBAAC,QAAD;GAAM,OAAO;aAAa;EAAY,CAAA,IAAI,MACjE,oBAAC,SAAD;GAAO,GAAI;GAAY,OAAO;EAAa,CAAA,CACtC;;AAEX;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACmBA,SAAgB,sBACd,UAA6B,CAAC,GACb;CACjB,MAAM,SAAS,IAAI,IAAqB,OAAO,QAAQ,OAAO,CAAC;CAG/D,MAAM,uBAAO,IAAI,IAA2C;CAK5D,IAAI,UAAkD,CAAC;CACvD,IAAI,YAAY;CAMhB,IAAI,aAAa;CACjB,IAAI,iBAAyD,CAAC;CAE9D,MAAM,cAAoB;EACxB,IAAI,QAAQ,WAAW,GAAG;EAC1B,MAAM,QAAQ;EACd,UAAU,CAAC;EACX,YAAY;EAMZ,KAAK,MAAM,EAAE,KAAK,WAAW,OAAO;GAClC,MAAM,OAAO,KAAK,IAAI,GAAG;GACzB,IAAI,MAGF,KAAK,MAAM,MAAM,CAAC,GAAG,IAAI,GACvB,IAAI;IACF,GAAG,KAAK;GACV,SAAS,KAAK;IAMZ,IAAI,QAAQ,IAAI,aAAa,cAC3B,QAAQ,MACN,2CACA,KACA,GACF;GAEJ;EAGN;CACF;CAEA,MAAM,iBAAuB;EAC3B,IAAI,WAAW;EACf,YAAY;EAGZ,IAAI,OAAO,mBAAmB,YAC5B,eAAe,KAAK;OAEpB,QAAQ,QAAQ,CAAC,CAAC,KAAK,KAAK;CAEhC;CAEA,MAAM,QAAuB;EAC3B,IAAiB,KAA4B;GAC3C,OAAO,OAAO,IAAI,GAAG;EACvB;EACA,IAAiB,KAAa,OAAgB;GAC5C,IAAI,OAAO,GAAG,OAAO,IAAI,GAAG,GAAG,KAAK,GAAG;GACvC,OAAO,IAAI,KAAK,KAAK;GAGrB,IAAI,aAAa,GACf,eAAe,KAAK;IAAE;IAAK;GAAM,CAAC;QAElC,QAAQ,KAAK;IAAE;IAAK;GAAM,CAAC;GAE7B,SAAS;EACX;EACA,UAAU,KAAa,IAA0C;GAC/D,IAAI,OAAO,KAAK,IAAI,GAAG;GACvB,IAAI,CAAC,MAAM;IACT,uBAAO,IAAI,IAAI;IACf,KAAK,IAAI,KAAK,IAAI;GACpB;GACA,KAAK,IAAI,EAAE;GACX,aAAa;IACX,MAAM,IAAI,KAAK,IAAI,GAAG;IACtB,IAAI,GAAG;KACL,EAAE,OAAO,EAAE;KACX,IAAI,EAAE,SAAS,GAAG,KAAK,OAAO,GAAG;IACnC;GACF;EACF;EACA,WAA8C;GAC5C,OAAO,OAAO,YAAY,MAAM;EAClC;EACA,WAAW,QAAuC;GAChD,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,MAAM,GACxC,MAAM,IAAI,GAAG,CAAC;EAElB;EACA,SAAe;GACb,MAAM;EACR;EACA,OAAU,IAAgB;GACxB;GACA,IAAI;IACF,OAAO,GAAG;GACZ,UAAU;IACR;IAKA,IAAI,eAAe,KAAK,eAAe,SAAS,GAAG;KACjD,MAAM,6BAAa,IAAI,IAAqB;KAC5C,KAAK,IAAI,IAAI,eAAe,SAAS,GAAG,KAAK,GAAG,KAAK;MACnD,MAAM,EAAE,KAAK,UAAU,eAAe;MACtC,IAAI,CAAC,WAAW,IAAI,GAAG,GAAG;OACxB,WAAW,IAAI,KAAK,KAAK;OACzB,QAAQ,KAAK;QAAE;QAAK;OAAM,CAAC;MAC7B;KACF;KACA,iBAAiB,CAAC;IACpB;GACF;EACF;EACA,uBAA6B;GAK3B,KAAK,MAAM;GAKX,UAAU,CAAC;GACX,iBAAiB,CAAC;GAClB,YAAY;EACd;CACF;CACA,OAAO;AACT;;;;;;AAWA,MAAM,yBAAyB,MAAM,cACnC,IACF;;;;;;;;AAqBA,SAAgB,wBACd,OACmB;CAInB,MAAM,CAAC,SAAS,MAAM,eAAe;EAEnC,OADU,sBAAsB;GAAE,GAAG,MAAM;GAAM,GAAG,MAAM;EAAQ,CAC3D;CACT,CAAC;CAWD,MAAM,gBAAgB;EACpB,aAAa;GAKX,MACG,uBAAuB;EAC5B;CACF,GAAG,CAAC,KAAK,CAAC;CAEV,MAAM,QAAQ,MAAM,eAAe,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC;CAEtD,OAAO,MAAM,cACX,uBAAuB,UACvB,EAAE,MAAM,GACR,MAAM,QACR;AACF;;;;;;;;;;;;;AAcA,SAAgB,qBAAsC;CACpD,MAAM,MAAM,MAAM,WAAW,sBAAsB;CACnD,IAAI,QAAQ,MACV,MAAM,IAAI,MACR,4LAGF;CAEF,OAAO,IAAI;AACb;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,yBACd,KACA,UAC6C;CAI7C,MAAM,MAAM,MAAM,WAAW,sBAAsB;CACnD,MAAM,QAAQ,QAAQ,KAAA,KAAa,QAAQ;CAK3C,MAAM,CAAC,OAAO,YAAY,MAAM,SAAY,QAAQ;CACpD,MAAM,GAAG,SAAS,MAAM,SAAS,CAAC;CAClC,MAAM,gBAAgB;EACpB,IAAI,CAAC,SAAS,QAAQ,KAAA,KAAa,QAAQ,MAAM;EACjD,MAAM,UAAW,IAAI,MAAM,IAAO,GAAG,KAAK;EAC1C,SAAS,OAAO;EAKhB,OAJc,IAAI,MAAM,UAAU,MAAM,SAAS;GAC/C,SAAS,IAAS;GAClB,OAAO,MAAM,IAAI,CAAC;EACpB,CACW;CAKb,GAAG;EAAC;EAAO;EAAK;CAAG,CAAC;CACpB,MAAM,MAAM,MAAM,aACf,SAAY;EACX,IAAI,CAAC,SAAS,QAAQ,KAAA,KAAa,QAAQ,MAAM;EACjD,IAAI,MAAM,IAAO,KAAK,IAAI;CAC5B,GACA;EAAC;EAAO;EAAK;CAAG,CAClB;CAKA,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,OAAO,CAAC,OAAO,GAAG;AACpB;AAEA,SAAgB,cACd,KACA,UACiC;CACjC,MAAM,QAAQ,yBAA4B,KAAK,QAAQ;CACvD,IAAI,UAAU,KAAA,GACZ,MAAM,IAAI,MACR,kKAGF;CAEF,OAAO;AACT;;;;;;AAgBA,SAAgB,eACd,QAC0B;CAC1B,MAAM,QAAQ,mBAAmB;CAQjC,OAAO,MAAM,aACV,UAAmB;EAElB,IAAI,WAAW,KAAA,GAAW;EAC1B,IAAI,OAAO,KACT,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,OAAO,GAAG,GAC5C,MAAM,IAAI,GAAG,CAAC;EAGlB,IAAI,OAAO,WAAW,KAAA,GAAW;GAC/B,MAAM,MAAM,OAAO;GACnB,MAAM,UAAU,MAAM,IAAa,GAAG;GACtC,MAAM,IAAI,KAAK,CAAC,OAAO;EACzB;CACF,GACA,CAAC,QAAQ,KAAK,CAChB;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;AC3ZA,SAAgB,OAAO,EAAE,KAAK,KAAK,OAAO,OAAO,GAAG,OAAO,YAA4C;CAKrG,MAAM,CAAC,UAAU,eAAe,MAAM,eAAe,MAAM,SAAS,KAAK,KAAK,GAAG,CAAC;CAClF,MAAM,QAAQ,yBAAiC,UAAU,QAAQ;CACjE,MAAM,aAAa,UAAU,KAAA;CAE7B,MAAM,UAAU,aAAa,KAAA,KAAa,QACtC,MAAM,MAAM,IAAI,KAAK,GAAG,IACxB,aACE,MAAM,OAAO,KAAK,GAAG,IACrB;CACN,MAAM,aAAkC;EACtC,SAAS;EACT,eAAe;EACf,KAAK;EACL,YAAY;EACZ,OAAO;CACT;CACA,MAAM,aAAkC;EACtC,UAAU;EACV,OAAO,OAAO;CAChB;CACA,MAAM,YAAiC;EAAE,SAAS;EAAQ,YAAY;EAAU,KAAK;CAAO;CAC5F,MAAM,aAAkC;EAAE,MAAM;EAAG,aAAa,OAAO;CAAW;CAClF,MAAM,aAAkC;EACtC,UAAU;EACV,WAAW;EACX,oBAAoB;EACpB,UAAU;EACV,OAAO,OAAO;CAChB;CACA,OACE,qBAAC,OAAD;EAAK,OAAO;EAAY,YAAS;YAAjC,CACG,UAAU,KAAA,IAAY,oBAAC,QAAD;GAAM,OAAO;aAAa;EAAY,CAAA,IAAI,MACjE,qBAAC,OAAD;GAAK,OAAO;aAAZ,CACE,oBAAC,SAAD;IACE,MAAK;IACA;IACA;IACC;IACN,OAAO;IACP,UAAU,QAAQ;IAClB,cAAY;IACZ,WAAW,MAAM;KACf,MAAM,OAAO,OAAO,EAAE,cAAc,KAAK;KACzC,IAAI,aAAa,KAAA,KAAa,OAC5B,MAAM,EAAE,CAAC,IAAI;UACR,IAAI,CAAC,YACV,YAAY,IAAI;IAEpB;IACA,OAAO;GACR,CAAA,GACD,oBAAC,QAAD;IAAM,OAAO;cAAa;GAAc,CAAA,CACrC;IACF;;AAET;AAEA,SAAS,MAAM,GAAW,IAAY,IAAoB;CACxD,IAAI,OAAO,MAAM,CAAC,GAAG,OAAO;CAC5B,OAAO,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC;AACrC;;;AC1FA,SAAgB,MAAM,EAAE,MAAM,MAAM,SAAwC;CAC1E,OACE,qBAAC,OAAD;EAAK,YAAS;EAAQ,mBAAiB;EAAM,OAAO,EAAE,YAAY,UAAU;YAA5E,CACG,UAAU,KAAA,IACT,oBAAC,OAAD;GAAK,OAAO;IAAE,UAAU;IAAQ,YAAY;IAAK,OAAO,OAAO;IAAI,cAAc;GAAM;aACpF;EACE,CAAA,IACH,MACH,YAAY,MAAM,IAAI,CACpB;;AAET;AAEA,SAAS,YAAY,MAAiB,MAAkC;CACtE,IAAI,SAAS,OAAO,OAAO,UAAU,IAAI;CACzC,IAAI,SAAS,QAAQ,OAAO,WAAW,IAAI;CAC3C,OAAO,UAAU,IAAI;AACvB;AAEA,SAAS,UAAU,MAAkC;CACnD,MAAM,SAAS,aAAa,IAAI;CAChC,IAAI,CAAC,QAAQ,OAAO,SAAS,aAAa,IAAI;CAC9C,MAAM,EAAE,QAAQ,WAAW;CAC3B,MAAM,MAAM,KAAK,IAAI,GAAG,GAAG,MAAM;CACjC,MAAM,QAAQ;CACd,MAAM,SAAS;CACf,MAAM,WAAW,QAAQ,KAAK,IAAI,GAAG,OAAO,MAAM;CAClD,OACE,oBAAC,OAAD;EACS;EACC;EACR,MAAK;EACL,cAAW;EACX,OAAO,EAAE,SAAS,QAAQ;YAEzB,OAAO,KAAK,GAAG,MAAM;GACpB,MAAM,IAAK,KAAK,IAAI,GAAG,CAAC,IAAI,OAAQ,SAAS;GAC7C,OACE,qBAAC,KAAD,EAAA,UAAA,CACE,oBAAC,QAAD;IACE,GAAG,IAAI,WAAW;IAClB,GAAG,SAAS,KAAK;IACjB,OAAO,KAAK,IAAI,GAAG,WAAW,CAAC;IAC/B,QAAQ;IACR,MAAM,OAAO;IACb,IAAI;GACL,CAAA,GACD,oBAAC,QAAD;IACE,GAAG,IAAI,WAAW,WAAW;IAC7B,GAAG,SAAS;IACZ,UAAU;IACV,YAAW;IACX,MAAM,OAAO;eAEX,OAAO,MAAM,GAAA,CAAI,MAAM,GAAG,CAAC;GACzB,CAAA,CACL,EAAA,GAlBK,CAkBL;EAEP,CAAC;CACE,CAAA;AAET;AAEA,SAAS,WAAW,MAAkC;CAEpD,IAAI,SAA0C,CAAC;CAC/C,IAAI,QAAQ,OAAO,SAAS,YAAY,YAAa,MAAkC;EAErF,MAAM,QADU,KAAyE,SAClE;EACvB,SAAS,MAAM,QAAQ,OAAO,MAAM,IAAI,MAAM,SAAS,CAAC;CAC1D,OAAO,IACL,QACA,OAAO,SAAS,YAChB,MAAM,QAAS,KAAyB,CAAC,KACzC,MAAM,QAAS,KAAyB,CAAC,GACzC;EACA,MAAM,OAAQ,KAAyB;EACvC,MAAM,OAAQ,KAAyB;EACvC,SAAS,KAAK,KAAK,GAAG,OAAO;GAAE;GAAG,GAAG,KAAK,MAAM;EAAE,EAAE;CACtD;CACA,IAAI,OAAO,WAAW,GAAG,OAAO,SAAS,cAAc,IAAI;CAC3D,MAAM,KAAK,OAAO,KAAK,MAAM,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,KAAK,MAAM,EAAE,CAAC;CAChC,MAAM,OAAO,KAAK,IAAI,GAAG,EAAE;CAC3B,MAAM,OAAO,KAAK,IAAI,GAAG,EAAE;CAC3B,MAAM,OAAO,KAAK,IAAI,GAAG,EAAE;CAC3B,MAAM,OAAO,KAAK,IAAI,GAAG,EAAE;CAC3B,MAAM,IAAI;CACV,MAAM,IAAI;CACV,MAAM,MAAM,MAAe,SAAS,OAAO,IAAI,KAAM,IAAI,SAAS,OAAO,SAAU,IAAI,KAAK;CAC5F,MAAM,MAAM,MACV,SAAS,OAAO,IAAI,IAAI,KAAM,IAAI,SAAS,OAAO,SAAU,IAAI,MAAM;CAIxE,OACE,qBAAC,OAAD;EAAK,OAAO;EAAG,QAAQ;EAAG,MAAK;EAAM,cAAW;EAAa,OAAO,EAAE,SAAS,QAAQ;YAAvF,CACE,oBAAC,QAAD;GAAM,GALG,OACV,KAAK,GAAG,MAAM,GAAG,MAAM,IAAI,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAClF,KAAK,GAGQ;GAAG,MAAK;GAAO,QAAQ,OAAO;GAAM,aAAa;EAAI,CAAA,GAChE,OAAO,KAAK,GAAG,MACd,oBAAC,UAAD;GAAgB,IAAI,GAAG,EAAE,CAAC;GAAG,IAAI,GAAG,EAAE,CAAC;GAAG,GAAG;GAAG,MAAM,OAAO;EAAO,GAAvD,CAAuD,CACrE,CACE;;AAET;AAEA,SAAS,UAAU,MAAkC;CACnD,MAAM,SAAS,aAAa,IAAI;CAChC,IAAI,CAAC,QAAQ,OAAO,SAAS,aAAa,IAAI;CAC9C,MAAM,QAAQ,OAAO,OAAO,QAAQ,GAAG,MAAM,IAAI,KAAK,IAAI,GAAG,CAAC,GAAG,CAAC,KAAK;CACvE,MAAM,KAAK;CACX,MAAM,KAAK;CACX,MAAM,IAAI;CACV,IAAI,QAAQ,CAAC,KAAK,KAAK;CACvB,OACE,oBAAC,OAAD;EAAK,OAAO;EAAK,QAAQ;EAAK,MAAK;EAAM,cAAW;EAAY,OAAO,EAAE,SAAS,QAAQ;YACvF,OAAO,OAAO,KAAK,GAAG,MAAM;GAC3B,MAAM,QAAS,KAAK,IAAI,GAAG,CAAC,IAAI,QAAS,KAAK,KAAK;GACnD,MAAM,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK;GAClC,MAAM,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK;GAClC,MAAM,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,KAAK;GAC1C,MAAM,KAAK,KAAK,IAAI,KAAK,IAAI,QAAQ,KAAK;GAC1C,MAAM,QAAQ,QAAQ,KAAK,KAAK,IAAI;GACpC,MAAM,OACJ,SAAS,KAAK,KAAK,IAAI,OACnB,KAAK,KAAK,EAAE,GAAG,GAAG,KAAK,EAAE,GAAG,EAAE,aAAmB,GAAG,KAAK,EAAE,GAAG,EAAE,SAAS,KAAK,EAAE,GAAG,OACnF,KAAK,GAAG,GAAG,GAAG,KAAK,GAAG,GAAG,GAAG,KAAK,EAAE,GAAG,EAAE,KAAK,MAAM,KAAK,GAAG,GAAG,GAAG;GACvE,SAAS;GAET,OAAO,oBAAC,QAAD;IAAc,GAAG;IAAM,MADjB,OAAO,IAAI,IAAI,OAAO,IAAI,WAAW,OAAO;GACd,GAAzB,CAAyB;EAC7C,CAAC;CACE,CAAA;AAET;AAEA,SAAS,aAAa,MAA8D;CAClF,IAAI,CAAC,QAAQ,OAAO,SAAS,UAAU,OAAO;CAC9C,MAAM,SAAU,KAA8B;CAC9C,MAAM,SAAU,KAA8B;CAC9C,IAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,CAAC,MAAM,QAAQ,MAAM,GAAG,OAAO;CAC7D,IAAI,OAAO,WAAW,OAAO,QAAQ,OAAO;CAC5C,IAAI,CAAC,OAAO,OAAO,MAAM,OAAO,MAAM,YAAY,OAAO,MAAM,QAAQ,GAAG,OAAO;CACjF,IAAI,CAAC,OAAO,OAAO,MAAM,OAAO,MAAM,YAAY,OAAO,MAAM,QAAQ,GAAG,OAAO;CACjF,OAAO;EACL,QAAQ,OAAO,IAAI,MAAM;EACzB,QAAQ,OAAO,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,OAAO,CAAC,CAAE;CACnE;AACF;AAEA,SAAS,SAAS,OAAe,MAAkC;CACjE,OACE,qBAAC,OAAD;EACE,OAAO;GACL,QAAQ;GACR,UAAU;GACV,YAAY,OAAO;GACnB,OAAO,OAAO;GACd,SAAS;GACT,cAAc;GACd,UAAU;EACZ;YATF;GAWG;GAAM;GAAQ,KAAK,UAAU,MAAM,MAAM,CAAC;EACxC;;AAET;;;;;;;AC7KA,SAAgB,UAAU,EAAE,SAAS,KAAK,YAA+C;CAUvF,OACE,oBAAC,OAAD;EAAK,OAAO;GATZ,SAAS;GACT,eAAe;GACf,SAAS,WAAW;GACpB,KAAK,OAAO;GACZ,YAAY;GACZ,OAAO;GACP,WAAW;EAGK;EAAG,YAAS;EACzB;CACE,CAAA;AAET;;;;;;;;;ACbA,SAAgB,KAAK,EAAE,UAAU,YAA0C;CAGzE,IAFiB,OAAO,aAAa,YACR,SAAoB,SAAS,IAAI,GAE5D,OACE,oBAAC,OAAD;EACE,YAAS;EACT,iBAAe,YAAY;EAC3B,OAAO;GACL,YAAY,OAAO;GACnB,OAAO,OAAO;GACd,SAAS;GACT,cAAc,OAAO;GACrB,UAAU;GACV,UAAU;GACV,QAAQ;EACV;YAEA,oBAAC,QAAD,EAAO,SAAe,CAAA;CACnB,CAAA;CAGT,OACE,oBAAC,QAAD;EACE,YAAS;EACT,iBAAe,YAAY;EAC3B,OAAO;GACL,YAAY,OAAO;GACnB,OAAO,OAAO;GACd,SAAS;GACT,cAAc,OAAO;GACrB,UAAU;EACZ;EAEC;CACG,CAAA;AAEV;;;ACrCA,MAAM,QAAgC;CACpC,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;AACL;AAEA,SAAgB,QAAQ,EAAE,OAAO,OAAO,YAA6C;CACnF,MAAM,YAAY,WAAW,KAAK;CAElC,MAAM,QAA6B;EACjC,YAAY;EACZ,UAHW,MAAM,cAAc,MAAM;EAIrC,YAAY;EACZ,OAAO,SAAS,OAAO;EACvB,QAAQ;EACR,YAAY;CACd;CAIA,OAAO,MAAM,cAAc,IAAI,aAAa;EAAE;EAAO,YAAY;CAAU,GAAG,QAAQ;AACxF;AAEA,SAAS,WAAW,OAAkD;CACpE,IAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,GAAG,OAAO;CACjE,MAAM,IAAI,KAAK,MAAM,KAAK;CAC1B,IAAI,IAAI,GAAG,OAAO;CAClB,IAAI,IAAI,GAAG,OAAO;CAClB,OAAO;AACT;;;ACpCA,SAAgB,KAAK,EAAE,SAAS,UAAU,MAAM,OAAO,YAA0C;CAS/F,OACE,oBAAC,KAAD;EAAG,OAAO;GARV,YAAY;GACZ,UAAU,QAAQ;GAClB,YAAY,WAAW,SAAS,MAAM;GACtC,OAAO,SAAS,OAAO;GACvB,QAAQ;GACR,YAAY;EAGE;EAAG,YAAS;EACvB;CACA,CAAA;AAEP;;;;;;;ACLA,SAAgB,QAAQ,EAAE,OAAO,WAA4C;CAC3E,MAAM,aAAa,WAAW,WAAW,GAAG,MAAM,MAAM;CACxD,MAAM,CAAC,QAAQ,aAAa,MAAM,SAAS,UAAU;CACrD,MAAM,YAAiC;EACrC,YAAY;EACZ,SAAS;EACT,eAAe;EACf,KAAK;CACP;CACA,MAAM,YAAiC;EACrC,SAAS;EACT,KAAK;EACL,WAAW;EACX,SAAS;EACT,QAAQ;CACV;CACA,MAAM,cAAmC;EACvC,OAAO;EACP,QAAQ;EACR,cAAc;EACd,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,YAAY;EACZ,UAAU;EACV,YAAY;EACZ,QAAQ;CACV;CACA,MAAM,YAAiC;EACrC,QAAQ,aAAa,OAAO;EAC5B,cAAc;EACd,SAAS;EACT,YAAY,OAAO;EACnB,OAAO,OAAO;CAChB;CACA,OACE,qBAAC,OAAD;EAAK,YAAS;EAAU,OAAO;YAA/B,CACE,oBAAC,MAAD;GAAI,OAAO;aACR,MAAM,KAAK,GAAG,MAAM;IACnB,MAAM,SAAS,IAAI;IACnB,MAAM,YAAY,MAAM;IACxB,MAAM,SAAS,IAAI;IACnB,MAAM,YAAiC;KACrC,GAAG;KACH,YAAY,YACR,OAAO,iBACP,SACE,OAAO,cACP,OAAO;KACb,OAAO,aAAa,SAAS,SAAS,OAAO;KAC7C,SAAS,SAAS,KAAM;KACxB,QAAQ,SAAS,gBAAgB;IACnC;IACA,OACE,oBAAC,MAAD,EAAA,UACE,oBAAC,UAAD;KACE,MAAK;KACL,cAAY,QAAQ,IAAI,EAAE,IAAI,EAAE;KAChC,gBAAc,YAAY,SAAS,KAAA;KACnC,UAAU;KACV,eAAe,UAAU,CAAC;KAC1B,OAAO;eAEN,IAAI;IACC,CAAA,EACN,GAXK,EAAE,EAWP;GAER,CAAC;EACC,CAAA,GACJ,qBAAC,OAAD;GAAK,OAAO;aAAZ,CACE,oBAAC,OAAD;IAAK,OAAO;KAAE,YAAY;KAAK,cAAc;KAAO,UAAU;IAAO;cAClE,MAAM,OAAO,EAAE,SAAS;GACtB,CAAA,GACL,oBAAC,OAAD;IAAK,OAAO,EAAE,UAAU,OAAO;cAAI,MAAM,OAAO,EAAE,QAAQ;GAAU,CAAA,CACjE;IACF;;AAET;AAEA,SAAS,WAAW,GAAW,KAAqB;CAClD,IAAI,QAAQ,GAAG,OAAO;CACtB,IAAI,CAAC,OAAO,SAAS,CAAC,GAAG,OAAO;CAChC,OAAO,KAAK,IAAI,MAAM,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC;AACrD;;;ACzFA,SAAgB,KAAK,EAAE,OAAO,YAAY,GAAG,YAA0C;CACrF,MAAM,SACJ,aAAa,IACT,sCACA,cAAc,IACZ,qCACA,cAAc,IACZ,qCACA;CAkBV,OACE,qBAAC,WAAD;EAAS,YAAS;EAAO,OAAO;GAjBhC,YAAY,OAAO;GACnB,QAAQ,aAAa,OAAO;GAC5B,cAAc,OAAO;GACrB,WAAW;GACX,SAAS,MAAM;GACf,YAAY;GACZ,OAAO,OAAO;GACd,SAAS;GACT,eAAe;GACf,KAAK,MAAM;EAQ6B;YAAxC,CACG,UAAU,KAAA,IAAY,oBAAC,MAAD;GAAI,OAAO;IANpC,UAAU;IACV,YAAY;IACZ,QAAQ;GAIqC;aAAI;EAAU,CAAA,IAAI,MAC5D,QACM;;AAEb;;;;;;;;ACxBA,SAAgB,KAAK,EAAE,OAAO,SAAS,YAA0C;CAC/E,MAAM,QAA6B;EACjC,QAAQ;EACR,aAAa;EACb,YAAY;EACZ,OAAO,OAAO;EACd,UAAU;EACV,YAAY;CACd;CACA,IAAI,aAAa,KAAA,KAAa,aAAa,MACzC,OAAO,MAAM,cAAc,UAAU,OAAO,MAAM;EAAE;EAAO,YAAY;CAAO,GAAG,QAAQ;CAE3F,MAAM,MAAM,MAAM,QAAQ,KAAK,IAAI,QAAQ,UAAU,KAAA,IAAY,CAAC,IAAI,CAAC,KAAK;CAC5E,OAAO,MAAM,cACX,UAAU,OAAO,MACjB;EAAE;EAAO,YAAY;CAAO,GAC5B,IAAI,KAAK,MAAM,MAAM;EAGnB,OAAO,oBAAC,MAAD,EAAA,UADL,OAAO,SAAS,YAAY,OAAO,SAAS,WAAW,OAAO,IAAI,IAAI,KAAK,UAAU,IAAI,EAC9D,GAAb,CAAa;CAC/B,CAAC,CACH;AACF;;;ACRA,SAAgB,cACd,MACA,MACkB;CAClB,MAAM,QAAkB,CAAC;CACzB,MAAM,UAAoB,CAAC;CAC3B,MAAM,UAAoB,CAAC;CAC3B,MAAM,WAAW,OAAO,KAAK,IAAI;CACjC,MAAM,WAAW,OAAO,KAAK,IAAI;CACjC,MAAM,UAAU,IAAI,IAAI,QAAQ;CAChC,MAAM,UAAU,IAAI,IAAI,QAAQ;CAChC,KAAK,MAAM,KAAK,UAAU;EACxB,IAAI,CAAC,QAAQ,IAAI,CAAC,GAAG;GACnB,MAAM,KAAK,CAAC;GACZ;EACF;EACA,IAAI,CAAC,OAAO,GAAG,KAAK,IAAI,KAAK,EAAE,GAC7B,QAAQ,KAAK,CAAC;CAElB;CACA,KAAK,MAAM,KAAK,UACd,IAAI,CAAC,QAAQ,IAAI,CAAC,GAAG,QAAQ,KAAK,CAAC;CAErC,MAAM,KAAK;CACX,QAAQ,KAAK;CACb,QAAQ,KAAK;CACb,IAAI,MAAM,WAAW,KAAK,QAAQ,WAAW,KAAK,QAAQ,WAAW,GACnE,OAAO;CAET,OAAO;EAAE;EAAO;EAAS;CAAQ;AACnC;;;;;AAMA,SAAgB,UACd,IACA,IACA,MACA,MACkB;CAGlB,OAAO,cAFO,QAAQ,GAAG,SAAS,GACpB,QAAQ,GAAG,SAAS,CACD;AACnC;;;;;;;;;;;;;;;;AAiBA,SAAgB,iBAAiB,KAA4B;CAC3D,IAAI,IAAI,WAAW,GAAG,OAAO;CAC7B,IAAI,IAAI,SAAS,IAAI,OAAO;CAC5B,IAAI,KAAK,KAAK,GAAG,GAAG,OAAO;CAC3B,IAAI,CAAC,4BAA4B,KAAK,GAAG,GACvC,OACE;CAIJ,OAAO;AACT;;;;;AAMA,SAAgB,WAAW,OAA8B;CAEvD,KAAK,MAAM,KAAK,OAAO,KAAK,MAAM,SAAS,CAAC,GAC1C,MAAM,IAAI,GAAG,KAAA,CAAS;AAE1B;AAoBA,SAAgB,UAAU,OAAgC;CACxD,MAAM,OAAO,MAAM,SAAS;CAC5B,MAAM,WAA0B;EAC9B,SAAS;EACT,MAAM,OAAO,KAAK,IAAI,CAAC,CAAC,KAAK;EAC7B,QAAQ,EAAE,GAAG,KAAK;CACpB;CACA,OAAO,KAAK,UAAU,UAAU,MAAM,CAAC;AACzC;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,MACd,OACA,IACG;CAOH,OAAOA,MAAS,aAAa,GAAG,KAAK,CAAC;AACxC;;;;;;;;;;;;;;;;;;;;;;;;;AC7IA,SAAgB,WAAW,OAA2C;CACpE,MAAM,EAAE,MAAM,UAAU,aAAa;CACrC,MAAM,CAAC,SAAS,cAAc,MAAM,SAAS,KAAK;CAOlD,OACE,qBAAA,UAAA,EAAA,UAAA,CACG,SANU,MAAM,kBAAkB;EACrC,YAAY,MAAM,CAAC,CAAC;CACtB,GAAG,CAAC,CAIe,GAAG,OAAO,GACxB,UACC,oBAAC,OAAD;EACE,YAAS;EACT,iBAAe,YAAY;EAC3B,OAAO;GACL,YAAY;GACZ,OAAO;GACP,SAAS;GACT,cAAc;GACd,WAAW;GACX,YAAY;GACZ,UAAU;GACV,YAAY;GACZ,QAAQ;GACR,WAAW;GACX,YAAY;EACd;YAEA,oBAAC,QAAD,EAAA,UAAO,KAAW,CAAA;CACf,CAAA,IACH,IACJ,EAAA,CAAA;AAEN;;;;;;;;;;;;;;;;;;;;;;;;;ACPA,SAAS,gBAAgB,EACvB,OACA,MACA,UACA,QAMoB;CAEpB,OACE,qBAAC,OAAD;EACE,MAAK;EACL,YAAS;EACT,iBAAe;EACf,OAAO;GACL,QAAQ;GACR,YAAY;GACZ,OAAO;GACP,cAAc;GACd,SAAS;GACT,YAAY;GACZ,UAAU;GACV,UAAU;GACV,QAAQ;EACV;YAdF;GAgBE,oBAAC,OAAD;IAAK,OAAO;KAAE,YAAY;KAAK,cAAc;IAAM;cAlBzC,OAAO,GAAG,KAAK,8BAA8B;GAkBW,CAAA;GAClE,oBAAC,QAAD;IACE,YAAS;IACT,OAAO;KACL,SAAS;KACT,YAAY;KACZ,UAAU;KACV,YAAY;KACZ,SAAS;KACT,cAAc;KACd,YAAY;KACZ,WAAW;IACb;cAEC,MAAM;GACH,CAAA;GACL,SAAS,KAAA,IACR,oBAAC,OAAD;IAAK,OAAO,EAAE,WAAW,MAAM;cAC7B,oBAAC,YAAD;KAAkB;KAAgB;gBAC9B,QAAQ,YACR,oBAAC,UAAD;MACE,MAAK;MACL,SAAS;MACT,YAAS;MACT,OAAO;OACL,YAAY;OACZ,QAAQ;OACR,OAAO;OACP,SAAS;OACT,UAAU;OACV,cAAc;OACd,QAAQ;MACV;gBAEC,UAAU,gBAAgB;KACrB,CAAA;IAEA,CAAA;GACT,CAAA,IACH;EACD;;AAET;;;;;;;AAQA,IAAa,wBAAb,cAA2C,MAAM,UAG/C;CACA,QAAe,EAAE,OAAO,KAAK;CAE7B,OAAO,yBAAyB,OAAqB;EACnD,OAAO,EAAE,MAAM;CACjB;CAEA,kBAA2B,OAAc,MAA6B;EACpE,IAAI,KAAK,MAAM,SACb,IAAI;GACF,KAAK,MAAM,QAAQ,OAAO,EAAE,gBAAgB,KAAK,kBAAkB,GAAG,CAAC;EACzE,QAAQ,CAER;CAEJ;CAEA,SAAmC;EACjC,MAAM,EAAE,UAAU,KAAK;EACvB,IAAI,UAAU,MAAM,OAAO,KAAK,MAAM;EAEtC,MAAM,EAAE,UAAU,MAAM,UAAU,SAAS,KAAK;EAChD,IAAI,OAAO,aAAa,YAAY,OAAO,SAAS,KAAK;EACzD,IAAI,aAAa,KAAA,GAAW,OAAO;EACnC,OACE,oBAAC,iBAAD;GACS;GACD;GACI;GACJ;EACP,CAAA;CAEL;AACF;;;AClJA,MAAM,mBAAiC,OAAO,OAAO;CACnD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;;;;;AAOD,MAAa,eAA6B;;;;;;;;;;;;;;;;;;;AAoB1C,SAAgB,eAAe,WAAgD;CAO7E,MAAM,SAAuB,EAAE,GAAG,iBAAiB;CACnD,KAAK,MAAM,OAAO,OAAO,KAAK,SAAS,GAA6B;EAClE,MAAM,QAAQ,UAAU;EACxB,IAAI,UAAU,KAAA,GAAW;EACzB,IAAI,IAAI,OAAO,IAAI,EAAE,CAAE,YAAY,GAAG;EACtC,OAAO,OAAO;CAChB;CACA,OAAO,OAAO,OAAO,MAAM;AAC7B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playgenx/components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Default React 19 implementations for every entry in DEFAULT_REGISTRY. Pair with @playgenx/registry and the parser/validator pipeline.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.mjs",
|