@close-by/clay 0.2.14 → 0.2.16
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 +5 -0
- package/docs/browser-apis.md +8 -1
- package/docs/llms.txt +1 -1
- package/docs/protocol.md +18 -2
- package/docs/url-search.md +97 -0
- package/package.json +5 -5
- package/src/index.ts +53 -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
|
@@ -1793,6 +1793,11 @@ onClick: async () => {
|
|
|
1793
1793
|
| `ui.clipboard(content)` | `void` | Write text to clipboard (prefer over `navigator.clipboard`) |
|
|
1794
1794
|
| `ui.getUrlHash()` | `string` | Current hash without `#` (from hello / last set) |
|
|
1795
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) |
|
|
1796
1801
|
| `ui.openExternal(url)` | `void` | `window.open` in a new tab (`noopener`) |
|
|
1797
1802
|
| `ui.theme.set(mode)` | `void` | Push `light` \| `dark` \| `system` to this client |
|
|
1798
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`, `urlchange`, `openExternal` in [WebSocket protocol](./protocol.md)
|
|
65
72
|
- Phase 1 reactivity (preferred over DOM hacks): [reactive-let](./reactive-let.md)
|
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
|
|
|
@@ -35,6 +35,14 @@ Sent on connect and after client-side navigation. The React client keeps matchin
|
|
|
35
35
|
| `type` | `string` | Event name (`click`, `input`, `change`, …) |
|
|
36
36
|
| `value` | `unknown` | Optional payload (string, number, boolean, …) |
|
|
37
37
|
|
|
38
|
+
### `urlchange`
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{ "op": "urlchange", "search": "tab=orders&partner=UBER", "hash": "" }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Browser Back/Forward (or other `popstate`) changed the query and/or hash **without** a path change. Server updates `session.urlSearch` / `session.urlHash` and notifies `ui.urlState` listeners — **no remount**. Path changes still use `hello`. Prefer this over reading `window` in page code. See [URL search](./url-search.md).
|
|
45
|
+
|
|
38
46
|
## Server → client
|
|
39
47
|
|
|
40
48
|
### `mount`
|
|
@@ -161,7 +169,15 @@ Client writes `content` with `navigator.clipboard.writeText` (shows an error toa
|
|
|
161
169
|
{ "op": "setUrlHash", "hash": "trace-abc" }
|
|
162
170
|
```
|
|
163
171
|
|
|
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`.
|
|
172
|
+
Client sets `location.hash` via `history.replaceState` (`hash` has no leading `#`; empty string clears). Hydrated the other way on `hello.hash` and re-synced in place on `urlchange`. Prefer `ui.setUrlHash` / `ui.getUrlHash`.
|
|
173
|
+
|
|
174
|
+
### `setUrlSearch`
|
|
175
|
+
|
|
176
|
+
```json
|
|
177
|
+
{ "op": "setUrlSearch", "search": "tab=orders&partner=UBER", "mode": "replace" }
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
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` and re-synced in place on `urlchange`. Prefer `ui.setUrlSearch` / `ui.updateUrlSearch` / `ui.getUrlSearch`. See [URL search](./url-search.md).
|
|
165
181
|
|
|
166
182
|
### `openExternal`
|
|
167
183
|
|
|
@@ -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` on connect / path change):
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{ "op": "hello", "path": "/lastmile", "hash": "", "search": "tab=orders&partner=UBER" }
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Client → server** (Back/Forward, same path — no remount):
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{ "op": "urlchange", "search": "partner=GLOVO", "hash": "" }
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Server → client**:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{ "op": "setUrlSearch", "search": "tab=orders&partner=UBER", "mode": "replace" }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Client writes `pathname + ?search + hash` via `history.replaceState` / `pushState`.
|
|
83
|
+
Hash is preserved (same as `setUrlHash` preserves search). Same-path `popstate`
|
|
84
|
+
sends `urlchange` so `ui.urlState` re-hydrates in place.
|
|
85
|
+
|
|
86
|
+
## Path vs query
|
|
87
|
+
|
|
88
|
+
| Concern | Prefer |
|
|
89
|
+
|---------|--------|
|
|
90
|
+
| Resource identity | Path (`/orders/:id`) via `ui.page` / `ui.navigate` |
|
|
91
|
+
| Active tab, filters, sort, page | **Query string** (`ui.urlState`) |
|
|
92
|
+
| In-page focus / trace id | Hash (`ui.setUrlHash`) when you already use it |
|
|
93
|
+
|
|
94
|
+
## Related
|
|
95
|
+
|
|
96
|
+
- [Browser APIs](./browser-apis.md)
|
|
97
|
+
- [WebSocket protocol](./protocol.md) — `hello.search`, `urlchange`, `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.16",
|
|
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.16",
|
|
41
|
+
"@close-by/clay-core": "0.2.16",
|
|
42
|
+
"@close-by/clay-components": "0.2.16",
|
|
43
|
+
"@close-by/clay-server": "0.2.16",
|
|
44
44
|
"@tailwindcss/cli": "^4.3.3",
|
|
45
45
|
"tailwindcss": "^4.3.3"
|
|
46
46
|
}
|
package/src/index.ts
CHANGED
|
@@ -443,6 +443,10 @@ import {
|
|
|
443
443
|
runJavaScript as runJavaScriptCore,
|
|
444
444
|
getUrlHash as getUrlHashCore,
|
|
445
445
|
setUrlHash as setUrlHashCore,
|
|
446
|
+
getUrlSearch as getUrlSearchCore,
|
|
447
|
+
getUrlSearchParams as getUrlSearchParamsCore,
|
|
448
|
+
setUrlSearch as setUrlSearchCore,
|
|
449
|
+
updateUrlSearch as updateUrlSearchCore,
|
|
446
450
|
openExternal as openExternalCore,
|
|
447
451
|
scroll as scrollCore,
|
|
448
452
|
timer as timerCore,
|
|
@@ -451,6 +455,7 @@ import {
|
|
|
451
455
|
reactive as reactiveCore,
|
|
452
456
|
state as stateCore,
|
|
453
457
|
subscribe as subscribeCore,
|
|
458
|
+
urlState as urlStateCore,
|
|
454
459
|
auto as autoCore,
|
|
455
460
|
draft as draftCore,
|
|
456
461
|
validate as validateCore,
|
|
@@ -468,6 +473,8 @@ import {
|
|
|
468
473
|
type ThemeMode,
|
|
469
474
|
type DraftStorage,
|
|
470
475
|
type DraftOptions,
|
|
476
|
+
type UrlStateOptions,
|
|
477
|
+
type UrlStateValue,
|
|
471
478
|
} from '@close-by/clay-core';
|
|
472
479
|
import {
|
|
473
480
|
ClayServer,
|
|
@@ -1593,6 +1600,35 @@ export function setUrlHash(hash: string): void {
|
|
|
1593
1600
|
setUrlHashCore(hash);
|
|
1594
1601
|
}
|
|
1595
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
|
+
|
|
1596
1632
|
/** Open a URL in a new tab. Prefer over `runJavaScript('window.open…')`. */
|
|
1597
1633
|
export function openExternal(url: string): void {
|
|
1598
1634
|
openExternalCore(url);
|
|
@@ -1630,6 +1666,18 @@ export const reactive = reactiveCore;
|
|
|
1630
1666
|
/** Alias for `reactive` — NiceGUI-ish mutable page state. Prefer `ui.state`. */
|
|
1631
1667
|
export const state = stateCore;
|
|
1632
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
|
+
|
|
1633
1681
|
/** Listen for a reactive property change. Prefer this over importing from `@close-by/clay-core`. */
|
|
1634
1682
|
export const subscribe = subscribeCore;
|
|
1635
1683
|
|
|
@@ -1915,6 +1963,10 @@ export const ui = {
|
|
|
1915
1963
|
clipboard,
|
|
1916
1964
|
getUrlHash,
|
|
1917
1965
|
setUrlHash,
|
|
1966
|
+
getUrlSearch,
|
|
1967
|
+
getUrlSearchParams,
|
|
1968
|
+
setUrlSearch,
|
|
1969
|
+
updateUrlSearch,
|
|
1918
1970
|
openExternal,
|
|
1919
1971
|
runJavaScript,
|
|
1920
1972
|
scroll,
|
|
@@ -1923,6 +1975,7 @@ export const ui = {
|
|
|
1923
1975
|
theme,
|
|
1924
1976
|
reactive,
|
|
1925
1977
|
state,
|
|
1978
|
+
urlState,
|
|
1926
1979
|
subscribe,
|
|
1927
1980
|
draft,
|
|
1928
1981
|
validate,
|