@close-by/clay 0.2.8 → 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 +1 -1
- package/SKILL.md +137 -0
- package/docs/README.md +1 -1
- package/docs/SKILL.md +137 -0
- package/docs/elements.md +13 -1
- package/docs/examples.md +3 -1
- package/docs/llms.txt +1 -0
- package/docs/ops-patterns.md +43 -0
- package/package.json +9 -6
- package/src/index.ts +120 -0
package/README.md
CHANGED
|
@@ -18,4 +18,4 @@ export default function () {
|
|
|
18
18
|
bunx clay hello.ts
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Docs: shipped with the package at `node_modules/@close-by/clay/docs/`. See [getting-started.md](docs/getting-started.md) after install.
|
|
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
CHANGED
|
@@ -4,7 +4,7 @@ Server-driven UI for TypeScript, inspired by [NiceGUI](https://nicegui.io/).
|
|
|
4
4
|
|
|
5
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
6
|
|
|
7
|
-
For coding agents: the repo-root [llms.txt](../llms.txt) is a short map of these guides.
|
|
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
8
|
|
|
9
9
|
## Guides
|
|
10
10
|
|
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/elements.md
CHANGED
|
@@ -21,6 +21,18 @@ Each factory creates an `Element` with a wire `type` string. The React client ma
|
|
|
21
21
|
| `combobox` | `combobox` | ShadCN `Combobox` (searchable select; optional `error`) |
|
|
22
22
|
| `date` | `date` | Calendar + Popover date picker (ISO `YYYY-MM-DD`; optional `error`) |
|
|
23
23
|
| `dateRange` | `dateRange` | Range picker with presets (ISO `from` / `to`) |
|
|
24
|
+
| `buttonGroup` | `buttonGroup` | Grouped buttons / segmented controls |
|
|
25
|
+
| `empty` | `empty` | Centered empty state (title, description, icon, footer slot) |
|
|
26
|
+
| `pagination` | `pagination` | Standalone pager (outside `dataTable`) |
|
|
27
|
+
| `filterBar` | `filterBar` | Ops filter row: chips + control slot |
|
|
28
|
+
| `filterChips` | `filterChips` | Removable filter chips only |
|
|
29
|
+
| `numberField` | `numberField` | Numeric input with +/- steppers |
|
|
30
|
+
| `phoneInput` | `phoneInput` | Country dial + local number |
|
|
31
|
+
| `field` | `field` | Label + description + error wrapper for one control |
|
|
32
|
+
| `nativeSelect` | `nativeSelect` | Styled native `<select>` |
|
|
33
|
+
| `navigationMenu` | `navigationmenu` (+ sub/link children) | Top nav mega-menu |
|
|
34
|
+
| `notice` | `notice` | Dismissible app banner |
|
|
35
|
+
| `eventCalendar` | `eventCalendar` | Month grid + day event list (`events` API → wire `items`) |
|
|
24
36
|
| `timeline` | `timeline` | Vertical or horizontal event timeline |
|
|
25
37
|
| `stepper` | `stepper` (+ `stepperStep`) | Inline multi-step flow with panels |
|
|
26
38
|
| `slider` | `slider` | ShadCN `Slider` (+ optional `error`) |
|
|
@@ -80,7 +92,7 @@ Each factory creates an `Element` with a wire `type` string. The React client ma
|
|
|
80
92
|
| `image` | `image` | `<img>` |
|
|
81
93
|
| `iframe` | `iframe` | `<iframe>` embed |
|
|
82
94
|
| `upload` | `upload` | File picker / dropzone → `POST /upload` (progress/abort) → WS `upload` / `progress` / `error` / `abort` |
|
|
83
|
-
| `stat` | `stat` | Grid of metric cards |
|
|
95
|
+
| `stat` | `stat` | Grid of metric cards (optional per-item `sparkline`) |
|
|
84
96
|
| `sparkline` | `sparkline` | KPI card with inline area/line mini chart (`headline`, `value`/`trend` shorthand) |
|
|
85
97
|
| `areaChart` | `areachart` | Recharts stacked area (+ legend, optional interactive periods, `headline`, `loading`, `stacked?`) |
|
|
86
98
|
| `barChart` | `barchart` | Recharts bar (`stacked?`, `layout?: 'vertical' \| 'horizontal'`, shared cartesian chrome) |
|
package/docs/examples.md
CHANGED
|
@@ -17,7 +17,9 @@ After `bun run build:client && bun run demo` (or `bun run demo:cli`), open http:
|
|
|
17
17
|
| `/examples/upload` | `FileUpload.ts` | `ui.upload` button + dropzone (progress/abort/size), `ui.storage.tab` / `user`, `ui.download`, `ui.clipboard` |
|
|
18
18
|
| `/examples/dashboard` | `Dashboard.ts` | `stat`, `areaChart`, full-chrome `dataTable` (views, editors, detail drawer) |
|
|
19
19
|
| `/examples/datatable` | `DataTableDemo.ts` | Multi-sort (Shift+click / badges), footer aggregates + a11y hints, column pin, remote filter/sort + pagination; density/zebra, editors (Enter/Esc + focus restore), resize, virtualization (≥40, coexists with reorder via windowed drop targets); facets, bulk, collapse/expand, loading/empty; `ui.table` sugar |
|
|
20
|
-
| `/examples/
|
|
20
|
+
| `/examples/composed` | `ComposedDemo.ts` | ReUI batch 1: `ui.timeline`, `ui.stepper`, `ui.dateRange`, `ui.feedList`/`ui.feedRow`, `ui.sparkline` |
|
|
21
|
+
| `/examples/reui-batches` | `ReuiBatchesDemo.ts` | ReUI batches 3–6: `buttonGroup`, `empty`, `pagination`, `filterBar`, form inputs, `navigationMenu`, `notice`, `stat`+sparkline, `eventCalendar`, datatable `showFilterChips` |
|
|
22
|
+
| `/examples/charts` | `ChartDemo.ts` | `ui.chart.*`, `ui.sparkline`, composed/scatter charts |
|
|
21
23
|
| `/examples/slider-demo` | `SliderDemo.ts` | Slider, checkbox, select + bindings |
|
|
22
24
|
| `/examples/feedback` | `FeedbackDemo.ts` | Alerts, progress, timer, `ui.theme`, `ui.runJavaScript` / `ui.scroll` |
|
|
23
25
|
| `/examples/form-demo` | `FormDemo.ts` | Form + validate + `ui.draft` + rating / colorPicker / tags |
|
package/docs/llms.txt
CHANGED
|
@@ -12,6 +12,7 @@ Notes for agents:
|
|
|
12
12
|
- Never use `window` / `navigator` in page code — use `ui.clipboard`, `ui.setUrlHash`, `ui.openExternal`.
|
|
13
13
|
- Runtime is Bun. In this repo: `bun run build:client`, then `bun run demo` (http://localhost:4000) or `bun run clay hello.ts`.
|
|
14
14
|
- `ui.ai.*` is visual primitives only — no model or streaming runtime.
|
|
15
|
+
- Agent skill (Cursor / compatible tools): `@close-by/clay/SKILL.md` — copy to `.cursor/skills/clay/SKILL.md` or read from `node_modules` when installed.
|
|
15
16
|
|
|
16
17
|
## Docs
|
|
17
18
|
|
package/docs/ops-patterns.md
CHANGED
|
@@ -111,6 +111,49 @@ ui.auto(() => {
|
|
|
111
111
|
|
|
112
112
|
Manual `ui.row` composition still works when you need a custom layout.
|
|
113
113
|
|
|
114
|
+
## Filter toolbar
|
|
115
|
+
|
|
116
|
+
Use **`ui.filterBar`** for the chip row + control slot pattern (Orders-style pages):
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
ui.auto(() => {
|
|
120
|
+
ui.filterBar(
|
|
121
|
+
{
|
|
122
|
+
chips: [
|
|
123
|
+
{ id: 'status', label: 'Status', value: live.statusFilter },
|
|
124
|
+
{ id: 'q', label: 'Search', value: live.query },
|
|
125
|
+
],
|
|
126
|
+
onRemoveChip: (id) => {
|
|
127
|
+
if (id === 'status') live.statusFilter = '';
|
|
128
|
+
if (id === 'q') live.query = '';
|
|
129
|
+
},
|
|
130
|
+
onClear: () => {
|
|
131
|
+
live.statusFilter = '';
|
|
132
|
+
live.query = '';
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
() => {
|
|
136
|
+
ui.select({
|
|
137
|
+
options: statusOptions,
|
|
138
|
+
value: live.statusFilter,
|
|
139
|
+
onChange: (v) => {
|
|
140
|
+
live.statusFilter = v;
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
ui.input({
|
|
144
|
+
value: live.query,
|
|
145
|
+
placeholder: 'Search…',
|
|
146
|
+
onInput: (v) => {
|
|
147
|
+
live.query = v;
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
For **`ui.dataTable`**, pass `showFilterChips: true` to surface active search + column filters as removable chips in the table chrome (`views` remain the saved-view tabs API).
|
|
156
|
+
|
|
114
157
|
## Logs / traces — sensitive bodies
|
|
115
158
|
|
|
116
159
|
Structured logs often contain API keys or partner tokens. Scrubbing is the **app’s** job; Clay can still reduce shoulder-surfing:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@close-by/clay",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "NiceGUI-like ui.* facade for Clay apps",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -9,11 +9,14 @@
|
|
|
9
9
|
".": "./src/index.ts",
|
|
10
10
|
"./page-tsconfig": "./page-tsconfig.json",
|
|
11
11
|
"./docs-dir": "./src/docs-dir.ts",
|
|
12
|
-
"./docs/*": "./docs/*"
|
|
12
|
+
"./docs/*": "./docs/*",
|
|
13
|
+
"./skill": "./SKILL.md",
|
|
14
|
+
"./SKILL.md": "./SKILL.md"
|
|
13
15
|
},
|
|
14
16
|
"files": [
|
|
15
17
|
"src",
|
|
16
18
|
"docs",
|
|
19
|
+
"SKILL.md",
|
|
17
20
|
"page-tsconfig.json",
|
|
18
21
|
"README.md",
|
|
19
22
|
"LICENSE",
|
|
@@ -34,10 +37,10 @@
|
|
|
34
37
|
"access": "public"
|
|
35
38
|
},
|
|
36
39
|
"dependencies": {
|
|
37
|
-
"@close-by/clay-compiler": "0.2.
|
|
38
|
-
"@close-by/clay-core": "0.2.
|
|
39
|
-
"@close-by/clay-components": "0.2.
|
|
40
|
-
"@close-by/clay-server": "0.2.
|
|
40
|
+
"@close-by/clay-compiler": "0.2.9",
|
|
41
|
+
"@close-by/clay-core": "0.2.9",
|
|
42
|
+
"@close-by/clay-components": "0.2.9",
|
|
43
|
+
"@close-by/clay-server": "0.2.9",
|
|
41
44
|
"@tailwindcss/cli": "^4.3.3",
|
|
42
45
|
"tailwindcss": "^4.3.3"
|
|
43
46
|
}
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,19 @@ import {
|
|
|
29
29
|
statusDot as statusDotFactory,
|
|
30
30
|
feedRow as feedRowFactory,
|
|
31
31
|
feedList as feedListFactory,
|
|
32
|
+
buttonGroup as buttonGroupFactory,
|
|
33
|
+
empty as emptyFactory,
|
|
34
|
+
pagination as paginationFactory,
|
|
35
|
+
filterBar as filterBarFactory,
|
|
36
|
+
filterChips as filterChipsFactory,
|
|
37
|
+
numberField as numberFieldFactory,
|
|
38
|
+
phoneInput as phoneInputFactory,
|
|
39
|
+
field as fieldFactory,
|
|
40
|
+
nativeSelect as nativeSelectFactory,
|
|
41
|
+
navigationMenu as navigationMenuFactory,
|
|
42
|
+
NavigationMenuElement,
|
|
43
|
+
notice as noticeFactory,
|
|
44
|
+
eventCalendar as eventCalendarFactory,
|
|
32
45
|
stat as statFactory,
|
|
33
46
|
dataTable as dataTableFactory,
|
|
34
47
|
areaChart as areaChartFactory,
|
|
@@ -134,6 +147,25 @@ import {
|
|
|
134
147
|
type FeedRowProps,
|
|
135
148
|
type FeedRowStatus,
|
|
136
149
|
type FeedListProps,
|
|
150
|
+
type ButtonGroupProps,
|
|
151
|
+
type ButtonGroupOrientation,
|
|
152
|
+
type EmptyProps,
|
|
153
|
+
type PaginationProps,
|
|
154
|
+
type FilterBarProps,
|
|
155
|
+
type FilterChip,
|
|
156
|
+
type NumberFieldProps,
|
|
157
|
+
type PhoneInputProps,
|
|
158
|
+
type PhoneCountry,
|
|
159
|
+
type FieldProps,
|
|
160
|
+
type FieldOrientation,
|
|
161
|
+
type NativeSelectProps,
|
|
162
|
+
type NativeSelectOption,
|
|
163
|
+
type NavigationMenuProps,
|
|
164
|
+
type NavigationMenuLinkItem,
|
|
165
|
+
type NoticeProps,
|
|
166
|
+
type NoticeVariant,
|
|
167
|
+
type EventCalendarProps,
|
|
168
|
+
type EventCalendarEvent,
|
|
137
169
|
type StatItem,
|
|
138
170
|
type DataTableProps,
|
|
139
171
|
type DataTableAction,
|
|
@@ -341,6 +373,7 @@ import {
|
|
|
341
373
|
DialogElement,
|
|
342
374
|
DialogStackElement,
|
|
343
375
|
StepperElement,
|
|
376
|
+
NavigationMenuElement,
|
|
344
377
|
AlertDialogElement,
|
|
345
378
|
DropdownMenuElement,
|
|
346
379
|
ContextMenuElement,
|
|
@@ -632,6 +665,7 @@ export {
|
|
|
632
665
|
DialogElement,
|
|
633
666
|
DialogStackElement,
|
|
634
667
|
StepperElement,
|
|
668
|
+
NavigationMenuElement,
|
|
635
669
|
AlertDialogElement,
|
|
636
670
|
DropdownMenuElement,
|
|
637
671
|
SheetElement,
|
|
@@ -864,6 +898,80 @@ export function feedRow(props: FeedRowProps, footer?: () => void): Element {
|
|
|
864
898
|
return feedRowFactory(props, footer);
|
|
865
899
|
}
|
|
866
900
|
|
|
901
|
+
/** Visually grouped buttons / controls. See {@link ButtonGroupProps}. */
|
|
902
|
+
export function buttonGroup(fn: () => void, props?: ButtonGroupProps): Element;
|
|
903
|
+
export function buttonGroup(props: ButtonGroupProps, fn: () => void): Element;
|
|
904
|
+
export function buttonGroup(a: any, b?: any): Element {
|
|
905
|
+
return buttonGroupFactory(a, b);
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/** Centered empty state. See {@link EmptyProps}. */
|
|
909
|
+
export function empty(props: EmptyProps, footer?: () => void): Element {
|
|
910
|
+
return emptyFactory(props, footer);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/** Standalone pager. See {@link PaginationProps}. */
|
|
914
|
+
export function pagination(props?: PaginationProps): Element {
|
|
915
|
+
return paginationFactory(props);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/** Ops filter toolbar with chips + control slot. See {@link FilterBarProps}. */
|
|
919
|
+
export function filterBar(fn: () => void, props?: FilterBarProps): Element;
|
|
920
|
+
export function filterBar(props: FilterBarProps, fn: () => void): Element;
|
|
921
|
+
export function filterBar(a: any, b?: any): Element {
|
|
922
|
+
return filterBarFactory(a, b);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/** Removable filter chips row. */
|
|
926
|
+
export function filterChips(props: Parameters<typeof filterChipsFactory>[0]): Element {
|
|
927
|
+
return filterChipsFactory(props);
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/** Numeric input with steppers. See {@link NumberFieldProps}. */
|
|
931
|
+
export function numberField(props?: NumberFieldProps): Element {
|
|
932
|
+
return numberFieldFactory(props);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
/** Country dial code + phone number. See {@link PhoneInputProps}. */
|
|
936
|
+
export function phoneInput(props?: PhoneInputProps): Element {
|
|
937
|
+
return phoneInputFactory(props);
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/** Label + description + error field wrapper. See {@link FieldProps}. */
|
|
941
|
+
export function field(fn: () => void, props?: FieldProps): Element;
|
|
942
|
+
export function field(props: FieldProps, fn: () => void): Element;
|
|
943
|
+
export function field(a: any, b?: any): Element {
|
|
944
|
+
return fieldFactory(a, b);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/** Styled native `<select>`. See {@link NativeSelectProps}. */
|
|
948
|
+
export function nativeSelect(props: NativeSelectProps): Element {
|
|
949
|
+
return nativeSelectFactory(props);
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/** Top navigation mega-menu. See {@link NavigationMenuProps}. */
|
|
953
|
+
export function navigationMenu(
|
|
954
|
+
fn: (menu: NavigationMenuElement) => void,
|
|
955
|
+
props?: NavigationMenuProps,
|
|
956
|
+
): NavigationMenuElement;
|
|
957
|
+
export function navigationMenu(
|
|
958
|
+
props: NavigationMenuProps,
|
|
959
|
+
fn: (menu: NavigationMenuElement) => void,
|
|
960
|
+
): NavigationMenuElement;
|
|
961
|
+
export function navigationMenu(a: any, b?: any): NavigationMenuElement {
|
|
962
|
+
return navigationMenuFactory(a, b);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/** Dismissible app banner. See {@link NoticeProps}. */
|
|
966
|
+
export function notice(message?: string | (() => string), props?: Omit<NoticeProps, 'message'>): Element {
|
|
967
|
+
return noticeFactory(message, props);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
/** Month calendar with day events. See {@link EventCalendarProps}. */
|
|
971
|
+
export function eventCalendar(props?: EventCalendarProps): Element {
|
|
972
|
+
return eventCalendarFactory(props);
|
|
973
|
+
}
|
|
974
|
+
|
|
867
975
|
/** Render markdown to HTML. Prefer `ui.markdown`. See {@link MarkdownProps}. */
|
|
868
976
|
export function markdown(text?: string, props?: MarkdownProps): Element {
|
|
869
977
|
return markdownFactory(text, props);
|
|
@@ -1621,6 +1729,18 @@ export const ui = {
|
|
|
1621
1729
|
statusDot,
|
|
1622
1730
|
feedRow,
|
|
1623
1731
|
feedList,
|
|
1732
|
+
buttonGroup,
|
|
1733
|
+
empty,
|
|
1734
|
+
pagination,
|
|
1735
|
+
filterBar,
|
|
1736
|
+
filterChips,
|
|
1737
|
+
numberField,
|
|
1738
|
+
phoneInput,
|
|
1739
|
+
field,
|
|
1740
|
+
nativeSelect,
|
|
1741
|
+
navigationMenu,
|
|
1742
|
+
notice,
|
|
1743
|
+
eventCalendar,
|
|
1624
1744
|
markdown,
|
|
1625
1745
|
html,
|
|
1626
1746
|
image,
|