@close-by/clay 0.2.13 → 0.2.15
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/SKILL.md +2 -1
- package/docs/README.md +2 -1
- package/docs/SKILL.md +2 -1
- package/docs/api.md +19 -3
- package/docs/browser-apis.md +8 -1
- package/docs/elements.md +3 -3
- package/docs/llms.txt +1 -1
- package/docs/protocol.md +9 -1
- package/docs/url-search.md +97 -0
- package/package.json +5 -5
- package/src/index.ts +56 -0
package/SKILL.md
CHANGED
|
@@ -62,6 +62,7 @@ ui.label(() => `Count: ${s.count}`);
|
|
|
62
62
|
| API | Use when |
|
|
63
63
|
|-----|----------|
|
|
64
64
|
| `ui.state(initial)` | Reactive object; writes trigger tracked rebuilds |
|
|
65
|
+
| `ui.urlState(defaults, opts?)` | Like `state`, hydrated from / synced to the query string |
|
|
65
66
|
| `ui.auto(fn)` | Rebuild a UI block when tracked state changes |
|
|
66
67
|
| `ui.label(() => …)` / `bindText` | Patch text only (stable tree) |
|
|
67
68
|
| `bindValue` | Two-way inputs |
|
|
@@ -94,7 +95,7 @@ ui.page('/dashboard', () => {
|
|
|
94
95
|
## Do not
|
|
95
96
|
|
|
96
97
|
- 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
|
+
- Use `window`, `document`, `navigator` in page code — use `ui.clipboard`, `ui.setUrlHash`, `ui.getUrlSearch` / `ui.updateUrlSearch`, `ui.openExternal`.
|
|
98
99
|
- Assume `let` is shared across tabs or users.
|
|
99
100
|
- Use `ui.ai.*` as an LLM runtime — it is visual primitives only.
|
|
100
101
|
|
package/docs/README.md
CHANGED
|
@@ -15,7 +15,8 @@ For coding agents: the repo-root [llms.txt](../llms.txt) is a short map of these
|
|
|
15
15
|
| [Tailwind](./tailwind.md) | Zero-config utilities from page modules; optional tokens |
|
|
16
16
|
| [Sample app](./tutorial.md) | Step-by-step tasks list, then two pages + shell |
|
|
17
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 |
|
|
18
|
+
| [Browser APIs](./browser-apis.md) | Prefer `ui.clipboard` / hash / search / `openExternal` — never `window` in page code |
|
|
19
|
+
| [URL search](./url-search.md) | `ui.urlState` + query helpers for shareable filters |
|
|
19
20
|
| [Production & security](./production.md) | Auth, server exposure, rate limits, boot parity |
|
|
20
21
|
| [Ops patterns](./ops-patterns.md) | Dense consoles: chips, master–detail, feeds, sensitive logs, auth nav |
|
|
21
22
|
| [API reference](./api.md) | `ui.*`, `Element` methods, `reactive`, helpers |
|
package/docs/SKILL.md
CHANGED
|
@@ -62,6 +62,7 @@ ui.label(() => `Count: ${s.count}`);
|
|
|
62
62
|
| API | Use when |
|
|
63
63
|
|-----|----------|
|
|
64
64
|
| `ui.state(initial)` | Reactive object; writes trigger tracked rebuilds |
|
|
65
|
+
| `ui.urlState(defaults, opts?)` | Like `state`, hydrated from / synced to the query string |
|
|
65
66
|
| `ui.auto(fn)` | Rebuild a UI block when tracked state changes |
|
|
66
67
|
| `ui.label(() => …)` / `bindText` | Patch text only (stable tree) |
|
|
67
68
|
| `bindValue` | Two-way inputs |
|
|
@@ -94,7 +95,7 @@ ui.page('/dashboard', () => {
|
|
|
94
95
|
## Do not
|
|
95
96
|
|
|
96
97
|
- 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
|
+
- Use `window`, `document`, `navigator` in page code — use `ui.clipboard`, `ui.setUrlHash`, `ui.getUrlSearch` / `ui.updateUrlSearch`, `ui.openExternal`.
|
|
98
99
|
- Assume `let` is shared across tabs or users.
|
|
99
100
|
- Use `ui.ai.*` as an LLM runtime — it is visual primitives only.
|
|
100
101
|
|
package/docs/api.md
CHANGED
|
@@ -1052,7 +1052,7 @@ ui.stat([
|
|
|
1052
1052
|
|
|
1053
1053
|
#### `ui.areaChart(props)`
|
|
1054
1054
|
|
|
1055
|
-
Stacked area chart (Recharts). Optional card chrome via `title` / `description`. Set `interactive: true` with ISO date `xKey` values for client-side period tabs (default 7d / 30d / 90d; override with `periods`). Optional `headline: { value, trend?, trendDirection? }` for a KPI row above the chart; `loading: true` shows a skeleton. Legend is
|
|
1055
|
+
Stacked area chart (Recharts). Optional card chrome via `title` / `description`. Set `interactive: true` with ISO date `xKey` values for client-side period tabs (default 7d / 30d / 90d; override with `periods`). Optional `headline: { value, trend?, trendDirection? }` for a KPI row above the chart; `loading: true` shows a skeleton. Legend is shown by default; pass `showLegend: false` to hide. Pass `stacked: false` to overlay series instead of stacking (default `true`).
|
|
1056
1056
|
|
|
1057
1057
|
```typescript
|
|
1058
1058
|
ui.areaChart({
|
|
@@ -1071,7 +1071,7 @@ ui.areaChart({
|
|
|
1071
1071
|
|
|
1072
1072
|
#### `ui.barChart(props)`
|
|
1073
1073
|
|
|
1074
|
-
Bar chart with the same cartesian props as area (`data`, `xKey`, `series`, title/description/height/interactive, `headline`, `periods`, `loading`). Optional `stacked` (default `false`) and `layout: 'vertical' | 'horizontal'` (default `'vertical'` — category on X).
|
|
1074
|
+
Bar chart with the same cartesian props as area (`data`, `xKey`, `series`, title/description/height/interactive, `headline`, `periods`, `loading`, `showLegend?`). Optional `stacked` (default `false`) and `layout: 'vertical' | 'horizontal'` (default `'vertical'` — category on X).
|
|
1075
1075
|
|
|
1076
1076
|
```typescript
|
|
1077
1077
|
ui.barChart({
|
|
@@ -1089,11 +1089,22 @@ ui.barChart({
|
|
|
1089
1089
|
|
|
1090
1090
|
#### `ui.lineChart(props)`
|
|
1091
1091
|
|
|
1092
|
-
Line chart with the same cartesian props as area (including interactive date ranges, headline, periods, loading).
|
|
1092
|
+
Line chart with the same cartesian props as area (including interactive date ranges, headline, periods, loading, `showLegend?`). Line styling:
|
|
1093
|
+
|
|
1094
|
+
| Prop | Default | Description |
|
|
1095
|
+
|------|---------|-------------|
|
|
1096
|
+
| `curve` | `'monotone'` | Recharts interpolation: `'linear'`, `'natural'`, `'step'`, `'stepBefore'`, `'stepAfter'`, `'basis'` |
|
|
1097
|
+
| `strokeWidth` | `2` | Line thickness in px |
|
|
1098
|
+
| `dots` | `false` | Show point markers |
|
|
1099
|
+
| `strokeDasharray` / `dashed` | — | Dashed stroke; `dashed: true` → `'4 4'` |
|
|
1100
|
+
| `variant` | `'default'` | `'minimal'` hides the grid for a cleaner plot |
|
|
1093
1101
|
|
|
1094
1102
|
```typescript
|
|
1095
1103
|
ui.lineChart({
|
|
1096
1104
|
title: 'Traffic trend',
|
|
1105
|
+
showLegend: false,
|
|
1106
|
+
curve: 'step',
|
|
1107
|
+
dots: true,
|
|
1097
1108
|
data: monthly,
|
|
1098
1109
|
xKey: 'month',
|
|
1099
1110
|
series: [
|
|
@@ -1782,6 +1793,11 @@ onClick: async () => {
|
|
|
1782
1793
|
| `ui.clipboard(content)` | `void` | Write text to clipboard (prefer over `navigator.clipboard`) |
|
|
1783
1794
|
| `ui.getUrlHash()` | `string` | Current hash without `#` (from hello / last set) |
|
|
1784
1795
|
| `ui.setUrlHash(hash)` | `void` | Set `location.hash` on the client |
|
|
1796
|
+
| `ui.getUrlSearch()` | `string` | Current search without `?` (from hello / last set) |
|
|
1797
|
+
| `ui.getUrlSearchParams()` | `URLSearchParams` | Mutable parse of `getUrlSearch()` |
|
|
1798
|
+
| `ui.setUrlSearch(search, opts?)` | `void` | Set `location.search` (`opts.mode`: `replace` \| `push`) |
|
|
1799
|
+
| `ui.updateUrlSearch(patch, opts?)` | `void` | Merge query keys; `null`/`''` deletes |
|
|
1800
|
+
| `ui.urlState(defaults, opts?)` | reactive object | `ui.state` + hydrate/sync query string (prefer this) |
|
|
1785
1801
|
| `ui.openExternal(url)` | `void` | `window.open` in a new tab (`noopener`) |
|
|
1786
1802
|
| `ui.theme.set(mode)` | `void` | Push `light` \| `dark` \| `system` to this client |
|
|
1787
1803
|
| `ui.theme.get()` | `ThemeMode \| null` | Last value set on this session |
|
package/docs/browser-apis.md
CHANGED
|
@@ -8,6 +8,7 @@ Page builders and event handlers run on the **server** (Bun), not in the browser
|
|
|
8
8
|
|------|-----|
|
|
9
9
|
| Copy text | `ui.clipboard(text)` |
|
|
10
10
|
| Read / set URL hash | `ui.getUrlHash()` / `ui.setUrlHash(hash)` |
|
|
11
|
+
| Read / set URL search (query) | `ui.urlState(defaults)` (prefer) or `ui.getUrlSearch` / `ui.updateUrlSearch` |
|
|
11
12
|
| Open a URL in a new tab | `ui.openExternal(url)` |
|
|
12
13
|
| In-app route change | `ui.navigate(path)` |
|
|
13
14
|
| Scroll | `ui.scroll.to` / `ui.scroll.intoView` |
|
|
@@ -23,17 +24,23 @@ ui.button('Copy id', {
|
|
|
23
24
|
|
|
24
25
|
const focus = ui.getUrlHash(); // from last hello / setUrlHash
|
|
25
26
|
ui.setUrlHash(traceId);
|
|
27
|
+
|
|
28
|
+
const partner = ui.getUrlSearchParams().get('partner');
|
|
29
|
+
ui.updateUrlSearch({ partner: 'UBER', page: null }); // omit empty defaults
|
|
26
30
|
ui.openExternal('https://signoz.example/trace/' + traceId);
|
|
27
31
|
```
|
|
28
32
|
|
|
29
33
|
`getUrlHash` returns the hash **without** a leading `#`. It is hydrated from the client on each `hello` (connect, reconnect, SPA navigate) and updated when you call `setUrlHash`.
|
|
30
34
|
|
|
35
|
+
`getUrlSearch` returns the query string **without** a leading `?` (same hydration / write path). Prefer **`ui.urlState`** for filter objects; use `ui.updateUrlSearch` for one-off merges. See [URL search](./url-search.md).
|
|
36
|
+
|
|
31
37
|
## Don’t
|
|
32
38
|
|
|
33
39
|
```ts
|
|
34
40
|
// Wrong — server has no clipboard / location
|
|
35
41
|
await navigator.clipboard.writeText(value);
|
|
36
42
|
window.location.hash = traceId;
|
|
43
|
+
window.location.search = '?partner=UBER';
|
|
37
44
|
window.open(url, '_blank');
|
|
38
45
|
```
|
|
39
46
|
|
|
@@ -61,5 +68,5 @@ With **`clay --reactive-let`**, transformed files that import known-fragile CJS
|
|
|
61
68
|
## Related
|
|
62
69
|
|
|
63
70
|
- Helpers table: [API — dialogs and feedback](./api.md#dialogs-and-feedback)
|
|
64
|
-
- Protocol: `clipboard`, `setUrlHash`, `openExternal` in [WebSocket protocol](./protocol.md)
|
|
71
|
+
- Protocol: `clipboard`, `setUrlHash`, `setUrlSearch`, `openExternal` in [WebSocket protocol](./protocol.md)
|
|
65
72
|
- Phase 1 reactivity (preferred over DOM hacks): [reactive-let](./reactive-let.md)
|
package/docs/elements.md
CHANGED
|
@@ -102,9 +102,9 @@ Each factory creates an `Element` with a wire `type` string. The React client ma
|
|
|
102
102
|
| `upload` | `upload` | File picker / dropzone → `POST /upload` (progress/abort) → WS `upload` / `progress` / `error` / `abort` |
|
|
103
103
|
| `stat` | `stat` | Grid of metric cards (optional per-item `sparkline`) |
|
|
104
104
|
| `sparkline` | `sparkline` | KPI card with inline area/line mini chart (`headline`, `value`/`trend` shorthand) |
|
|
105
|
-
| `areaChart` | `areachart` | Recharts stacked area (+ legend, optional interactive periods, `headline`, `loading`, `stacked?`) |
|
|
106
|
-
| `barChart` | `barchart` | Recharts bar (`stacked?`, `layout?: 'vertical' \| 'horizontal'`, shared cartesian chrome) |
|
|
107
|
-
| `lineChart` | `linechart` | Recharts line (+
|
|
105
|
+
| `areaChart` | `areachart` | Recharts stacked area (+ legend, optional interactive periods, `headline`, `loading`, `stacked?`, `showLegend?`) |
|
|
106
|
+
| `barChart` | `barchart` | Recharts bar (`stacked?`, `layout?: 'vertical' \| 'horizontal'`, shared cartesian chrome, `showLegend?`) |
|
|
107
|
+
| `lineChart` | `linechart` | Recharts line (+ interactive periods, headline, loading, `curve`/`strokeWidth`/`dots`/`dashed`/`variant`, `showLegend?`) |
|
|
108
108
|
| `pieChart` | `piechart` | Recharts pie/donut (`nameKey`/`valueKey` or `series`, `innerRadius?`) |
|
|
109
109
|
| `radarChart` | `radarchart` | Recharts radar (`angleKey` + `series`, optional `fillOpacity`) |
|
|
110
110
|
| `radialChart` | `radialchart` | Recharts radial bar (`nameKey`/`valueKey` or stacked `series`, optional center text / angles) |
|
package/docs/llms.txt
CHANGED
|
@@ -9,7 +9,7 @@ Notes for agents:
|
|
|
9
9
|
- Public API and wire protocol are camelCase (`onClick`, `bindValue`, `setText`).
|
|
10
10
|
- Page builders run once per WebSocket session (per browser tab). `let` is per-tab, not shared.
|
|
11
11
|
- Update existing nodes with `setText` / `setValue` / bindings. Prefer `ui.state` + `ui.auto` (+ `ui.timer`) for async/live UIs; `ui.refreshable` for simple one-shot rebuilds. Shared or persisted state: `ui.storage` (`tab` / `user` / `app`).
|
|
12
|
-
- Never use `window` / `navigator` in page code — use `ui.clipboard`, `ui.setUrlHash`, `ui.openExternal`.
|
|
12
|
+
- Never use `window` / `navigator` in page code — use `ui.clipboard`, `ui.setUrlHash`, `ui.getUrlSearch` / `ui.updateUrlSearch`, `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
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.
|
package/docs/protocol.md
CHANGED
|
@@ -19,7 +19,7 @@ On unexpected socket close, the client reconnects with exponential backoff (500m
|
|
|
19
19
|
{ "op": "hello", "path": "/examples/counter", "userId": "optional-stable-id", "hash": "optional-hash-without-hash-sign", "browserStorage": {}, "clientStorage": {}, "tabStorage": {} }
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
Client connects (or sends another `hello` after navigate on the same socket) and identifies the page. Optional `userId` (from localStorage/cookie) enables `ui.storage.user` unless overridden by `resolveUserId`. Optional `hash` (no leading `#`) hydrates `ui.getUrlHash()`. Optional `browserStorage` / `clientStorage` / `tabStorage` bags hydrate `ui.storage.browser` / `ui.storage.client` / `ui.storage.tab`.
|
|
22
|
+
Client connects (or sends another `hello` after navigate on the same socket) and identifies the page. Optional `userId` (from localStorage/cookie) enables `ui.storage.user` unless overridden by `resolveUserId`. Optional `hash` (no leading `#`) hydrates `ui.getUrlHash()`. Optional `search` (no leading `?`) hydrates `ui.getUrlSearch()`. Optional `browserStorage` / `clientStorage` / `tabStorage` bags hydrate `ui.storage.browser` / `ui.storage.client` / `ui.storage.tab`.
|
|
23
23
|
|
|
24
24
|
Sent on connect and after client-side navigation. The React client keeps matching `app` shell chrome mounted across remounts; only inset content is replaced.
|
|
25
25
|
|
|
@@ -163,6 +163,14 @@ Client writes `content` with `navigator.clipboard.writeText` (shows an error toa
|
|
|
163
163
|
|
|
164
164
|
Client sets `location.hash` via `history.replaceState` (`hash` has no leading `#`; empty string clears). Hydrated the other way on `hello.hash`. Prefer `ui.setUrlHash` / `ui.getUrlHash`.
|
|
165
165
|
|
|
166
|
+
### `setUrlSearch`
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{ "op": "setUrlSearch", "search": "tab=orders&partner=UBER", "mode": "replace" }
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Client sets `location.search` via `history.replaceState` or `pushState` (`mode`, default `replace`). `search` has no leading `?`; empty string clears. Pathname and hash are preserved. Hydrated the other way on `hello.search`. Prefer `ui.setUrlSearch` / `ui.updateUrlSearch` / `ui.getUrlSearch`. See [URL search](./url-search.md).
|
|
173
|
+
|
|
166
174
|
### `openExternal`
|
|
167
175
|
|
|
168
176
|
```json
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# URL search (query string) helpers
|
|
2
|
+
|
|
3
|
+
Ops consoles often need **shareable filter state** in the address bar
|
|
4
|
+
(`?tab=orders&partner=UBER&from=2026-01-01`). Page builders run on the server, so
|
|
5
|
+
they must not read `window.location`. Clay mirrors the existing hash helpers.
|
|
6
|
+
|
|
7
|
+
## Prefer `ui.urlState`
|
|
8
|
+
|
|
9
|
+
Like `ui.state`, but hydrated from the query string and write-through synced:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { ui } from '@close-by/clay';
|
|
13
|
+
|
|
14
|
+
ui.page('/lastmile', () => {
|
|
15
|
+
const filters = ui.urlState({
|
|
16
|
+
tab: 'dashboard',
|
|
17
|
+
partner: '',
|
|
18
|
+
from: '',
|
|
19
|
+
page: 1,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// ?partner=UBER&page=2 — defaults (dashboard / '' / 1) are omitted
|
|
23
|
+
filters.partner = 'UBER';
|
|
24
|
+
filters.page = 2;
|
|
25
|
+
|
|
26
|
+
ui.select({
|
|
27
|
+
label: 'Partner',
|
|
28
|
+
value: filters.partner || '__all__',
|
|
29
|
+
options: [
|
|
30
|
+
{ value: '__all__', label: 'All' },
|
|
31
|
+
{ value: 'UBER', label: 'Uber Eats' },
|
|
32
|
+
],
|
|
33
|
+
onChange: (v) => {
|
|
34
|
+
filters.partner = v === '__all__' ? '' : v;
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| Option | Default | Role |
|
|
41
|
+
|--------|---------|------|
|
|
42
|
+
| `omitDefaults` | `true` | Drop keys whose value equals the default |
|
|
43
|
+
| `mode` | `'replace'` | History write (`'push'` when Back should undo) |
|
|
44
|
+
| `keys` | identity | Remap state keys → query names (`{ page: 'p' }`) |
|
|
45
|
+
|
|
46
|
+
Works with `ui.auto` / `bindValue` the same way as `ui.state`.
|
|
47
|
+
|
|
48
|
+
## Low-level helpers
|
|
49
|
+
|
|
50
|
+
Escape hatches when you need a one-off read/write without a reactive object:
|
|
51
|
+
|
|
52
|
+
| Helper | Role |
|
|
53
|
+
|--------|------|
|
|
54
|
+
| `ui.getUrlSearch()` | Current search **without** leading `?` (from `hello` / last set) |
|
|
55
|
+
| `ui.getUrlSearchParams()` | Mutable `URLSearchParams` copy of that string |
|
|
56
|
+
| `ui.setUrlSearch(search, opts?)` | Replace the whole query string (empty clears) |
|
|
57
|
+
| `ui.updateUrlSearch(patch, opts?)` | Merge keys; `null` / `undefined` / `''` deletes |
|
|
58
|
+
|
|
59
|
+
`opts.mode` is `'replace'` (default) or `'push'`. Prefer **replace** for filter
|
|
60
|
+
tweaks so Back is not flooded.
|
|
61
|
+
|
|
62
|
+
## Protocol
|
|
63
|
+
|
|
64
|
+
**Client → server** (`hello`):
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{ "op": "hello", "path": "/lastmile", "hash": "", "search": "tab=orders&partner=UBER" }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Server → client**:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{ "op": "setUrlSearch", "search": "tab=orders&partner=UBER", "mode": "replace" }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Client writes `pathname + ?search + hash` via `history.replaceState` / `pushState`.
|
|
77
|
+
Hash is preserved (same as `setUrlHash` preserves search).
|
|
78
|
+
|
|
79
|
+
## Path vs query
|
|
80
|
+
|
|
81
|
+
| Concern | Prefer |
|
|
82
|
+
|---------|--------|
|
|
83
|
+
| Resource identity | Path (`/orders/:id`) via `ui.page` / `ui.navigate` |
|
|
84
|
+
| Active tab, filters, sort, page | **Query string** (`ui.urlState`) |
|
|
85
|
+
| In-page focus / trace id | Hash (`ui.setUrlHash`) when you already use it |
|
|
86
|
+
|
|
87
|
+
## Limits / follow-ups
|
|
88
|
+
|
|
89
|
+
- Hydration runs on each `hello` (connect, reconnect, SPA path change). Pure
|
|
90
|
+
Back/Forward that only changes the query does **not** remount yet — same
|
|
91
|
+
limitation as hash today. A future `urlchange` client event (or popstate →
|
|
92
|
+
hello) would close that gap and re-hydrate `urlState` in place.
|
|
93
|
+
|
|
94
|
+
## Related
|
|
95
|
+
|
|
96
|
+
- [Browser APIs](./browser-apis.md)
|
|
97
|
+
- [WebSocket protocol](./protocol.md) — `hello.search`, `setUrlSearch`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@close-by/clay",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
4
4
|
"description": "NiceGUI-like ui.* facade for Clay apps",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -37,10 +37,10 @@
|
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@close-by/clay-compiler": "0.2.
|
|
41
|
-
"@close-by/clay-core": "0.2.
|
|
42
|
-
"@close-by/clay-components": "0.2.
|
|
43
|
-
"@close-by/clay-server": "0.2.
|
|
40
|
+
"@close-by/clay-compiler": "0.2.15",
|
|
41
|
+
"@close-by/clay-core": "0.2.15",
|
|
42
|
+
"@close-by/clay-components": "0.2.15",
|
|
43
|
+
"@close-by/clay-server": "0.2.15",
|
|
44
44
|
"@tailwindcss/cli": "^4.3.3",
|
|
45
45
|
"tailwindcss": "^4.3.3"
|
|
46
46
|
}
|
package/src/index.ts
CHANGED
|
@@ -237,6 +237,9 @@ import {
|
|
|
237
237
|
type ChartPeriod,
|
|
238
238
|
type ChartReferenceLine,
|
|
239
239
|
type ChartReferenceArea,
|
|
240
|
+
type LineCurve,
|
|
241
|
+
type LineChartVariant,
|
|
242
|
+
type LineChartStyleProps,
|
|
240
243
|
type SparklineProps,
|
|
241
244
|
type CardProps,
|
|
242
245
|
type DialogProps,
|
|
@@ -440,6 +443,10 @@ import {
|
|
|
440
443
|
runJavaScript as runJavaScriptCore,
|
|
441
444
|
getUrlHash as getUrlHashCore,
|
|
442
445
|
setUrlHash as setUrlHashCore,
|
|
446
|
+
getUrlSearch as getUrlSearchCore,
|
|
447
|
+
getUrlSearchParams as getUrlSearchParamsCore,
|
|
448
|
+
setUrlSearch as setUrlSearchCore,
|
|
449
|
+
updateUrlSearch as updateUrlSearchCore,
|
|
443
450
|
openExternal as openExternalCore,
|
|
444
451
|
scroll as scrollCore,
|
|
445
452
|
timer as timerCore,
|
|
@@ -448,6 +455,7 @@ import {
|
|
|
448
455
|
reactive as reactiveCore,
|
|
449
456
|
state as stateCore,
|
|
450
457
|
subscribe as subscribeCore,
|
|
458
|
+
urlState as urlStateCore,
|
|
451
459
|
auto as autoCore,
|
|
452
460
|
draft as draftCore,
|
|
453
461
|
validate as validateCore,
|
|
@@ -465,6 +473,8 @@ import {
|
|
|
465
473
|
type ThemeMode,
|
|
466
474
|
type DraftStorage,
|
|
467
475
|
type DraftOptions,
|
|
476
|
+
type UrlStateOptions,
|
|
477
|
+
type UrlStateValue,
|
|
468
478
|
} from '@close-by/clay-core';
|
|
469
479
|
import {
|
|
470
480
|
ClayServer,
|
|
@@ -1590,6 +1600,35 @@ export function setUrlHash(hash: string): void {
|
|
|
1590
1600
|
setUrlHashCore(hash);
|
|
1591
1601
|
}
|
|
1592
1602
|
|
|
1603
|
+
/** URL search without `?` (from hello / last `setUrlSearch`). Prefer over `window.location`. */
|
|
1604
|
+
export function getUrlSearch(): string {
|
|
1605
|
+
return getUrlSearchCore();
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
/** Parse {@link getUrlSearch} as `URLSearchParams` (mutable copy). */
|
|
1609
|
+
export function getUrlSearchParams(): URLSearchParams {
|
|
1610
|
+
return getUrlSearchParamsCore();
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
/** Set the browser query string. Prefer over `window.history` / `location.search`. */
|
|
1614
|
+
export function setUrlSearch(
|
|
1615
|
+
search: string,
|
|
1616
|
+
opts?: { mode?: 'replace' | 'push' },
|
|
1617
|
+
): void {
|
|
1618
|
+
setUrlSearchCore(search, opts);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/**
|
|
1622
|
+
* Merge keys into the current query string.
|
|
1623
|
+
* `null` / `undefined` / `''` deletes the key.
|
|
1624
|
+
*/
|
|
1625
|
+
export function updateUrlSearch(
|
|
1626
|
+
patch: Record<string, string | number | boolean | null | undefined>,
|
|
1627
|
+
opts?: { mode?: 'replace' | 'push' },
|
|
1628
|
+
): void {
|
|
1629
|
+
updateUrlSearchCore(patch, opts);
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1593
1632
|
/** Open a URL in a new tab. Prefer over `runJavaScript('window.open…')`. */
|
|
1594
1633
|
export function openExternal(url: string): void {
|
|
1595
1634
|
openExternalCore(url);
|
|
@@ -1627,6 +1666,18 @@ export const reactive = reactiveCore;
|
|
|
1627
1666
|
/** Alias for `reactive` — NiceGUI-ish mutable page state. Prefer `ui.state`. */
|
|
1628
1667
|
export const state = stateCore;
|
|
1629
1668
|
|
|
1669
|
+
/**
|
|
1670
|
+
* Like `ui.state`, but hydrated from the query string and write-through synced
|
|
1671
|
+
* via `ui.updateUrlSearch`. Prefer for shareable filters / tabs.
|
|
1672
|
+
* See {@link UrlStateOptions}.
|
|
1673
|
+
*/
|
|
1674
|
+
export function urlState<T extends Record<string, UrlStateValue>>(
|
|
1675
|
+
defaults: T,
|
|
1676
|
+
opts?: UrlStateOptions<T>,
|
|
1677
|
+
): T {
|
|
1678
|
+
return urlStateCore(defaults, opts);
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1630
1681
|
/** Listen for a reactive property change. Prefer this over importing from `@close-by/clay-core`. */
|
|
1631
1682
|
export const subscribe = subscribeCore;
|
|
1632
1683
|
|
|
@@ -1912,6 +1963,10 @@ export const ui = {
|
|
|
1912
1963
|
clipboard,
|
|
1913
1964
|
getUrlHash,
|
|
1914
1965
|
setUrlHash,
|
|
1966
|
+
getUrlSearch,
|
|
1967
|
+
getUrlSearchParams,
|
|
1968
|
+
setUrlSearch,
|
|
1969
|
+
updateUrlSearch,
|
|
1915
1970
|
openExternal,
|
|
1916
1971
|
runJavaScript,
|
|
1917
1972
|
scroll,
|
|
@@ -1920,6 +1975,7 @@ export const ui = {
|
|
|
1920
1975
|
theme,
|
|
1921
1976
|
reactive,
|
|
1922
1977
|
state,
|
|
1978
|
+
urlState,
|
|
1923
1979
|
subscribe,
|
|
1924
1980
|
draft,
|
|
1925
1981
|
validate,
|