@ggui-ai/gadgets 0.1.0-rc.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.
@@ -0,0 +1,663 @@
1
+ import { GadgetHook, GadgetDescriptor, GadgetExportBase, JsonValue } from '@ggui-ai/protocol';
2
+ export { GadgetError, GadgetHook, GadgetStatus } from '@ggui-ai/protocol';
3
+
4
+ /**
5
+ * `createGguiGadget` — the plugin SDK factory for ggui client
6
+ * library wrappers.
7
+ *
8
+ * Authors compose 3rd-party JS libraries (Leaflet, Mapbox, Stripe,
9
+ * Chart.js, …) into stable React hooks that satisfy
10
+ * {@link GadgetHook}. The factory:
11
+ *
12
+ * 1. Strictly validates the spec at call time via
13
+ * `strictGadgetDescriptorSchema` — non-empty teaching text
14
+ * (`description` / `usage` / `example`), at least one of
15
+ * `package` / `bundleUrl`, plus a non-function `hookImpl` check
16
+ * the schema can't see. Throws {@link WrapperConformanceError}
17
+ * with field-level paths on any violation.
18
+ * 2. Returns the React hook function as the primary export, with
19
+ * the serializable {@link GadgetDescriptor} descriptor attached
20
+ * as `.descriptor`. Single export, dual purpose:
21
+ *
22
+ * ```ts
23
+ * export const useLeafletMap = createGguiGadget({
24
+ * hook: 'useLeafletMap',
25
+ * description: '...',
26
+ * usage: '...',
27
+ * example: { ... },
28
+ * package: '@my-org/ggui-leaflet',
29
+ * styleUrl: '/leaflet.css',
30
+ * hookImpl: (options) => { …real React hook… },
31
+ * });
32
+ *
33
+ * // Call as a React hook from generated component code:
34
+ * const map = useLeafletMap({ center: [0, 0], zoom: 2 });
35
+ *
36
+ * // Register on App.gadgets via the descriptor:
37
+ * app.gadgets.push(useLeafletMap.descriptor);
38
+ * ```
39
+ *
40
+ * Why no `bind: (lib) => …` indirection: wrappers bundle their
41
+ * underlying dependencies at build time (per the "ggui hosts every
42
+ * bundle" model that the CSP derivation depends on). Authors `import L
43
+ * from 'leaflet'` inside the wrapper and tsup/esbuild rolls it into
44
+ * the published bundle. The factory doesn't need to load 3rd-party
45
+ * libs at runtime — just validate the spec + plumb the descriptor.
46
+ *
47
+ * Why no separate `HookResult` shape: the existing protocol
48
+ * {@link GadgetHook} already nails the return contract
49
+ * (`{value, status, error?, start, stop?}`). Re-using it keeps
50
+ * stdlib hooks and 3rd-party plugins indistinguishable at the
51
+ * consumption site — generated component code calls them the same
52
+ * way.
53
+ */
54
+
55
+ /**
56
+ * Author input to {@link createGguiGadget}. A `createGguiGadget` call
57
+ * builds a gadget PACKAGE that exposes exactly one HOOK export — so
58
+ * the spec keeps the per-export teaching fields flat (`hook`,
59
+ * `description`, `usage`, `example`, `gotchas`, `permission`,
60
+ * `required`) alongside the package identity + transport fields
61
+ * (`package`, `version`, `bundleUrl`, …) and a `hookImpl` runtime
62
+ * function. The factory assembles these into a
63
+ * {@link GadgetDescriptor} of the package+exports shape.
64
+ *
65
+ * Generic over `TOutput` / `TOptions` so consumers get type-safe
66
+ * value + options surfaces. Defaults to `void` / `unknown` for hooks
67
+ * that take no options.
68
+ */
69
+ interface GguiGadgetSpec<TOutput, TOptions = void> extends Omit<GadgetDescriptor, 'exports'>, Omit<GadgetExportBase, 'description' | 'usage' | 'example'> {
70
+ /**
71
+ * Hook name — `use`-prefixed camelCase. Becomes the single hook
72
+ * export of the assembled descriptor.
73
+ */
74
+ hook: string;
75
+ /** REQUIRED — see {@link GadgetExportBase.description}. */
76
+ description: string;
77
+ /** REQUIRED — see {@link GadgetExportBase.usage}. */
78
+ usage: string;
79
+ /** REQUIRED — see {@link GadgetExportBase.example}. */
80
+ example: JsonValue;
81
+ /**
82
+ * The React hook implementation. Conforms to
83
+ * {@link GadgetHook} — same shape stdlib hooks satisfy.
84
+ * Authors `import` underlying 3rd-party libs at build time and
85
+ * compose inside this function.
86
+ */
87
+ hookImpl: GadgetHook<TOutput, TOptions>;
88
+ }
89
+ /**
90
+ * Output of {@link createGguiGadget} — the React hook
91
+ * function with the serializable {@link GadgetDescriptor}
92
+ * descriptor attached as `.descriptor`. Authors export this as their
93
+ * primary surface; consumers call it as a hook and operators
94
+ * register the descriptor.
95
+ */
96
+ type GguiGadget<TOutput, TOptions = void> = GadgetHook<TOutput, TOptions> & {
97
+ /** Serializable registry entry — register on App.gadgets. */
98
+ readonly descriptor: GadgetDescriptor;
99
+ };
100
+ /**
101
+ * Conformance violation thrown by the wrapper-author SDK
102
+ * ({@link createGguiGadget} / `defineGadgetPackage`) when the supplied
103
+ * spec fails the strict registry schema or the SDK-specific checks
104
+ * (`impl` shape). Carries the zod issues so authoring tools can
105
+ * highlight individual fields.
106
+ */
107
+ declare class WrapperConformanceError extends Error {
108
+ readonly code: "wrapper_conformance";
109
+ readonly violations: ReadonlyArray<{
110
+ readonly path: ReadonlyArray<string | number>;
111
+ readonly message: string;
112
+ }>;
113
+ constructor(
114
+ /** Identifying label for the failing spec — a hook name
115
+ * (`createGguiGadget`) or a package name (`defineGadgetPackage`). */
116
+ label: string | undefined, violations: ReadonlyArray<{
117
+ readonly path: ReadonlyArray<string | number>;
118
+ readonly message: string;
119
+ }>);
120
+ }
121
+ /**
122
+ * Build a ggui gadget. See file-level docstring for
123
+ * the usage pattern.
124
+ *
125
+ * Throws {@link WrapperConformanceError} synchronously when the spec
126
+ * is malformed — caller-time validation means a bad wrapper fails at
127
+ * module-load, not at first invocation.
128
+ */
129
+ declare function createGguiGadget<TOutput, TOptions = void>(spec: GguiGadgetSpec<TOutput, TOptions>): GguiGadget<TOutput, TOptions>;
130
+
131
+ /**
132
+ * `defineGadgetPackage` — the wrapper-author SDK builder for a gadget
133
+ * PACKAGE that ships one or more exports, hooks AND/OR components,
134
+ * behind a single npm identity.
135
+ *
136
+ * Where {@link createGguiGadget} is the single-hook convenience (the
137
+ * common case — returns the callable hook with `.descriptor` grafted
138
+ * on), `defineGadgetPackage` is the general builder: it takes the
139
+ * package identity + transport metadata once, plus a list of export
140
+ * declarations, and returns the validated {@link GadgetDescriptor}.
141
+ *
142
+ * A gadget package mixing kinds — say a chart package exporting a
143
+ * `Chart` component + a `useChartTheme` hook — is exactly one
144
+ * descriptor with two `exports[]`. Authoring it:
145
+ *
146
+ * ```ts
147
+ * export const Chart: ComponentType<ChartProps> = (props) => { … };
148
+ * export const useChartTheme: GadgetHook<ChartTheme> = () => { … };
149
+ *
150
+ * export const chartDescriptor = defineGadgetPackage({
151
+ * package: '@my-org/gadget-chart',
152
+ * version: '0.0.1',
153
+ * styleUrl: 'https://…/chart.css',
154
+ * exports: [
155
+ * { component: 'Chart', impl: Chart,
156
+ * description: '…', usage: '…', example: { … } },
157
+ * { hook: 'useChartTheme', impl: useChartTheme,
158
+ * description: '…', usage: '…', example: { … } },
159
+ * ],
160
+ * });
161
+ * ```
162
+ *
163
+ * The author exports each impl directly (typed where defined — no
164
+ * casts) and `defineGadgetPackage` produces the registry descriptor.
165
+ * `impl` is threaded purely so the SDK can conformance-check it is a
166
+ * function at module-load (the same fail-fast `createGguiGadget` does
167
+ * for `hookImpl`); the builder never inspects its signature.
168
+ *
169
+ * Throws {@link WrapperConformanceError} synchronously on a malformed
170
+ * spec — a bad package fails at module-load, not at first use.
171
+ */
172
+
173
+ /**
174
+ * Any callable — a React hook or component. `defineGadgetPackage`
175
+ * conformance-checks `impl` is a function but never depends on its
176
+ * signature, so the broadest "some function" type is correct here
177
+ * (`never[]` params accept any concrete signature without variance
178
+ * friction; it is NOT `any` — the value stays opaque).
179
+ */
180
+ type GadgetImpl = (...args: never[]) => unknown;
181
+ /** Per-export teaching text — required for the registry-strict schema. */
182
+ interface GadgetExportTeaching {
183
+ /** Human-readable description of what the export does. */
184
+ readonly description: string;
185
+ /** When / why / by-whom the export is used. */
186
+ readonly usage: string;
187
+ /** Concrete usage example for boilerplate + prompt priming. */
188
+ readonly example: JsonValue;
189
+ /** Anti-patterns + known gotchas surfaced in code-gen prompts. */
190
+ readonly gotchas?: string;
191
+ /** Optional Web-Permissions identifier the export gates on. */
192
+ readonly permission?: string;
193
+ /** Whether the UI MUST mount this export. Default `false`. */
194
+ readonly required?: boolean;
195
+ }
196
+ /** A hook export declaration — `use`-prefixed name + its impl. */
197
+ interface GadgetHookExportSpec extends GadgetExportTeaching {
198
+ /** Hook name — `use`-prefixed camelCase (`HOOK_NAME_RE`). */
199
+ readonly hook: string;
200
+ /** The React hook implementation. */
201
+ readonly impl: GadgetImpl;
202
+ }
203
+ /** A component export declaration — PascalCase name + its impl. */
204
+ interface GadgetComponentExportSpec extends GadgetExportTeaching {
205
+ /** Component name — PascalCase (`COMPONENT_NAME_RE`). */
206
+ readonly component: string;
207
+ /** The React component implementation. */
208
+ readonly impl: GadgetImpl;
209
+ }
210
+ /**
211
+ * One export of a gadget package — a hook or a component,
212
+ * discriminated by which identifier field is present. Mirrors the
213
+ * protocol's {@link GadgetExport} field-presence union.
214
+ */
215
+ type GadgetExportSpec = GadgetHookExportSpec | GadgetComponentExportSpec;
216
+ /** Author input to {@link defineGadgetPackage}. */
217
+ interface GadgetPackageSpec {
218
+ /** Bare npm package name. */
219
+ readonly package: string;
220
+ /** Exact semver pin. */
221
+ readonly version: string;
222
+ /** ggui-hosted ESM bundle URL. */
223
+ readonly bundleUrl?: string;
224
+ /** Registry hostname the bundle URL is derived from. */
225
+ readonly bundleHost?: string;
226
+ /** `sha384-<base64>` SRI of the bundle. */
227
+ readonly bundleSri?: string;
228
+ /** Stylesheet URL the package ships. */
229
+ readonly styleUrl?: string;
230
+ /** Outbound origins the package's exports call at runtime (CSP). */
231
+ readonly connect?: readonly string[];
232
+ /** `App.publicEnv` keys the package's exports require. */
233
+ readonly requires?: readonly string[];
234
+ /** Published `.d.ts` URL — required registry-side for non-stdlib. */
235
+ readonly typesUrl?: string;
236
+ /** `sha384-<base64>` SRI of the `.d.ts`. */
237
+ readonly typesSri?: string;
238
+ /** The exports the package ships — at least one, hooks and/or
239
+ * components. */
240
+ readonly exports: readonly GadgetExportSpec[];
241
+ }
242
+ /**
243
+ * Build + validate a gadget package descriptor. See the file-level
244
+ * docstring for the authoring pattern.
245
+ *
246
+ * Throws {@link WrapperConformanceError} synchronously when the spec
247
+ * is malformed (non-function `impl`, missing teaching text, malformed
248
+ * package identity / export name).
249
+ */
250
+ declare function defineGadgetPackage(spec: GadgetPackageSpec): GadgetDescriptor;
251
+
252
+ /**
253
+ * `@ggui-ai/gadgets/catalog-adapter`.
254
+ *
255
+ * The contract surface between the ggui server and a per-deployment
256
+ * gadget catalog. The wire-side `DataContract.clientCapabilities.gadgets`
257
+ * is intentionally narrow — it is package-keyed and carries identity
258
+ * only (`(package, export name)`, no `version`, no transport
259
+ * metadata), so push-time resolution looks up the matching
260
+ * {@link GadgetDescriptor} descriptor by npm package name. This module
261
+ * declares the pluggable "where do descriptors come from?" port + ships
262
+ * two batteries-included implementations:
263
+ *
264
+ * - {@link InMemoryGadgetCatalog} — static map, configured at
265
+ * construction. Right for tests, the OSS default seed, and
266
+ * deployments that ship a fixed catalog at boot.
267
+ *
268
+ * - {@link CachingGadgetCatalog} — decorator over any other adapter
269
+ * with per-appId TTL caching + single-flight deduplication. Right
270
+ * for production cloud paths where descriptors live in DynamoDB /
271
+ * a registry service and re-fetching on every push would burn
272
+ * network round-trips. Caches are scoped per-appId so two apps
273
+ * never see each other's catalogs.
274
+ *
275
+ * ## Design contract
276
+ *
277
+ * - One method: `list(appId)`. Single batch read — never an N+1
278
+ * "fetch descriptor for one package at a time" pattern. The
279
+ * resolution caller indexes the returned array by `package`
280
+ * itself. Single network call per push (with cache, often zero).
281
+ *
282
+ * - Return type is `readonly GadgetDescriptor[]` — the same shape the
283
+ * wire push-time resolution + downstream consumers (boilerplate
284
+ * generator, CSP builder, Permissions-Policy deriver, system
285
+ * prompt builder) already speak.
286
+ *
287
+ * - Errors propagate. Adapters MUST throw on retrieval failure
288
+ * rather than returning an empty array — silent "no gadgets"
289
+ * would let pushes through with broken gadget refs and surface
290
+ * as render-time hook-resolution failures. The caller's job is
291
+ * to decide whether the push fails (gate fires) or proceeds
292
+ * ungated.
293
+ *
294
+ * ## Deployment-specific adapters
295
+ *
296
+ * Per-deployment adapters wear this same interface: a JSON-backed
297
+ * adapter reads from the `ggui.json#app.gadgets` array; a
298
+ * database-backed adapter reads from an app-metadata store. Both
299
+ * implement `GadgetCatalogAdapter`, so the enrichment pipeline doesn't
300
+ * care which environment is feeding it.
301
+ */
302
+
303
+ /**
304
+ * Per-deployment source of registered gadget descriptors.
305
+ *
306
+ * Named parties: caller (push-time resolution / dev tools) ↔ adapter
307
+ * (deployment-specific descriptor backing store, e.g., JSON, DynamoDB,
308
+ * in-memory).
309
+ *
310
+ * Obligations:
311
+ * - `list(appId)` MUST return the full set of descriptors registered
312
+ * for `appId` in a single call. No pagination at this layer;
313
+ * adapters that page internally MUST aggregate before returning.
314
+ * - On retrieval failure, the adapter MUST throw — silently
315
+ * returning `[]` is forbidden (it would mask broken catalogs).
316
+ *
317
+ * Failure mode: thrown error propagates to caller. Caller decides
318
+ * whether to fail the push (strict) or fall back to a default set
319
+ * (lenient).
320
+ *
321
+ * Observable violation: caller observes a thrown error or a
322
+ * descriptor-list whose contents are inconsistent with `appId`.
323
+ */
324
+ interface GadgetCatalogAdapter {
325
+ /**
326
+ * Resolve the full registered gadget catalog for a given app.
327
+ *
328
+ * Single-batch by design. Callers MUST NOT call this in a loop
329
+ * per-package; index the returned array by `entry.package` instead.
330
+ */
331
+ list(appId: string): Promise<readonly GadgetDescriptor[]>;
332
+ }
333
+ /**
334
+ * Static map-backed adapter. Pre-populated at construction; never
335
+ * fetches anything at runtime. Right for tests, examples, and
336
+ * deployments whose catalog is known at boot.
337
+ */
338
+ declare class InMemoryGadgetCatalog implements GadgetCatalogAdapter {
339
+ #private;
340
+ constructor(byApp: ReadonlyMap<string, readonly GadgetDescriptor[]>);
341
+ /**
342
+ * Convenience factory for the common "every app gets the same
343
+ * catalog" case (the OSS stdlib seed pattern). Apps not explicitly
344
+ * keyed fall back to the supplied default.
345
+ */
346
+ static withDefault(defaultEntries: readonly GadgetDescriptor[], perApp?: ReadonlyMap<string, readonly GadgetDescriptor[]>): InMemoryGadgetCatalog;
347
+ list(appId: string): Promise<readonly GadgetDescriptor[]>;
348
+ }
349
+ /**
350
+ * Decorator adapter — wraps any {@link GadgetCatalogAdapter} with
351
+ * per-appId TTL caching + single-flight deduplication. Right for
352
+ * production paths where the inner adapter hits DynamoDB / a registry
353
+ * service and per-push fetches would dominate latency.
354
+ *
355
+ * Concurrent `list(appId)` calls during a cache miss share ONE
356
+ * inflight Promise — no thundering herd against the inner adapter.
357
+ *
358
+ * Caches are scoped per-appId; entry TTL is configurable
359
+ * (default 30s — short enough that operator changes propagate
360
+ * promptly, long enough to amortize cold-start). Set `ttlMs: 0` for
361
+ * single-flight-only behavior (no TTL caching, just dedup).
362
+ */
363
+ interface CachingGadgetCatalogOptions {
364
+ /**
365
+ * Per-entry time-to-live in milliseconds. Cache entries older than
366
+ * this are refetched on next `list()`. `0` disables TTL caching
367
+ * entirely (single-flight dedup only).
368
+ *
369
+ * Default: 30000 (30s).
370
+ */
371
+ readonly ttlMs?: number;
372
+ /**
373
+ * Clock injection for tests. Default `Date.now`.
374
+ */
375
+ readonly now?: () => number;
376
+ }
377
+ declare class CachingGadgetCatalog implements GadgetCatalogAdapter {
378
+ #private;
379
+ constructor(inner: GadgetCatalogAdapter, options?: CachingGadgetCatalogOptions);
380
+ list(appId: string): Promise<readonly GadgetDescriptor[]>;
381
+ /**
382
+ * Drop the cached entry for `appId`. Right for operator-triggered
383
+ * invalidation (gadget registered / unregistered / updated). When
384
+ * `appId` is omitted, drops every cached entry.
385
+ */
386
+ invalidate(appId?: string): void;
387
+ }
388
+
389
+ /**
390
+ * `getPublicEnv(key, opts?)`.
391
+ *
392
+ * Wrapper-author accessor for the public env channel. Reads values
393
+ * the operator stamped on `App.publicEnv` and the server projected
394
+ * (filtered to declared wrappers' `requires`) onto
395
+ * `globalThis.__ggui__.publicEnv`.
396
+ *
397
+ * Usage pattern (inside a wrapper's `hookImpl`):
398
+ *
399
+ * ```ts
400
+ * import { createGguiGadget, getPublicEnv } from '@ggui-ai/gadgets';
401
+ * import mapboxgl from 'mapbox-gl';
402
+ *
403
+ * export const useMapbox = createGguiGadget({
404
+ * hook: 'useMapbox',
405
+ * requires: ['GGUI_PUBLIC_APP_MAPBOX_TOKEN'],
406
+ * hookImpl: (opts) => {
407
+ * mapboxgl.accessToken = getPublicEnv('GGUI_PUBLIC_APP_MAPBOX_TOKEN');
408
+ * return useMapboxInternal(opts);
409
+ * },
410
+ * // …
411
+ * });
412
+ * ```
413
+ *
414
+ * Semantics
415
+ * ---------
416
+ *
417
+ * - **Throws** when called before the iframe runtime initializes
418
+ * (`globalThis.__ggui__` absent). This indicates the wrapper is
419
+ * running outside the ggui iframe (test misconfig, host SDK
420
+ * misuse).
421
+ * - **Throws** by default when the requested key is not present in
422
+ * the registry. The thrown error names the missing key + lists
423
+ * available keys — gives gadget authors actionable diagnostics
424
+ * without leaking the values themselves.
425
+ * - Pass `{ optional: true }` to get `undefined` for a missing key
426
+ * instead of throwing. Use when the wrapper has a sensible
427
+ * fallback (e.g., a default origin); the push gate enforces that
428
+ * declared `requires` keys are present, so the typical wrapper
429
+ * path uses the throwing default.
430
+ * - Empty-string values are returned verbatim (the operator may
431
+ * have intentionally configured a key with no value).
432
+ *
433
+ * The push gate (`assertPublicEnvSatisfied`) verifies the
434
+ * `App.publicEnv` satisfies every declared wrapper's `requires` BEFORE
435
+ * the iframe boots. So in well-configured deployments, the only
436
+ * `getPublicEnv` throws are gadget authoring bugs (typoed key, key
437
+ * not declared in `requires`). In production, the push gate catches
438
+ * the misconfiguration upstream.
439
+ */
440
+ interface GetPublicEnvOptions {
441
+ /**
442
+ * When true, returns `undefined` for a missing key instead of
443
+ * throwing. Use sparingly — the push gate enforces declared
444
+ * `requires`, so a missing key usually indicates a wrapper bug
445
+ * (key not in `requires` array). The throwing default is the
446
+ * right call for keys that ARE declared in `requires`.
447
+ */
448
+ readonly optional?: boolean;
449
+ }
450
+ /**
451
+ * Return the public env value at `key`. Throws on missing-required;
452
+ * returns `undefined` when `{ optional: true }`.
453
+ *
454
+ * `target` is the global root to read from; defaults to `globalThis`.
455
+ * Exposed for tests; production callers omit it.
456
+ */
457
+ declare function getPublicEnv(key: string, opts?: GetPublicEnvOptions, target?: typeof globalThis): string | undefined;
458
+
459
+ /**
460
+ * `useGeolocation` — browser-capability hook for reading the user's
461
+ * current location via the Geolocation API. Satisfies
462
+ * `GadgetHook<GeolocationCoords, GeolocationOptions>`.
463
+ *
464
+ * Lifecycle:
465
+ * - `idle` — initial, no request fired.
466
+ * - `prompting` — permission prompt visible (browser-controlled).
467
+ * - `active` — permission granted; for watch mode, position updates
468
+ * stream as they arrive.
469
+ * - `completed` — terminal for one-shot mode (`watch: false`) after
470
+ * a successful read.
471
+ * - `denied` — user rejected the permission prompt or the request
472
+ * errored with `PERMISSION_DENIED`.
473
+ * - `error` — any other failure (`POSITION_UNAVAILABLE`, `TIMEOUT`,
474
+ * or `Geolocation API unavailable`).
475
+ *
476
+ * Contract authors declare this hook via
477
+ * `clientCapabilities.gadgets['@ggui-ai/gadgets'] = { useGeolocation: {} }`
478
+ * and the UI generator emits the import + call site. Values surface to
479
+ * the agent via `contextSpec` (the component code threads `value` into
480
+ * the relevant slot's setter).
481
+ */
482
+
483
+ /** Coordinates returned by the hook. Pure JSON for easy contextSpec
484
+ * threading. */
485
+ interface GeolocationCoords {
486
+ readonly latitude: number;
487
+ readonly longitude: number;
488
+ readonly accuracy: number;
489
+ readonly altitude?: number;
490
+ readonly altitudeAccuracy?: number;
491
+ readonly heading?: number;
492
+ readonly speed?: number;
493
+ readonly timestamp: number;
494
+ }
495
+ /** Options accepted by `useGeolocation`. */
496
+ interface GeolocationOptions {
497
+ /** Continuously stream updates instead of single read. Default: false. */
498
+ readonly watch?: boolean;
499
+ /** Request high-accuracy positioning. Default: false. */
500
+ readonly enableHighAccuracy?: boolean;
501
+ /** Max age (ms) of a cached position the browser may return.
502
+ * Default: 0 (always fresh). */
503
+ readonly maximumAge?: number;
504
+ /** Request timeout in ms. Default: Infinity. */
505
+ readonly timeout?: number;
506
+ }
507
+ declare const useGeolocation: GadgetHook<GeolocationCoords, GeolocationOptions>;
508
+
509
+ /**
510
+ * `useClipboardWrite` — browser-capability hook for writing text to
511
+ * the system clipboard via `navigator.clipboard.writeText`. Satisfies
512
+ * `GadgetHook<string, ClipboardWriteOptions>`.
513
+ *
514
+ * One-shot semantics: call `start({text})` to write; status moves
515
+ * `idle → prompting → completed` on success or `idle → prompting →
516
+ * denied/error` on failure. The hook's `value` carries the most
517
+ * recently written text so component code can confirm what landed
518
+ * on the clipboard.
519
+ *
520
+ * `useClipboardPaste` is a separate hook — the read direction has
521
+ * different permission semantics (requires explicit user-gesture
522
+ * activation in most browsers).
523
+ */
524
+
525
+ interface ClipboardWriteOptions {
526
+ /** Text to write. Required at call site, not at hook construction. */
527
+ readonly text: string;
528
+ }
529
+ declare const useClipboardWrite: GadgetHook<string, ClipboardWriteOptions>;
530
+
531
+ /**
532
+ * `useClipboardPaste` — browser-capability hook for reading text from
533
+ * the system clipboard via `navigator.clipboard.readText`. Satisfies
534
+ * `GadgetHook<string, void>`.
535
+ *
536
+ * Most browsers require an explicit user gesture (button click) to
537
+ * invoke `start()` — wire this hook to a "paste" button rather than
538
+ * firing on mount.
539
+ *
540
+ * Lifecycle: idle → prompting → completed (with the text in `value`)
541
+ * on success; denied/error on failure.
542
+ */
543
+
544
+ declare const useClipboardPaste: GadgetHook<string>;
545
+
546
+ /**
547
+ * `useNotifications` — browser-capability hook for showing system
548
+ * notifications via the Notification API. Satisfies
549
+ * `GadgetHook<NotificationResult, NotificationOptions>`.
550
+ *
551
+ * Permission semantics: the first `start()` call requests permission
552
+ * via `Notification.requestPermission()`; subsequent calls reuse the
553
+ * granted permission. Permission denial is sticky on the browser
554
+ * side — once denied, future `start()` calls error with
555
+ * `permission_denied` without re-prompting.
556
+ *
557
+ * Lifecycle: idle → prompting → completed (after notification
558
+ * dismissed/clicked) or denied/error.
559
+ */
560
+
561
+ interface NotificationOptions_ {
562
+ readonly title: string;
563
+ readonly body?: string;
564
+ readonly icon?: string;
565
+ readonly tag?: string;
566
+ }
567
+ interface NotificationResult {
568
+ readonly outcome: 'clicked' | 'closed';
569
+ readonly tag?: string;
570
+ }
571
+ declare const useNotifications: GadgetHook<NotificationResult, NotificationOptions_>;
572
+
573
+ /**
574
+ * `useFilePicker` — browser-capability hook for prompting the user
575
+ * to select one or more files. Uses `<input type="file">` as the
576
+ * universal fallback; the File System Access API
577
+ * (`showOpenFilePicker`) is preferred when available.
578
+ *
579
+ * Returns one entry per selected file with `name`, `size`, `type`,
580
+ * and the raw `File` handle on `_file` for component code that
581
+ * wants to read content (the contract doesn't carry the File over
582
+ * the wire — component reads + extracts what it needs for the
583
+ * agent's contextSpec / actionSpec payload).
584
+ *
585
+ * Lifecycle: idle → prompting → completed (with picked files) or
586
+ * denied/error.
587
+ */
588
+
589
+ interface FilePickerOptions {
590
+ /** Allow multi-select. Default: false. */
591
+ readonly multiple?: boolean;
592
+ /** MIME-type filter (e.g., 'image/*', '.pdf'). Optional. */
593
+ readonly accept?: string;
594
+ }
595
+ interface PickedFile {
596
+ readonly name: string;
597
+ readonly size: number;
598
+ readonly type: string;
599
+ /** Raw File handle. NOT serialized — component reads + extracts. */
600
+ readonly _file: File;
601
+ }
602
+ interface FilePickerResult {
603
+ readonly files: readonly PickedFile[];
604
+ }
605
+ declare const useFilePicker: GadgetHook<FilePickerResult, FilePickerOptions>;
606
+
607
+ /**
608
+ * `useMicrophone` — browser-capability hook for capturing audio via
609
+ * `MediaDevices.getUserMedia({audio: true})` + MediaRecorder.
610
+ *
611
+ * Lifecycle: idle → prompting → active (recording) → completed
612
+ * (with the captured blob). `stop()` ends the recording; without it,
613
+ * the recording continues until the user revokes mic access or the
614
+ * tab loses focus.
615
+ *
616
+ * Returns the captured audio as a Blob + duration metadata so
617
+ * component code can thread the URL or upload-handle into a
618
+ * contextSpec slot or actionSpec payload.
619
+ */
620
+
621
+ interface MicrophoneOptions {
622
+ /** MIME type for the recorder. Defaults to whatever the browser supports. */
623
+ readonly mimeType?: string;
624
+ }
625
+ interface MicrophoneResult {
626
+ readonly blob: Blob;
627
+ readonly durationMs: number;
628
+ readonly mimeType: string;
629
+ }
630
+ declare const useMicrophone: GadgetHook<MicrophoneResult, MicrophoneOptions>;
631
+
632
+ /**
633
+ * `useCamera` — browser-capability hook for capturing a still photo
634
+ * via `MediaDevices.getUserMedia({video: true})` + canvas drawing.
635
+ *
636
+ * Lifecycle: idle → prompting → active (camera live) → completed
637
+ * (with the captured photo as a data URL). The hook returns a single
638
+ * snapshot per `start()` — call `stop()` to release the camera
639
+ * without taking a photo.
640
+ *
641
+ * Component code threads the data URL into a contextSpec slot or
642
+ * actionSpec payload. For continuous video / multi-frame capture, a
643
+ * different hook would be needed (out of v1 scope).
644
+ */
645
+
646
+ interface CameraOptions {
647
+ /** Prefer front ('user') or rear ('environment') camera. Optional. */
648
+ readonly facingMode?: 'user' | 'environment';
649
+ /** MIME type for the encoded snapshot. Default: 'image/png'. */
650
+ readonly mimeType?: string;
651
+ /** JPEG quality 0–1. Only used when mimeType is image/jpeg. */
652
+ readonly quality?: number;
653
+ }
654
+ interface CameraResult {
655
+ /** Encoded snapshot as a data URL. */
656
+ readonly dataUrl: string;
657
+ readonly width: number;
658
+ readonly height: number;
659
+ readonly mimeType: string;
660
+ }
661
+ declare const useCamera: GadgetHook<CameraResult, CameraOptions>;
662
+
663
+ export { CachingGadgetCatalog, type CachingGadgetCatalogOptions, type CameraOptions, type CameraResult, type ClipboardWriteOptions, type FilePickerOptions, type FilePickerResult, type GadgetCatalogAdapter, type GadgetComponentExportSpec, type GadgetExportSpec, type GadgetHookExportSpec, type GadgetImpl, type GadgetPackageSpec, type GeolocationCoords, type GeolocationOptions, type GetPublicEnvOptions, type GguiGadget, type GguiGadgetSpec, InMemoryGadgetCatalog, type MicrophoneOptions, type MicrophoneResult, type NotificationOptions_ as NotificationOptions, type NotificationResult, type PickedFile, WrapperConformanceError, createGguiGadget, defineGadgetPackage, getPublicEnv, useCamera, useClipboardPaste, useClipboardWrite, useFilePicker, useGeolocation, useMicrophone, useNotifications };