@human-synthesis/norns-ui 0.1.0 → 0.2.0
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/package.json +4 -2
- package/src/auto-import.js +8 -1
- package/src/components/ChatThread.n +97 -0
- package/src/components/DataTable.n +10 -2
- package/src/components/Feed.n +71 -0
- package/src/components/Kanban.n +17 -7
- package/src/components/LiveLog.n +64 -0
- package/src/components/PresenceAvatars.n +45 -0
- package/src/components/StreamText.n +64 -0
- package/src/components/Table.n +4 -0
- package/src/components/Timeline.n +5 -3
- package/src/components/Tree.n +4 -2
- package/src/contracts.js +135 -0
- package/src/lib/behaviors/index.js +1 -0
- package/src/lib/behaviors/optimistic.svelte.js +24 -0
- package/src/palette-manifest.json +4188 -0
- package/src/types/Chart.d.ts +21 -0
- package/src/types/ChatThread.d.ts +27 -0
- package/src/types/DataTable.d.ts +5 -1
- package/src/types/Feed.d.ts +19 -0
- package/src/types/Kanban.d.ts +26 -0
- package/src/types/Listbox.d.ts +19 -0
- package/src/types/LiveLog.d.ts +16 -0
- package/src/types/PresenceAvatars.d.ts +17 -0
- package/src/types/Spinner.d.ts +12 -0
- package/src/types/StreamText.d.ts +18 -0
- package/src/types/Table.d.ts +5 -1
- package/src/types/Timeline.d.ts +2 -0
- package/src/types/Tree.d.ts +2 -0
- package/src/types/Window.d.ts +17 -0
package/src/contracts.js
CHANGED
|
@@ -20,6 +20,9 @@ import * as v from 'valibot';
|
|
|
20
20
|
const NAME = '[A-Za-z_][A-Za-z0-9_]*';
|
|
21
21
|
export const QUERY_ADDRESS = new RegExp(`^${NAME}\\.Query\\.${NAME}$`);
|
|
22
22
|
export const ACTION_ADDRESS = new RegExp(`^${NAME}\\.Action\\.${NAME}$`);
|
|
23
|
+
export const SNIPPET_ADDRESS = new RegExp(`^${NAME}\\.Snippet\\.${NAME}$`);
|
|
24
|
+
export const STREAM_ADDRESS = new RegExp(`^${NAME}\\.Endpoint\\.${NAME}$`);
|
|
25
|
+
export const ROOM_ADDRESS = new RegExp(`^${NAME}\\.Worker\\.${NAME}$`);
|
|
23
26
|
|
|
24
27
|
export const query = () =>
|
|
25
28
|
v.pipe(v.string(), v.regex(QUERY_ADDRESS, 'expected a Query address (module.Query.name)'));
|
|
@@ -30,6 +33,17 @@ export const action = () =>
|
|
|
30
33
|
/** A pass-through prop value — string/number/boolean literal from the spec. */
|
|
31
34
|
export const literal = () => v.union([v.string(), v.number(), v.boolean()]);
|
|
32
35
|
|
|
36
|
+
export const snippet = () =>
|
|
37
|
+
v.pipe(v.string(), v.regex(SNIPPET_ADDRESS, 'expected a Snippet address (module.Snippet.name)'));
|
|
38
|
+
|
|
39
|
+
/** A `stream:` Endpoint binding — the emitter turns it into a streamSource URL. */
|
|
40
|
+
export const stream = () =>
|
|
41
|
+
v.pipe(v.string(), v.regex(STREAM_ADDRESS, 'expected an Endpoint address (module.Endpoint.name)'));
|
|
42
|
+
|
|
43
|
+
/** A `room: true` Worker binding — the emitter turns it into a roomChannel name. */
|
|
44
|
+
export const room = () =>
|
|
45
|
+
v.pipe(v.string(), v.regex(ROOM_ADDRESS, 'expected a Worker address (module.Worker.name)'));
|
|
46
|
+
|
|
33
47
|
const opt = (schema) => v.optional(schema);
|
|
34
48
|
|
|
35
49
|
export const contracts = {
|
|
@@ -41,6 +55,8 @@ export const contracts = {
|
|
|
41
55
|
dense: opt(literal()),
|
|
42
56
|
stickyHeader: opt(literal()),
|
|
43
57
|
emptyMessage: opt(literal()),
|
|
58
|
+
cell: opt(snippet()),
|
|
59
|
+
empty: opt(snippet()),
|
|
44
60
|
class: opt(literal())
|
|
45
61
|
}),
|
|
46
62
|
DataTable: v.strictObject({
|
|
@@ -52,6 +68,8 @@ export const contracts = {
|
|
|
52
68
|
emptyMessage: opt(literal()),
|
|
53
69
|
sortKey: opt(literal()),
|
|
54
70
|
sortDir: opt(literal()),
|
|
71
|
+
cell: opt(snippet()),
|
|
72
|
+
empty: opt(snippet()),
|
|
55
73
|
class: opt(literal())
|
|
56
74
|
}),
|
|
57
75
|
Kanban: v.strictObject({
|
|
@@ -61,6 +79,8 @@ export const contracts = {
|
|
|
61
79
|
title: opt(literal()),
|
|
62
80
|
subtitle: opt(literal()),
|
|
63
81
|
emptyMessage: opt(literal()),
|
|
82
|
+
card: opt(snippet()),
|
|
83
|
+
empty: opt(snippet()),
|
|
64
84
|
class: opt(literal())
|
|
65
85
|
}),
|
|
66
86
|
Chart: v.strictObject({
|
|
@@ -133,5 +153,120 @@ export const contracts = {
|
|
|
133
153
|
pageSize: opt(literal()),
|
|
134
154
|
siblingCount: opt(literal()),
|
|
135
155
|
class: opt(literal())
|
|
156
|
+
}),
|
|
157
|
+
Checkbox: v.strictObject({
|
|
158
|
+
name: opt(literal()),
|
|
159
|
+
checked: opt(literal()),
|
|
160
|
+
required: opt(literal()),
|
|
161
|
+
disabled: opt(literal()),
|
|
162
|
+
error: opt(literal()),
|
|
163
|
+
class: opt(literal())
|
|
164
|
+
}),
|
|
165
|
+
Switch: v.strictObject({
|
|
166
|
+
name: opt(literal()),
|
|
167
|
+
checked: opt(literal()),
|
|
168
|
+
required: opt(literal()),
|
|
169
|
+
disabled: opt(literal()),
|
|
170
|
+
error: opt(literal()),
|
|
171
|
+
class: opt(literal())
|
|
172
|
+
}),
|
|
173
|
+
Textarea: v.strictObject({
|
|
174
|
+
name: opt(literal()),
|
|
175
|
+
value: opt(literal()),
|
|
176
|
+
placeholder: opt(literal()),
|
|
177
|
+
rows: opt(literal()),
|
|
178
|
+
required: opt(literal()),
|
|
179
|
+
disabled: opt(literal()),
|
|
180
|
+
error: opt(literal()),
|
|
181
|
+
class: opt(literal())
|
|
182
|
+
}),
|
|
183
|
+
DatePicker: v.strictObject({
|
|
184
|
+
name: opt(literal()),
|
|
185
|
+
value: opt(literal()),
|
|
186
|
+
min: opt(literal()),
|
|
187
|
+
max: opt(literal()),
|
|
188
|
+
placeholder: opt(literal()),
|
|
189
|
+
label: opt(literal()),
|
|
190
|
+
disabled: opt(literal()),
|
|
191
|
+
error: opt(literal()),
|
|
192
|
+
id: opt(literal()),
|
|
193
|
+
class: opt(literal())
|
|
194
|
+
}),
|
|
195
|
+
Uploader: v.strictObject({
|
|
196
|
+
multiple: opt(literal()),
|
|
197
|
+
accept: opt(literal()),
|
|
198
|
+
disabled: opt(literal()),
|
|
199
|
+
placeholder: opt(literal()),
|
|
200
|
+
helper: opt(literal()),
|
|
201
|
+
class: opt(literal())
|
|
202
|
+
}),
|
|
203
|
+
Timeline: v.strictObject({
|
|
204
|
+
data: query(),
|
|
205
|
+
class: opt(literal())
|
|
206
|
+
}),
|
|
207
|
+
Tree: v.strictObject({
|
|
208
|
+
data: query(),
|
|
209
|
+
class: opt(literal())
|
|
210
|
+
}),
|
|
211
|
+
Calendar: v.strictObject({
|
|
212
|
+
value: opt(literal()),
|
|
213
|
+
range: opt(literal()),
|
|
214
|
+
min: opt(literal()),
|
|
215
|
+
max: opt(literal()),
|
|
216
|
+
label: opt(literal()),
|
|
217
|
+
class: opt(literal())
|
|
218
|
+
}),
|
|
219
|
+
StreamText: v.strictObject({
|
|
220
|
+
streamSource: stream(),
|
|
221
|
+
autostart: opt(literal()),
|
|
222
|
+
class: opt(literal())
|
|
223
|
+
}),
|
|
224
|
+
ChatThread: v.strictObject({
|
|
225
|
+
roomChannel: room(),
|
|
226
|
+
send: opt(literal()),
|
|
227
|
+
receive: opt(literal()),
|
|
228
|
+
placeholder: opt(literal()),
|
|
229
|
+
sendLabel: opt(literal()),
|
|
230
|
+
emptyMessage: opt(literal()),
|
|
231
|
+
message: opt(snippet()),
|
|
232
|
+
class: opt(literal())
|
|
233
|
+
}),
|
|
234
|
+
Feed: v.strictObject({
|
|
235
|
+
streamSource: opt(stream()),
|
|
236
|
+
roomChannel: opt(room()),
|
|
237
|
+
receive: opt(literal()),
|
|
238
|
+
max: opt(literal()),
|
|
239
|
+
emptyMessage: opt(literal()),
|
|
240
|
+
item: opt(snippet()),
|
|
241
|
+
class: opt(literal())
|
|
242
|
+
}),
|
|
243
|
+
PresenceAvatars: v.strictObject({
|
|
244
|
+
roomChannel: room(),
|
|
245
|
+
max: opt(literal()),
|
|
246
|
+
size: opt(literal()),
|
|
247
|
+
label: opt(literal()),
|
|
248
|
+
showCount: opt(literal()),
|
|
249
|
+
class: opt(literal())
|
|
250
|
+
}),
|
|
251
|
+
LiveLog: v.strictObject({
|
|
252
|
+
streamSource: opt(stream()),
|
|
253
|
+
roomChannel: opt(room()),
|
|
254
|
+
receive: opt(literal()),
|
|
255
|
+
maxLines: opt(literal()),
|
|
256
|
+
class: opt(literal())
|
|
136
257
|
})
|
|
137
258
|
};
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Snippet slot signatures: tag → prop → ordered arg names the component
|
|
262
|
+
* passes when it renders the slot. The generator checks a bound Snippet
|
|
263
|
+
* unit's declared `args` against this signature (exact names, exact order),
|
|
264
|
+
* so a mismatch refuses at generate time.
|
|
265
|
+
*/
|
|
266
|
+
export const snippetSlots = {
|
|
267
|
+
Table: { cell: ['row', 'column', 'value'], empty: [] },
|
|
268
|
+
DataTable: { cell: ['row', 'column', 'value'], empty: [] },
|
|
269
|
+
Kanban: { card: ['card'], empty: [] },
|
|
270
|
+
ChatThread: { message: ['message'] },
|
|
271
|
+
Feed: { item: ['item'] }
|
|
272
|
+
};
|
|
@@ -12,3 +12,4 @@ export { focusTrap } from './focusTrap.svelte.js';
|
|
|
12
12
|
export { lock as scrollLockAcquire, unlock as scrollLockRelease, scrollLock } from './scrollLock.svelte.js';
|
|
13
13
|
export { useFloating, positionAt } from './floating.svelte.js';
|
|
14
14
|
export { rovingTabindex } from './rovingTabindex.svelte.js';
|
|
15
|
+
export { optimisticOverlay } from './optimistic.svelte.js';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimistic overlay: a client-side id → value map that shadows server truth
|
|
3
|
+
* until the next reload. A user gesture (drag-drop, toggle) lands instantly
|
|
4
|
+
* via `set`, while the bound action persists it in the background; `get`
|
|
5
|
+
* answers the overlaid value and falls back to the server's. Extracted from
|
|
6
|
+
* Kanban's drag-drop so other components can share the interaction model.
|
|
7
|
+
*/
|
|
8
|
+
export function optimisticOverlay() {
|
|
9
|
+
let overlay = $state({});
|
|
10
|
+
return {
|
|
11
|
+
get(id, base) {
|
|
12
|
+
return overlay[id] ?? base;
|
|
13
|
+
},
|
|
14
|
+
set(id, value) {
|
|
15
|
+
overlay = { ...overlay, [id]: value };
|
|
16
|
+
},
|
|
17
|
+
clear() {
|
|
18
|
+
overlay = {};
|
|
19
|
+
},
|
|
20
|
+
get size() {
|
|
21
|
+
return Object.keys(overlay).length;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|