@close-by/clay 0.2.7 → 0.2.9

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/README.md CHANGED
@@ -18,4 +18,4 @@ export default function () {
18
18
  bunx clay hello.ts
19
19
  ```
20
20
 
21
- Docs: [Getting started](https://github.com/closeby-corp/clay/blob/main/docs/getting-started.md).
21
+ Docs: shipped with the package at `node_modules/@close-by/clay/docs/`. Agent skill: `node_modules/@close-by/clay/SKILL.md` (or `@close-by/clay/skill`). See [getting-started.md](docs/getting-started.md) after install.
package/SKILL.md ADDED
@@ -0,0 +1,137 @@
1
+ ---
2
+ name: clay
3
+ description: >-
4
+ Build Clay server-driven UI apps with ui.* on Bun. Use when authoring Clay
5
+ pages, demos, or ops consoles; importing @close-by/clay; running clay CLI;
6
+ ui.page/ui.run/ui.state/ui.auto; or when the user asks about Clay, NiceGUI-style
7
+ TypeScript UI, or server-owned element trees.
8
+ ---
9
+
10
+ # Clay
11
+
12
+ Server-driven UI for TypeScript. Write imperative `ui.*` on the server; Clay owns a per-tab element tree and syncs it to a React + ShadCN client over WebSocket.
13
+
14
+ **Clay is not React.** Do not write React components, JSX, or client-side screen logic for app pages. The React client is a renderer only.
15
+
16
+ ## Install and run
17
+
18
+ ```bash
19
+ bun add @close-by/clay-cli @close-by/clay
20
+ ```
21
+
22
+ ```typescript
23
+ // hello.ts
24
+ import { ui } from '@close-by/clay';
25
+
26
+ export default function () {
27
+ ui.label('Hello Clay');
28
+ ui.button('Ping', { onClick: () => ui.notify('hi', 'success') });
29
+ }
30
+ ```
31
+
32
+ ```bash
33
+ bunx clay hello.ts # http://localhost:3000
34
+ ```
35
+
36
+ **Monorepo (this repo):** `bun run build:client` before `bun run demo` or `bun run clay …` — new wire types require a rebuilt client bundle.
37
+
38
+ ## Mental model
39
+
40
+ 1. `ui.page(path, fn)` or `ui.run(fn)` builds an **element tree** on the server.
41
+ 2. Each browser tab = one **WebSocket session**; page code runs once per tab.
42
+ 3. User events → server handlers → **patches** (`updateProps`, `setChildren`) → client re-render.
43
+ 4. Local `let` / session state is **per tab**, not shared. Use `ui.storage.app` for process-wide data.
44
+
45
+ ## Reactive UI (preferred)
46
+
47
+ Keep mutable state **outside** rebuild blocks:
48
+
49
+ ```typescript
50
+ const s = ui.state({ count: 0, items: [] as string[] });
51
+
52
+ ui.auto(() => {
53
+ ui.label(`Count: ${s.count}`);
54
+ ui.button('+', { onClick: () => { s.count++; } });
55
+ for (const item of s.items) ui.label(item);
56
+ });
57
+
58
+ // Or bind one label without a full block:
59
+ ui.label(() => `Count: ${s.count}`);
60
+ ```
61
+
62
+ | API | Use when |
63
+ |-----|----------|
64
+ | `ui.state(initial)` | Reactive object; writes trigger tracked rebuilds |
65
+ | `ui.auto(fn)` | Rebuild a UI block when tracked state changes |
66
+ | `ui.label(() => …)` / `bindText` | Patch text only (stable tree) |
67
+ | `bindValue` | Two-way inputs |
68
+ | `ui.refreshable` + `.refresh()` | Simple one-shot structural rebuilds |
69
+ | `ui.timer(ms, fn)` | Poll / clock ticks inside reactive pages |
70
+
71
+ **Inside `ui.auto`:** child tree shape (types + counts) should stay stable when possible — then updates use `updateProps` instead of remounting. For toggling optional `className`, use `undefined` to clear (Clay sends `null` patches).
72
+
73
+ **Button groups:** use `variant: 'outline'` on all segmented buttons; highlight selection with `className: active ? 'bg-accent' : undefined`.
74
+
75
+ ## Pages and layout
76
+
77
+ ```typescript
78
+ ui.page('/dashboard', () => {
79
+ ui.column({ gap: 4 }, () => {
80
+ ui.row({ gap: 2 }, () => {
81
+ ui.button('Save', { onClick: save });
82
+ });
83
+ ui.card({ title: 'Stats' }, () => {
84
+ ui.label('…');
85
+ });
86
+ });
87
+ });
88
+ ```
89
+
90
+ - Nest with callbacks: `ui.row(() => { … })`, `ui.column(() => { … })`.
91
+ - Tailwind: `ui.label('x').classes('text-sm text-muted-foreground')` or pass `className` in props.
92
+ - Toasts: `ui.notify('Saved', 'success')`.
93
+
94
+ ## Do not
95
+
96
+ - Import React, write JSX, or add client components for screens.
97
+ - Use `window`, `document`, `navigator` in page code — use `ui.clipboard`, `ui.setUrlHash`, `ui.openExternal`.
98
+ - Assume `let` is shared across tabs or users.
99
+ - Use `ui.ai.*` as an LLM runtime — it is visual primitives only.
100
+
101
+ ## Docs (shipped with package)
102
+
103
+ After install, read version-matched docs at `node_modules/@close-by/clay/docs/`:
104
+
105
+ | Doc | Purpose |
106
+ |-----|---------|
107
+ | `getting-started.md` | CLI, first app, publish |
108
+ | `concepts.md` | Sessions, patches, storage |
109
+ | `api.md` | Full `ui.*` reference |
110
+ | `elements.md` | Wire types → client mapping |
111
+ | `reactive-let.md` | `ui.state` / `ui.auto` / compile-time `let` |
112
+ | `ops-patterns.md` | Dense consoles, feeds, tables |
113
+ | `examples.md` | Demo route catalog |
114
+
115
+ Resolve docs programmatically: `resolveClayDocsDir()` from `@close-by/clay/docs-dir`.
116
+
117
+ ## Optional compile-time reactive `let`
118
+
119
+ Opt-in via `// @clay-reactive` + `clay app.ts --reactive-let`. Prefer explicit `ui.state` + `ui.auto` for production/async-heavy apps.
120
+
121
+ ## Package map (monorepo)
122
+
123
+ | npm | Role |
124
+ |-----|------|
125
+ | `@close-by/clay` | `ui.*` facade (this skill ships here) |
126
+ | `@close-by/clay-cli` | `clay` binary + bundled client |
127
+ | `@close-by/clay-core` | Element tree, sessions, patches |
128
+ | `@close-by/clay-components` | Wire element factories |
129
+ | `@close-by/clay-server` | HTTP + WebSocket server |
130
+
131
+ ## Agent checklist
132
+
133
+ 1. Read `api.md` / `elements.md` before adding unfamiliar wire types.
134
+ 2. Use `ui.state` + `ui.auto` for live data; avoid manual `setText` loops.
135
+ 3. Rebuild client in monorepo after new element types (`bun run build:client`).
136
+ 4. Match existing demo patterns under `apps/demo/src/examples/` when unsure.
137
+ 5. Run `bun test` in touched packages; keep diffs minimal.
package/docs/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # Clay Documentation
2
+
3
+ Server-driven UI for TypeScript, inspired by [NiceGUI](https://nicegui.io/).
4
+
5
+ You write imperative `ui.*` code on the server. Clay owns a per-client element tree and syncs it to a thin React + ShadCN client over WebSocket.
6
+
7
+ For coding agents: the repo-root [llms.txt](../llms.txt) is a short map of these guides. The [agent skill](./SKILL.md) ships with `@close-by/clay` at package root (`SKILL.md`).
8
+
9
+ ## Guides
10
+
11
+ | Doc | Description |
12
+ |-----|-------------|
13
+ | [Getting started](./getting-started.md) | Install, `clay` CLI, create a page |
14
+ | [Boot: CLI vs library](./boot.md) | Production `ui.run` path, `clientDir`, `_run.ts`, reload stubs |
15
+ | [Tailwind](./tailwind.md) | Zero-config utilities from page modules; optional tokens |
16
+ | [Sample app](./tutorial.md) | Step-by-step tasks list, then two pages + shell |
17
+ | [Concepts](./concepts.md) | Mental model: sessions, elements, patches; **canonical `state`/`auto`/`timer` recipe**; reactive `let` alternative |
18
+ | [Browser APIs](./browser-apis.md) | Prefer `ui.clipboard` / hash / `openExternal` — never `window` in page code |
19
+ | [Production & security](./production.md) | Auth, server exposure, rate limits, boot parity |
20
+ | [Ops patterns](./ops-patterns.md) | Dense consoles: chips, master–detail, feeds, sensitive logs, auth nav |
21
+ | [API reference](./api.md) | `ui.*`, `Element` methods, `reactive`, helpers |
22
+ | [Elements](./elements.md) | All element types and props |
23
+ | [WebSocket protocol](./protocol.md) | Client ↔ server message formats |
24
+ | [Architecture](./architecture.md) | Packages, data flow, and ownership |
25
+ | [Examples](./examples.md) | Demo apps and patterns |
26
+ | [Reactive `let`](./reactive-let.md) | NiceGUI-style `let` transform (opt-in `--reactive-let`) |
27
+ | [AI UI](./ai.md) | `ui.ai.*` visual AI primitives (no model runtime) |
28
+ | [DuckDB](./duckdb.md) | Multi-DB DuckDB wrapper (`@close-by/clay-duckdb`) |
29
+ | [Kibana](./kibana.md) | Kibana REST + ES search (`@close-by/clay-kibana`) |
30
+ | [ClickHouse](./clickhouse.md) | Multi-connection ClickHouse wrapper (`@close-by/clay-clickhouse`) |
31
+
32
+ ## Quick start
33
+
34
+ ```bash
35
+ bun install
36
+ bun run build:client # monorepo: build + copy client into @close-by/clay-cli
37
+ bun run clay hello.ts
38
+ # → http://localhost:3000
39
+ ```
40
+
41
+ Outside the monorepo: `bun run pack:publishable` then install the `dist-pack/*.tgz` set (see [Getting started](./getting-started.md)), or `bun add @close-by/clay-cli @close-by/clay` once published. Maintainers: `bun run publish:dry` / `publish:npm` (order core → … → cli). Shipped CLI includes prebuilt client assets — no separate `build:client` after install.
42
+
43
+ ```typescript
44
+ // hello.ts
45
+ import { ui } from '@close-by/clay';
46
+
47
+ ui.run(() => {
48
+ let count = 0;
49
+ const label = ui.label(`Count: ${count}`);
50
+
51
+ ui.row(() => {
52
+ ui.button('-', {
53
+ onClick: () => {
54
+ count--;
55
+ label.setText(`Count: ${count}`);
56
+ },
57
+ });
58
+ ui.button('+', {
59
+ onClick: () => {
60
+ count++;
61
+ label.setText(`Count: ${count}`);
62
+ },
63
+ });
64
+ });
65
+ });
66
+ ```
67
+
68
+ Multi-page with shell:
69
+
70
+ ```bash
71
+ bun run clay ./pages --app --title "My App"
72
+ ```
73
+
74
+ Or library-style entry (`loadPages` + `ui.run({ app })`) — see [Getting started](./getting-started.md).
75
+
76
+ ## Naming
77
+
78
+ Public Clay APIs and the wire protocol use **camelCase** (`onClick`, `bindValue`, `setText`, element `type: "button"`).
79
+
80
+ React/ShadCN components inside `@close-by/clay-client` stay PascalCase because React requires it — that layer is an implementation detail.
package/docs/SKILL.md ADDED
@@ -0,0 +1,137 @@
1
+ ---
2
+ name: clay
3
+ description: >-
4
+ Build Clay server-driven UI apps with ui.* on Bun. Use when authoring Clay
5
+ pages, demos, or ops consoles; importing @close-by/clay; running clay CLI;
6
+ ui.page/ui.run/ui.state/ui.auto; or when the user asks about Clay, NiceGUI-style
7
+ TypeScript UI, or server-owned element trees.
8
+ ---
9
+
10
+ # Clay
11
+
12
+ Server-driven UI for TypeScript. Write imperative `ui.*` on the server; Clay owns a per-tab element tree and syncs it to a React + ShadCN client over WebSocket.
13
+
14
+ **Clay is not React.** Do not write React components, JSX, or client-side screen logic for app pages. The React client is a renderer only.
15
+
16
+ ## Install and run
17
+
18
+ ```bash
19
+ bun add @close-by/clay-cli @close-by/clay
20
+ ```
21
+
22
+ ```typescript
23
+ // hello.ts
24
+ import { ui } from '@close-by/clay';
25
+
26
+ export default function () {
27
+ ui.label('Hello Clay');
28
+ ui.button('Ping', { onClick: () => ui.notify('hi', 'success') });
29
+ }
30
+ ```
31
+
32
+ ```bash
33
+ bunx clay hello.ts # http://localhost:3000
34
+ ```
35
+
36
+ **Monorepo (this repo):** `bun run build:client` before `bun run demo` or `bun run clay …` — new wire types require a rebuilt client bundle.
37
+
38
+ ## Mental model
39
+
40
+ 1. `ui.page(path, fn)` or `ui.run(fn)` builds an **element tree** on the server.
41
+ 2. Each browser tab = one **WebSocket session**; page code runs once per tab.
42
+ 3. User events → server handlers → **patches** (`updateProps`, `setChildren`) → client re-render.
43
+ 4. Local `let` / session state is **per tab**, not shared. Use `ui.storage.app` for process-wide data.
44
+
45
+ ## Reactive UI (preferred)
46
+
47
+ Keep mutable state **outside** rebuild blocks:
48
+
49
+ ```typescript
50
+ const s = ui.state({ count: 0, items: [] as string[] });
51
+
52
+ ui.auto(() => {
53
+ ui.label(`Count: ${s.count}`);
54
+ ui.button('+', { onClick: () => { s.count++; } });
55
+ for (const item of s.items) ui.label(item);
56
+ });
57
+
58
+ // Or bind one label without a full block:
59
+ ui.label(() => `Count: ${s.count}`);
60
+ ```
61
+
62
+ | API | Use when |
63
+ |-----|----------|
64
+ | `ui.state(initial)` | Reactive object; writes trigger tracked rebuilds |
65
+ | `ui.auto(fn)` | Rebuild a UI block when tracked state changes |
66
+ | `ui.label(() => …)` / `bindText` | Patch text only (stable tree) |
67
+ | `bindValue` | Two-way inputs |
68
+ | `ui.refreshable` + `.refresh()` | Simple one-shot structural rebuilds |
69
+ | `ui.timer(ms, fn)` | Poll / clock ticks inside reactive pages |
70
+
71
+ **Inside `ui.auto`:** child tree shape (types + counts) should stay stable when possible — then updates use `updateProps` instead of remounting. For toggling optional `className`, use `undefined` to clear (Clay sends `null` patches).
72
+
73
+ **Button groups:** use `variant: 'outline'` on all segmented buttons; highlight selection with `className: active ? 'bg-accent' : undefined`.
74
+
75
+ ## Pages and layout
76
+
77
+ ```typescript
78
+ ui.page('/dashboard', () => {
79
+ ui.column({ gap: 4 }, () => {
80
+ ui.row({ gap: 2 }, () => {
81
+ ui.button('Save', { onClick: save });
82
+ });
83
+ ui.card({ title: 'Stats' }, () => {
84
+ ui.label('…');
85
+ });
86
+ });
87
+ });
88
+ ```
89
+
90
+ - Nest with callbacks: `ui.row(() => { … })`, `ui.column(() => { … })`.
91
+ - Tailwind: `ui.label('x').classes('text-sm text-muted-foreground')` or pass `className` in props.
92
+ - Toasts: `ui.notify('Saved', 'success')`.
93
+
94
+ ## Do not
95
+
96
+ - Import React, write JSX, or add client components for screens.
97
+ - Use `window`, `document`, `navigator` in page code — use `ui.clipboard`, `ui.setUrlHash`, `ui.openExternal`.
98
+ - Assume `let` is shared across tabs or users.
99
+ - Use `ui.ai.*` as an LLM runtime — it is visual primitives only.
100
+
101
+ ## Docs (shipped with package)
102
+
103
+ After install, read version-matched docs at `node_modules/@close-by/clay/docs/`:
104
+
105
+ | Doc | Purpose |
106
+ |-----|---------|
107
+ | `getting-started.md` | CLI, first app, publish |
108
+ | `concepts.md` | Sessions, patches, storage |
109
+ | `api.md` | Full `ui.*` reference |
110
+ | `elements.md` | Wire types → client mapping |
111
+ | `reactive-let.md` | `ui.state` / `ui.auto` / compile-time `let` |
112
+ | `ops-patterns.md` | Dense consoles, feeds, tables |
113
+ | `examples.md` | Demo route catalog |
114
+
115
+ Resolve docs programmatically: `resolveClayDocsDir()` from `@close-by/clay/docs-dir`.
116
+
117
+ ## Optional compile-time reactive `let`
118
+
119
+ Opt-in via `// @clay-reactive` + `clay app.ts --reactive-let`. Prefer explicit `ui.state` + `ui.auto` for production/async-heavy apps.
120
+
121
+ ## Package map (monorepo)
122
+
123
+ | npm | Role |
124
+ |-----|------|
125
+ | `@close-by/clay` | `ui.*` facade (this skill ships here) |
126
+ | `@close-by/clay-cli` | `clay` binary + bundled client |
127
+ | `@close-by/clay-core` | Element tree, sessions, patches |
128
+ | `@close-by/clay-components` | Wire element factories |
129
+ | `@close-by/clay-server` | HTTP + WebSocket server |
130
+
131
+ ## Agent checklist
132
+
133
+ 1. Read `api.md` / `elements.md` before adding unfamiliar wire types.
134
+ 2. Use `ui.state` + `ui.auto` for live data; avoid manual `setText` loops.
135
+ 3. Rebuild client in monorepo after new element types (`bun run build:client`).
136
+ 4. Match existing demo patterns under `apps/demo/src/examples/` when unsure.
137
+ 5. Run `bun test` in touched packages; keep diffs minimal.
package/docs/ai.md ADDED
@@ -0,0 +1,65 @@
1
+ # AI UI (`ui.ai.*`)
2
+
3
+ Visual, data-driven primitives for AI-native interfaces. Callers pass props (messages, tools, status, text) and handle events (`onSubmit`, `onApprove`, …). **No AI SDK / model runtime** lives inside clay.
4
+
5
+ Inspired by [Beautiful UI](https://www.beautifului.dev/) patterns; reimplemented with clay factories + ShadCN/Tailwind on the client.
6
+
7
+ ## Quick start
8
+
9
+ ```ts
10
+ import { ui } from '@close-by/clay';
11
+
12
+ ui.ai.chat({
13
+ tabs: [{ id: 'flavors', label: 'Flavors' }],
14
+ activeTab: 'flavors',
15
+ messages: [
16
+ { id: '1', role: 'user', text: 'Compare mint chip…' },
17
+ {
18
+ id: '2',
19
+ role: 'assistant',
20
+ text: 'Mint chip is up 12%.',
21
+ thinking: [{ kind: 'reasoning', title: 'Comparison', durationMs: 2000 }],
22
+ },
23
+ ],
24
+ onSubmit: (text) => {
25
+ // App owns state; patch via chat.setMessages(...) or refreshable
26
+ },
27
+ });
28
+
29
+ ui.ai.promptBar({
30
+ placeholder: 'Ask…',
31
+ sources: [{ id: 'v1', label: 'Vanilla 1' }],
32
+ models: [{ id: 'gpt', label: 'GPT' }],
33
+ onSubmit: (value) => {},
34
+ });
35
+ ```
36
+
37
+ Demo gallery: `/examples/ai` (`AiDemo.ts`).
38
+
39
+ **State note:** `ui.ai.chat` is intentionally imperative (`var messages` + `chat.setMessages`). Surrounding controls can use `ui.state`; do not wrap the whole chat in `ui.auto` (remounts the composer). See [Ops patterns — ai.chat](./ops-patterns.md#uiaichat-and-state).
40
+
41
+ ## Wire types
42
+
43
+ | `ui.ai.*` | Wire `type` | Notes |
44
+ |-----------|-------------|--------|
45
+ | `loader` | `aiLoader` | Variants: `drive` / `dots` / `orbit` / `pixel`; optional `startedAt` elapsed |
46
+ | `thinking` | `aiThinking` | Expandable step traces (`steps` / `reasoning` / `search` / `coding`) |
47
+ | `message` | `aiMessage` | Role, markdown/text, sources, actions, follow-ups, `streaming?` |
48
+ | `chat` | `aiChat` | Tabs + messages + composer; `AiChatElement` owns `setMessages` / `setActiveTab` |
49
+ | `promptBar` | `aiPromptBar` | Textarea, @sources, /commands, model picker, dictate stub |
50
+ | `codeBlock` | `aiCodeBlock` | Filename + optional streaming cue (Shiki) |
51
+ | `approval` | `aiApproval` | HITL option cards |
52
+ | `toolChips` | `aiToolChips` | Compact tool / edit chips |
53
+ | `tasks` | `aiTasks` | Nested running / failed / completed rows |
54
+ | `recommendation` | `aiRecommendation` | Confidence + alternatives |
55
+ | `context` | `aiContext` | Retrieved chunks + sources |
56
+ | `diffTable` | `aiDiffTable` | Compact proposed-edit table (not DataTable) |
57
+ | `insights` | `aiInsights` | Paged insight cards |
58
+ | `selectionActions` | `aiSelectionActions` | Selection text + action strip |
59
+ | `fineTune` | `aiFineTune` | Inspector-style field editors → `onChange` |
60
+
61
+ **Skipped overlaps:** use `ui.dataTable` for records/filter tables, `ui.app` for sidebar nav, `ui.command` for search.
62
+
63
+ ## Events (common)
64
+
65
+ `submit`, `followUp`, `action`, `tabChange`, `sourceRemove`, `modelChange`, `command`, `dictate`, `approve`, `reject`, `chipClick`, `taskClick`, `accept`, `alternative`, `chunkClick`, `rowClick`, `indexChange`, `prompt`, `change`, `toggle`.