dsh-generative-ui 0.0.0 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +90 -0
  3. package/cordis.patch.yml +6 -0
  4. package/lib/client.js +18568 -0
  5. package/lib/client.js.map +62 -0
  6. package/lib/index.js +1597 -0
  7. package/lib/types/client/canvas/CanvasLauncher.d.ts +6 -0
  8. package/lib/types/client/canvas/CanvasPanel.d.ts +88 -0
  9. package/lib/types/client/canvas/collect.d.ts +45 -0
  10. package/lib/types/client/canvas/index.d.ts +43 -0
  11. package/lib/types/client/canvas/mount.d.ts +30 -0
  12. package/lib/types/client/canvas/panel-css.d.ts +1 -0
  13. package/lib/types/client/canvas/read.d.ts +12 -0
  14. package/lib/types/client/canvas/subpages.d.ts +20 -0
  15. package/lib/types/client/canvas/useDismissable.d.ts +15 -0
  16. package/lib/types/client/index.d.ts +20 -0
  17. package/lib/types/client/runtime/GenUISurface.d.ts +159 -0
  18. package/lib/types/client/runtime/bindings.d.ts +143 -0
  19. package/lib/types/client/runtime/compiler.d.ts +35 -0
  20. package/lib/types/client/runtime/inline-fence.d.ts +23 -0
  21. package/lib/types/client/runtime/observe.d.ts +30 -0
  22. package/lib/types/client/runtime/register.d.ts +2 -0
  23. package/lib/types/client/runtime/registry.d.ts +7 -0
  24. package/lib/types/client/runtime/report-error.d.ts +17 -0
  25. package/lib/types/client/runtime/segments.d.ts +18 -0
  26. package/lib/types/client/runtime/state.d.ts +18 -0
  27. package/lib/types/client/runtime/uno-config.d.ts +16 -0
  28. package/lib/types/client/runtime/uno.d.ts +50 -0
  29. package/lib/types/client/session.d.ts +26 -0
  30. package/lib/types/contract-assets.d.ts +41 -0
  31. package/lib/types/contract.d.ts +56 -0
  32. package/lib/types/index.d.ts +255 -0
  33. package/lib/types/prompt.d.ts +13 -0
  34. package/lib/types/skill.d.ts +27 -0
  35. package/package.json +135 -9
  36. package/src/client/canvas/CanvasLauncher.tsx +52 -0
  37. package/src/client/canvas/CanvasPanel.tsx +238 -0
  38. package/src/client/canvas/collect.ts +188 -0
  39. package/src/client/canvas/index.ts +255 -0
  40. package/src/client/canvas/mount.ts +91 -0
  41. package/src/client/canvas/panel-css.ts +2 -0
  42. package/src/client/canvas/panel.css +242 -0
  43. package/src/client/canvas/read.ts +55 -0
  44. package/src/client/canvas/subpages.ts +109 -0
  45. package/src/client/canvas/useDismissable.ts +37 -0
  46. package/src/client/index.ts +217 -0
  47. package/src/client/runtime/GenUISurface.tsx +359 -0
  48. package/src/client/runtime/bindings.ts +292 -0
  49. package/src/client/runtime/compiler.ts +80 -0
  50. package/src/client/runtime/inline-fence.ts +222 -0
  51. package/src/client/runtime/observe.ts +65 -0
  52. package/src/client/runtime/register.ts +57 -0
  53. package/src/client/runtime/registry.ts +65 -0
  54. package/src/client/runtime/report-error.ts +79 -0
  55. package/src/client/runtime/segments.ts +116 -0
  56. package/src/client/runtime/state.ts +47 -0
  57. package/src/client/runtime/uno-config.ts +71 -0
  58. package/src/client/runtime/uno.ts +124 -0
  59. package/src/client/session.ts +46 -0
  60. package/src/contract-assets.ts +46 -0
  61. package/src/contract.ts +111 -0
  62. package/src/index.ts +583 -0
  63. package/src/prompt.ts +377 -0
  64. package/src/skill.ts +931 -0
  65. package/types/README.md +34 -0
  66. package/types/ai.d.ts +14 -0
  67. package/types/chat.d.ts +14 -0
  68. package/types/check.ts +39 -0
  69. package/types/exec.d.ts +17 -0
  70. package/types/fs.d.ts +17 -0
  71. package/types/importmap.json +10 -0
  72. package/types/standalone/ai.js +7 -0
  73. package/types/standalone/chat.js +6 -0
  74. package/types/standalone/exec.js +7 -0
  75. package/types/standalone/fs.js +18 -0
  76. package/types/standalone/importmap.json +10 -0
  77. package/types/standalone/state.js +24 -0
  78. package/types/standalone/web.js +7 -0
  79. package/types/state.d.ts +25 -0
  80. package/types/web.d.ts +31 -0
  81. package/index.js +0 -1
@@ -0,0 +1,34 @@
1
+ # `$dsh/*` type declarations
2
+
3
+ Two import maps, because the commands want different things:
4
+
5
+ | File | For | Targets |
6
+ | --- | --- | --- |
7
+ | `importmap.json` | `genui check`, `genui lint` | the `.d.ts` beside it |
8
+ | `standalone/importmap.json` | `genui build`, `genui dev` | generated JS stubs |
9
+
10
+ `build` and `dev` produce a page with no dsh around it, so the capabilities genuinely cannot
11
+ work there — the conversation, the model and the workspace all live in the harness. The stubs
12
+ make such a card *build* anyway: each call warns to the console and returns an empty value of
13
+ the right shape (`""`, `[]`, an empty async iterator), so a page that `await`s one keeps
14
+ running instead of dying on `undefined`. Everything that does not touch the harness — layout,
15
+ styling, local state — is real. Anything that does is inert.
16
+
17
+ `standalone/` is generated by `scripts/gen-standalone.ts`, which enumerates the export names
18
+ from `bind()` rather than a second hand-written list, so a capability added to the
19
+ implementation cannot go missing from the stubs. It runs before `build` and `prepare`.
20
+
21
+ The declarations are hand-written rather than emitted from `src/client/runtime/bindings.ts`:
22
+ what a card should see is the capability surface, not how it reaches the host.
23
+
24
+ `check.ts` keeps them honest — it asserts the declared surface and `bind()`'s return type are
25
+ assignable **both ways**, so a declaration that is narrower than the implementation (hiding
26
+ capability) fails just as a wider one does (promising what the runtime will not do). Verified
27
+ it catches both an added method and a changed signature. Editing a `.d.ts` therefore means
28
+ editing the transcription in `check.ts` too; that duplication is the point, since a check that
29
+ derives from the thing it checks proves nothing.
30
+
31
+ Pointing the map at the real `.ts` sources instead was measured and rejected: `paths` targets
32
+ are compiled as part of the program, so the implementation's own diagnostics — a module it
33
+ imports, an error in a file beside it — surface as errors on the model'"'"'s card, about code the
34
+ model cannot see or fix.
package/types/ai.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ /** `$dsh/ai` — stream from the app's own model. See `types/chat.d.ts` on why these are hand-written. */
2
+ declare module "$dsh/ai" {
3
+ /**
4
+ * One user turn plus an optional system prompt; there is no conversation here.
5
+ *
6
+ * Pass `signal` when the card can start a second call before the first finishes — regenerating
7
+ * per keystroke, or a Stop button. Aborting stops the generation itself, not just your reading
8
+ * of it. The abort surfaces as an `AbortError`, which is the one rejection that is not a
9
+ * failure: `if (error.name === "AbortError") return;` before showing anything.
10
+ */
11
+ export type StreamOptions = { prompt: string; system?: string; signal?: AbortSignal };
12
+ /** Yields text as it arrives, one piece per network chunk. Inherits the app's model and credentials. */
13
+ export function streamText(options: StreamOptions | string): AsyncIterable<string>;
14
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `$dsh/chat` — drive the next conversation turn from inside a card.
3
+ *
4
+ * Hand-written rather than emitted: the implementation's types are shaped by how it reaches
5
+ * the host, and what a card should see is the surface, not the plumbing. `types/check.ts`
6
+ * asserts the two stay assignable, so drift fails the build rather than the model.
7
+ */
8
+ declare module "$dsh/chat" {
9
+ /**
10
+ * Sends a prompt into the current session, exactly as the composer would — the text lands
11
+ * in the transcript as the user's own message.
12
+ */
13
+ export function sendMessage(text: string): void;
14
+ }
package/types/check.ts ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Asserts the hand-written `$dsh/*` declarations still describe what `bind()` returns.
3
+ *
4
+ * They are hand-written on purpose — a card should see the capability surface, not how it
5
+ * reaches the host — but a surface nobody checks drifts, and the model would find out by
6
+ * getting a false error from `genui check`. This file is type-only: `bun run typecheck` fails
7
+ * if a signature moves without its declaration moving too.
8
+ *
9
+ * The declarations are IMPORTED, not transcribed. An earlier version restated them by hand and
10
+ * said so: "editing a `.d.ts` alone changes nothing — replacing `bash(command: string)` with
11
+ * `bash(command: number)` leaves `tsc` silent." That was true, and it made this file a check on
12
+ * a copy rather than on the thing — the same shape as `compiler.test.ts` testing a
13
+ * re-implementation of its own module. `types/` is on the tsconfig `include`, so the ambient
14
+ * `declare module` blocks resolve here directly.
15
+ */
16
+ import type * as Ai from "$dsh/ai";
17
+ import type * as Chat from "$dsh/chat";
18
+ import type * as Exec from "$dsh/exec";
19
+ import type * as Web from "$dsh/web";
20
+ import type * as Fs from "$dsh/fs";
21
+ import type { bind } from "../src/client/runtime/bindings.ts";
22
+
23
+ type Bound = ReturnType<typeof bind>;
24
+
25
+ type Declared = {
26
+ chat: { sendMessage: typeof Chat.sendMessage };
27
+ ai: { streamText: typeof Ai.streamText };
28
+ fs: { readFile: typeof Fs.readFile; readdir: typeof Fs.readdir; readBytes: typeof Fs.readBytes; writeFile: typeof Fs.writeFile };
29
+ exec: { bash: typeof Exec.bash };
30
+ web: { search: typeof Web.search };
31
+ state: { usePersistedState: <T>(key: string, initial: T | (() => T)) => [T, import("react").Dispatch<import("react").SetStateAction<T>>] };
32
+ };
33
+
34
+ // Both directions: a declaration narrower than the implementation hides capability, and one
35
+ // wider promises what the runtime will not do. Either way the model is told something untrue.
36
+ const _implementationSatisfiesDeclaration: Declared = null as unknown as Bound;
37
+ const _declarationCoversImplementation: Bound = null as unknown as Declared;
38
+ void _implementationSatisfiesDeclaration;
39
+ void _declarationCoversImplementation;
@@ -0,0 +1,17 @@
1
+ /** `$dsh/exec` — one command in the workspace, under the session's own sandbox mode. */
2
+ declare module "$dsh/exec" {
3
+ /** What a command left behind. `truncated` means output was cut, not that it failed. */
4
+ export type ExecResult = { stdout: string; stderr: string; exitCode: number | null; truncated: { stdout: boolean; stderr: boolean }; timedOut: boolean };
5
+ /**
6
+ * Runs `command` and resolves with its output.
7
+ *
8
+ * A non-zero exit RESOLVES — check `exitCode` rather than catching. Only a failure to run
9
+ * at all rejects. Commands are killed after 15 seconds.
10
+ *
11
+ * Pass a `signal` when the card runs commands on a timer or per keystroke: aborting kills
12
+ * the command itself, not just the wait, so a slow run cannot pile up behind the next one.
13
+ * An aborted call REJECTS with an `AbortError` — the one rejection that is not a failure,
14
+ * so ignore it rather than showing it (`if (e.name === "AbortError") return`).
15
+ */
16
+ export function bash(command: string, options?: { signal?: AbortSignal }): Promise<ExecResult>;
17
+ }
package/types/fs.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ /** `$dsh/fs` — the workspace, under the session's own access mode. See `types/chat.d.ts`. */
2
+ declare module "$dsh/fs" {
3
+ /** The file's text. Rejects when it does not exist or the session may not read it. */
4
+ export function readFile(path: string): Promise<string>;
5
+ /** One directory entry. Treat `size` as optional — draw nothing rather than `0 B` when it is missing. */
6
+ export type DirEntry = { name: string; type?: "file" | "directory"; size?: number };
7
+ /** A directory's entries — enough to draw a tree without probing each name. */
8
+ export function readdir(path: string): Promise<DirEntry[]>;
9
+ /**
10
+ * The file's raw bytes — for audio, MIDI, images, anything not text.
11
+ *
12
+ * `readFile` decodes as UTF-8 and would corrupt them silently. Capped at 8MB.
13
+ */
14
+ export function readBytes(path: string): Promise<Uint8Array<ArrayBuffer>>;
15
+ /** Rejects with `FS_SANDBOX_DENIED` when the session is read-only. */
16
+ export function writeFile(path: string, content: string): Promise<void>;
17
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "imports": {
3
+ "$dsh/chat": "./chat.d.ts",
4
+ "$dsh/ai": "./ai.d.ts",
5
+ "$dsh/fs": "./fs.d.ts",
6
+ "$dsh/exec": "./exec.d.ts",
7
+ "$dsh/state": "./state.d.ts",
8
+ "$dsh/web": "./web.d.ts"
9
+ }
10
+ }
@@ -0,0 +1,7 @@
1
+ // Generated by scripts/gen-standalone.ts — do not edit.
2
+ // Stands in for $dsh/ai outside dsh, where the harness these forward to does not exist.
3
+ export function streamText(...args) {
4
+ console.warn("[$dsh/ai] streamText() did nothing: this page is not running inside dsh, so there is no harness to reach.", ...args);
5
+ return (async function* () {})();
6
+ }
7
+ export default { streamText };
@@ -0,0 +1,6 @@
1
+ // Generated by scripts/gen-standalone.ts — do not edit.
2
+ // Stands in for $dsh/chat outside dsh, where the harness these forward to does not exist.
3
+ export function sendMessage(...args) {
4
+ console.warn("[$dsh/chat] sendMessage() did nothing: this page is not running inside dsh, so there is no harness to reach.", ...args);
5
+ }
6
+ export default { sendMessage };
@@ -0,0 +1,7 @@
1
+ // Generated by scripts/gen-standalone.ts — do not edit.
2
+ // Stands in for $dsh/exec outside dsh, where the harness these forward to does not exist.
3
+ export async function bash(...args) {
4
+ console.warn("[$dsh/exec] bash() did nothing: this page is not running inside dsh, so there is no harness to reach.", ...args);
5
+ return { stdout: "", stderr: "", exitCode: 0, truncated: { stdout: false, stderr: false }, timedOut: false };
6
+ }
7
+ export default { bash };
@@ -0,0 +1,18 @@
1
+ // Generated by scripts/gen-standalone.ts — do not edit.
2
+ // Stands in for $dsh/fs outside dsh, where the harness these forward to does not exist.
3
+ export async function readFile(...args) {
4
+ console.warn("[$dsh/fs] readFile() did nothing: this page is not running inside dsh, so there is no harness to reach.", ...args);
5
+ return "";
6
+ }
7
+ export async function readdir(...args) {
8
+ console.warn("[$dsh/fs] readdir() did nothing: this page is not running inside dsh, so there is no harness to reach.", ...args);
9
+ return [];
10
+ }
11
+ export async function readBytes(...args) {
12
+ console.warn("[$dsh/fs] readBytes() did nothing: this page is not running inside dsh, so there is no harness to reach.", ...args);
13
+ return new Uint8Array();
14
+ }
15
+ export async function writeFile(...args) {
16
+ console.warn("[$dsh/fs] writeFile() did nothing: this page is not running inside dsh, so there is no harness to reach.", ...args);
17
+ }
18
+ export default { readFile, readdir, readBytes, writeFile };
@@ -0,0 +1,10 @@
1
+ {
2
+ "imports": {
3
+ "$dsh/chat": "./chat.js",
4
+ "$dsh/ai": "./ai.js",
5
+ "$dsh/fs": "./fs.js",
6
+ "$dsh/exec": "./exec.js",
7
+ "$dsh/web": "./web.js",
8
+ "$dsh/state": "./state.js"
9
+ }
10
+ }
@@ -0,0 +1,24 @@
1
+ // src/client/runtime/state.ts
2
+ import * as React from "react";
3
+ var scope = (key) => `dsh-genui:${key}`;
4
+ function read(key, initial) {
5
+ const fallback = () => typeof initial === "function" ? initial() : initial;
6
+ try {
7
+ const raw = globalThis.localStorage?.getItem(scope(key));
8
+ return raw === null || raw === undefined ? fallback() : JSON.parse(raw);
9
+ } catch {
10
+ return fallback();
11
+ }
12
+ }
13
+ function usePersistedState(key, initial) {
14
+ const [value, setValue] = React.useState(() => read(key, initial));
15
+ React.useEffect(() => {
16
+ try {
17
+ globalThis.localStorage?.setItem(scope(key), JSON.stringify(value));
18
+ } catch {}
19
+ }, [key, value]);
20
+ return [value, setValue];
21
+ }
22
+ export {
23
+ usePersistedState
24
+ };
@@ -0,0 +1,7 @@
1
+ // Generated by scripts/gen-standalone.ts — do not edit.
2
+ // Stands in for $dsh/web outside dsh, where the harness these forward to does not exist.
3
+ export async function search(...args) {
4
+ console.warn("[$dsh/web] search() did nothing: this page is not running inside dsh, so there is no harness to reach.", ...args);
5
+ return { sources: [], truncated: false };
6
+ }
7
+ export default { search };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `$dsh/state` — state that survives a remount.
3
+ *
4
+ * Hand-written rather than emitted, like the rest of `types/*.d.ts`; `types/check.ts` asserts it
5
+ * stays assignable to what `bind()` returns.
6
+ *
7
+ * This is the one capability with nothing behind it — no host call, no network. It exists because
8
+ * three independent runs of a habit-tracker prompt imported exactly this name from exactly this
9
+ * module without being told it was there, and an unresolvable specifier renders a blank card.
10
+ */
11
+ declare module "$dsh/state" {
12
+ import type { Dispatch, SetStateAction } from "react";
13
+
14
+ /**
15
+ * `useState`, except the value is written to `localStorage` under a namespaced key and read
16
+ * back on mount.
17
+ *
18
+ * Reach for it whenever losing the value would be a bug: a canvas is remounted by every
19
+ * revision you make to it, and an inline card by any transcript re-render. Both are
20
+ * indistinguishable from a reload, and neither is rare.
21
+ *
22
+ * `initial` may be a value or a lazy initialiser, exactly as in `useState`.
23
+ */
24
+ export function usePersistedState<T>(key: string, initial: T | (() => T)): [T, Dispatch<SetStateAction<T>>];
25
+ }
package/types/web.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ /** `$dsh/web` — one web search, through whichever provider the host composed. */
2
+ declare module "$dsh/web" {
3
+ /** One citeable result. Only `url` is guaranteed: not every provider returns the rest. */
4
+ export type SearchSource = {
5
+ url: string;
6
+ title?: string;
7
+ snippet?: string;
8
+ /** Publication or crawl time, as the provider's own ISO-8601 string. */
9
+ publishedAt?: string;
10
+ };
11
+ /** What one search returns. */
12
+ export type SearchResult = {
13
+ /** A generated answer or summary, when the provider makes one — DeepSeek and Exa do not. */
14
+ content?: string;
15
+ sources: readonly SearchSource[];
16
+ /** True when the result set was cut down to `maxResults`. */
17
+ truncated: boolean;
18
+ };
19
+ /**
20
+ * Runs one search and resolves with its sources.
21
+ *
22
+ * **Search only — there is no `fetch` here.** A card cannot retrieve a page body; render the
23
+ * snippet and link to the source instead. Show the sources you used: a card that states a fact
24
+ * from the web without the link it came from cannot be checked, and this is the one capability
25
+ * whose output the reader has no other way to verify.
26
+ *
27
+ * Pass a `signal` when searching as the reader types. An aborted call REJECTS with an
28
+ * `AbortError`, which is not a failure — ignore it (`if (e.name === "AbortError") return`).
29
+ */
30
+ export function search(query: string, options?: { maxResults?: number; signal?: AbortSignal }): Promise<SearchResult>;
31
+ }
package/index.js DELETED
@@ -1 +0,0 @@
1
- // Placeholder