@playgenx/components 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/state-cli.mjs +196 -0
- 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 +6 -2
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
/**
|
|
4
|
+
* playgenx-state — CLI for inspecting and operating on
|
|
5
|
+
* PlaygroundState snapshots.
|
|
6
|
+
*
|
|
7
|
+
* Pure ESM, no extra deps. Reads JSON from stdin (or argv),
|
|
8
|
+
* writes JSON to stdout.
|
|
9
|
+
*
|
|
10
|
+
* Subcommands:
|
|
11
|
+
*
|
|
12
|
+
* snapshot Pretty-print a snapshot in the v1 envelope format.
|
|
13
|
+
* Accepts a JSON object on stdin (the snapshot you
|
|
14
|
+
* captured from `state.snapshot()` or a DebugSurface).
|
|
15
|
+
*
|
|
16
|
+
* validate Validate one or more stateKey strings from stdin.
|
|
17
|
+
* Reads a JSON array of strings, returns the per-key
|
|
18
|
+
* verdict.
|
|
19
|
+
*
|
|
20
|
+
* diff Diff two snapshots. Reads `{prev, next}` from stdin
|
|
21
|
+
* and returns the structured {added, changed, removed}
|
|
22
|
+
* diff (or null when equal).
|
|
23
|
+
*
|
|
24
|
+
* keys List the sorted keys of a snapshot from stdin.
|
|
25
|
+
*
|
|
26
|
+
* count Count the keys in a snapshot from stdin.
|
|
27
|
+
*
|
|
28
|
+
* Usage examples (run from the components package root):
|
|
29
|
+
*
|
|
30
|
+
* echo '{"volume": 5}' | node bin/state-cli.mjs snapshot
|
|
31
|
+
* echo '["volume","user.name","bad key"]' | node bin/state-cli.mjs validate
|
|
32
|
+
* echo '{"prev":{"a":1},"next":{"a":2,"b":3}}' | node bin/state-cli.mjs diff
|
|
33
|
+
*
|
|
34
|
+
* Exit codes:
|
|
35
|
+
*
|
|
36
|
+
* 0 success
|
|
37
|
+
* 1 bad subcommand
|
|
38
|
+
* 2 bad input (not parseable as JSON, wrong shape, etc.)
|
|
39
|
+
* 3 validation failure (one or more keys rejected)
|
|
40
|
+
*
|
|
41
|
+
* @packageDocumentation
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import {
|
|
45
|
+
dumpState,
|
|
46
|
+
diffSnapshots,
|
|
47
|
+
validateStateKey,
|
|
48
|
+
} from '../dist/index.mjs';
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Read all of stdin into a string. Node-only.
|
|
52
|
+
*/
|
|
53
|
+
async function readStdin() {
|
|
54
|
+
if (process.stdin.isTTY) {
|
|
55
|
+
// Interactive / no pipe — return empty; the caller will treat
|
|
56
|
+
// this as "no input" and print usage.
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
const chunks = [];
|
|
60
|
+
for await (const chunk of process.stdin) {
|
|
61
|
+
chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk);
|
|
62
|
+
}
|
|
63
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function usage() {
|
|
67
|
+
return [
|
|
68
|
+
'Usage: playgenx-state <subcommand> [input]',
|
|
69
|
+
'',
|
|
70
|
+
'Subcommands:',
|
|
71
|
+
' snapshot Pretty-print a state snapshot (JSON on stdin)',
|
|
72
|
+
' validate Validate an array of stateKey strings (JSON array on stdin)',
|
|
73
|
+
' diff Diff two snapshots ({prev, next} on stdin)',
|
|
74
|
+
' keys List sorted keys of a snapshot (JSON on stdin)',
|
|
75
|
+
' count Count keys in a snapshot (JSON on stdin)',
|
|
76
|
+
'',
|
|
77
|
+
'Reads from stdin when no arg is given.',
|
|
78
|
+
].join('\n');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function ok(payload) {
|
|
82
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + '\n');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function fail(code, msg) {
|
|
86
|
+
process.stderr.write(`error: ${msg}\n`);
|
|
87
|
+
process.exit(code);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Convert an arbitrary snapshot to the StateEnvelope shape. The
|
|
92
|
+
* input is whatever the caller passed; we expect a plain object.
|
|
93
|
+
*/
|
|
94
|
+
function envelope(input) {
|
|
95
|
+
const keys = Object.keys(input).sort();
|
|
96
|
+
return { version: 1, keys, values: { ...input } };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function main(argv) {
|
|
100
|
+
const sub = argv[0];
|
|
101
|
+
if (!sub || sub === '-h' || sub === '--help') {
|
|
102
|
+
process.stdout.write(usage() + '\n');
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
const stdinPayload = (await readStdin()).trim();
|
|
106
|
+
const argPayload = argv[1];
|
|
107
|
+
|
|
108
|
+
switch (sub) {
|
|
109
|
+
case 'snapshot': {
|
|
110
|
+
const input = parseObject(argPayload ?? stdinPayload);
|
|
111
|
+
// Re-use the library's envelope so the CLI output matches the
|
|
112
|
+
// canonical dumpState shape. (We don't have a real store to
|
|
113
|
+
// pass to dumpState; envelope() builds the same structure.)
|
|
114
|
+
const env = envelope(input);
|
|
115
|
+
process.stdout.write(JSON.stringify(env, null, 2) + '\n');
|
|
116
|
+
// Reference dumpState so it's not tree-shaken — the import
|
|
117
|
+
// is documented as the canonical envelope producer.
|
|
118
|
+
void dumpState;
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case 'validate': {
|
|
123
|
+
const input = parseArray(argPayload ?? stdinPayload);
|
|
124
|
+
const results = input.map((k) => {
|
|
125
|
+
if (typeof k !== 'string') {
|
|
126
|
+
return { key: String(k), ok: false, error: 'must be a string' };
|
|
127
|
+
}
|
|
128
|
+
const err = validateStateKey(k);
|
|
129
|
+
return err === null
|
|
130
|
+
? { key: k, ok: true }
|
|
131
|
+
: { key: k, ok: false, error: err };
|
|
132
|
+
});
|
|
133
|
+
const anyFailed = results.some((r) => !r.ok);
|
|
134
|
+
ok(results);
|
|
135
|
+
return anyFailed ? 3 : 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
case 'diff': {
|
|
139
|
+
const input = parseObject(argPayload ?? stdinPayload);
|
|
140
|
+
if (!('prev' in input) || !('next' in input)) {
|
|
141
|
+
fail(2, 'diff expects {prev, next}');
|
|
142
|
+
}
|
|
143
|
+
const prev = input.prev;
|
|
144
|
+
const next = input.next;
|
|
145
|
+
if (typeof prev !== 'object' || prev === null) fail(2, 'prev must be an object');
|
|
146
|
+
if (typeof next !== 'object' || next === null) fail(2, 'next must be an object');
|
|
147
|
+
const d = diffSnapshots(prev, next);
|
|
148
|
+
ok(d);
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
case 'keys': {
|
|
153
|
+
const input = parseObject(argPayload ?? stdinPayload);
|
|
154
|
+
ok(Object.keys(input).sort());
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
case 'count': {
|
|
159
|
+
const input = parseObject(argPayload ?? stdinPayload);
|
|
160
|
+
ok({ keys: Object.keys(input).length });
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
default:
|
|
165
|
+
process.stderr.write(usage() + '\n');
|
|
166
|
+
return 1;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function parseObject(raw) {
|
|
171
|
+
if (raw === '') fail(2, 'expected a JSON object on stdin or as argv[1]');
|
|
172
|
+
try {
|
|
173
|
+
const v = JSON.parse(raw);
|
|
174
|
+
if (typeof v !== 'object' || v === null || Array.isArray(v)) {
|
|
175
|
+
fail(2, 'expected a JSON object');
|
|
176
|
+
}
|
|
177
|
+
return v;
|
|
178
|
+
} catch (err) {
|
|
179
|
+
fail(2, `JSON parse error: ${err.message}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function parseArray(raw) {
|
|
184
|
+
if (raw === '') fail(2, 'expected a JSON array on stdin or as argv[1]');
|
|
185
|
+
try {
|
|
186
|
+
const v = JSON.parse(raw);
|
|
187
|
+
if (!Array.isArray(v)) fail(2, 'expected a JSON array');
|
|
188
|
+
return v;
|
|
189
|
+
} catch (err) {
|
|
190
|
+
fail(2, `JSON parse error: ${err.message}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
main(process.argv.slice(2))
|
|
195
|
+
.then((code) => process.exit(code))
|
|
196
|
+
.catch((err) => fail(2, err.message ?? String(err)));
|
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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"}
|