@keemakr/ui-sdk 1.4.0-dev-20261012081437 → 1.4.0-dev-20261012082811
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 +44 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/nodes.d.ts +1 -1
- package/dist/nodes.js +1 -0
- package/dist/schema.d.ts +86 -0
- package/dist/schema.js +111 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,7 +54,50 @@ export default defineScreen({
|
|
|
54
54
|
Folder names are screen identities. For example, `ui/campaigns/campaign/screen.ts` is the
|
|
55
55
|
`campaign` screen. Screen folder names must be unique across the module.
|
|
56
56
|
|
|
57
|
-
## 4.
|
|
57
|
+
## 4. Optional workspace presentation
|
|
58
|
+
|
|
59
|
+
Screens use the document layout unless they opt into a platform-owned workspace. A workspace can
|
|
60
|
+
place page actions in a pinned footer and promote one keyed root section into a responsive right
|
|
61
|
+
rail. The module declares meaning; each Keemakr client owns widths, breakpoints and styling.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
load: async () => ({
|
|
65
|
+
version: 1,
|
|
66
|
+
title: 'Account review',
|
|
67
|
+
actions: [
|
|
68
|
+
{
|
|
69
|
+
type: 'button',
|
|
70
|
+
label: 'Save review',
|
|
71
|
+
onPress: { kind: 'action', action: 'save_review' },
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
presentation: {
|
|
75
|
+
layout: 'workspace',
|
|
76
|
+
actionPlacement: 'footer',
|
|
77
|
+
rightRail: {
|
|
78
|
+
sectionKey: 'history',
|
|
79
|
+
label: 'Review history',
|
|
80
|
+
collapsible: true,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
children: [
|
|
84
|
+
{ type: 'text', value: 'Current review' },
|
|
85
|
+
{
|
|
86
|
+
type: 'section',
|
|
87
|
+
key: 'history',
|
|
88
|
+
title: 'History',
|
|
89
|
+
layout: 'stack',
|
|
90
|
+
columns: 1,
|
|
91
|
+
children: [{ type: 'text', value: 'Version 1' }],
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
}),
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`rightRail.sectionKey` must identify exactly one keyed section in the screen's root `children`.
|
|
98
|
+
Older screens require no changes and keep header actions plus the normal document flow.
|
|
99
|
+
|
|
100
|
+
## 5. Generate before Eve runs
|
|
58
101
|
|
|
59
102
|
Use the SDK wrapper in the module scripts:
|
|
60
103
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { UI_SCHEMA_VERSION, pressSchema, buttonSchema, cellSchema, nodeSchema, screenTreeSchema, actionResultSchema, screenLoadRequestSchema, screenLoadResponseSchema, screenActionRequestSchema, interfaceFamilySchema, surfaceDefinitionSchema, surfaceSchema, screenRegistrationSchema, uiManifestSchema, } from './schema.js';
|
|
2
|
-
export type { Press, ScreenNode, ScreenRootNode, ScreenTree, ActionResult, InterfaceFamily, SurfaceDefinition, Surface, ScreenRegistration, UiManifest, ScreenTreeInput, ActionResultInput, } from './schema.js';
|
|
1
|
+
export { UI_SCHEMA_VERSION, pressSchema, buttonSchema, cellSchema, nodeSchema, screenPresentationSchema, screenTreeSchema, actionResultSchema, screenLoadRequestSchema, screenLoadResponseSchema, screenActionRequestSchema, interfaceFamilySchema, surfaceDefinitionSchema, surfaceSchema, screenRegistrationSchema, uiManifestSchema, } from './schema.js';
|
|
2
|
+
export type { Press, ScreenPresentation, ScreenNode, ScreenRootNode, ScreenTree, ActionResult, InterfaceFamily, SurfaceDefinition, Surface, ScreenRegistration, UiManifest, ScreenTreeInput, ActionResultInput, } from './schema.js';
|
|
3
3
|
export { childNodesOf, KNOWN_CONTAINERS, KNOWN_NODE_TYPES } from './nodes.js';
|
|
4
4
|
export type { ContainerChild, ContainerNodeType, KnownNodeType } from './nodes.js';
|
|
5
5
|
export { pageRangeLabel, pageWindow, pressWithValue } from './pager.js';
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// platform components, proxies load/action with a grant
|
|
7
7
|
//
|
|
8
8
|
// Module code never ships to the browser. UI is data, not code.
|
|
9
|
-
export { UI_SCHEMA_VERSION, pressSchema, buttonSchema, cellSchema, nodeSchema, screenTreeSchema, actionResultSchema, screenLoadRequestSchema, screenLoadResponseSchema, screenActionRequestSchema, interfaceFamilySchema, surfaceDefinitionSchema, surfaceSchema, screenRegistrationSchema, uiManifestSchema, } from './schema.js';
|
|
9
|
+
export { UI_SCHEMA_VERSION, pressSchema, buttonSchema, cellSchema, nodeSchema, screenPresentationSchema, screenTreeSchema, actionResultSchema, screenLoadRequestSchema, screenLoadResponseSchema, screenActionRequestSchema, interfaceFamilySchema, surfaceDefinitionSchema, surfaceSchema, screenRegistrationSchema, uiManifestSchema, } from './schema.js';
|
|
10
10
|
export { childNodesOf, KNOWN_CONTAINERS, KNOWN_NODE_TYPES } from './nodes.js';
|
|
11
11
|
export { pageRangeLabel, pageWindow, pressWithValue } from './pager.js';
|
|
12
12
|
export { defineScreen, defineSurface } from './define.js';
|
package/dist/nodes.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ScreenRootNode } from './schema.js';
|
|
2
2
|
/** Every node type the root union admits (KEE-574 item 3). */
|
|
3
|
-
export declare const KNOWN_NODE_TYPES: readonly ["heading", "text", "markdown", "stat", "button", "form", "table", "timeline", "pending_request", "pending_requests", "choice_cards", "progress", "chart", "pagination", "select", "alert", "details", "timestamp", "divider", "section", "tabs"];
|
|
3
|
+
export declare const KNOWN_NODE_TYPES: readonly ["heading", "text", "markdown", "stat", "button", "form", "table", "timeline", "pending_request", "pending_requests", "choice_cards", "progress", "chart", "pagination", "select", "alert", "details", "timestamp", "divider", "board", "section", "tabs"];
|
|
4
4
|
export type KnownNodeType = (typeof KNOWN_NODE_TYPES)[number];
|
|
5
5
|
/**
|
|
6
6
|
* The node types that hold child nodes. `section` mounts its children
|
package/dist/nodes.js
CHANGED
package/dist/schema.d.ts
CHANGED
|
@@ -365,11 +365,56 @@ declare const leafNodeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
365
365
|
label: z.ZodOptional<z.ZodString>;
|
|
366
366
|
}, z.core.$strip>, z.ZodObject<{
|
|
367
367
|
type: z.ZodLiteral<"divider">;
|
|
368
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
369
|
+
type: z.ZodLiteral<"board">;
|
|
370
|
+
columns: z.ZodArray<z.ZodObject<{
|
|
371
|
+
key: z.ZodString;
|
|
372
|
+
label: z.ZodString;
|
|
373
|
+
tone: z.ZodDefault<z.ZodEnum<{
|
|
374
|
+
success: "success";
|
|
375
|
+
default: "default";
|
|
376
|
+
warning: "warning";
|
|
377
|
+
destructive: "destructive";
|
|
378
|
+
}>>;
|
|
379
|
+
cards: z.ZodArray<z.ZodObject<{
|
|
380
|
+
key: z.ZodString;
|
|
381
|
+
title: z.ZodString;
|
|
382
|
+
subtitle: z.ZodOptional<z.ZodString>;
|
|
383
|
+
values: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
384
|
+
kind: z.ZodLiteral<"text">;
|
|
385
|
+
value: z.ZodString;
|
|
386
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
387
|
+
kind: z.ZodLiteral<"badge">;
|
|
388
|
+
value: z.ZodString;
|
|
389
|
+
tone: z.ZodDefault<z.ZodEnum<{
|
|
390
|
+
success: "success";
|
|
391
|
+
default: "default";
|
|
392
|
+
warning: "warning";
|
|
393
|
+
destructive: "destructive";
|
|
394
|
+
}>>;
|
|
395
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
396
|
+
kind: z.ZodLiteral<"date">;
|
|
397
|
+
value: z.ZodISODateTime;
|
|
398
|
+
}, z.core.$strip>], "kind">>>;
|
|
399
|
+
onPress: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
400
|
+
kind: z.ZodLiteral<"action">;
|
|
401
|
+
action: z.ZodString;
|
|
402
|
+
input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
403
|
+
confirm: z.ZodOptional<z.ZodString>;
|
|
404
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
405
|
+
kind: z.ZodLiteral<"navigate">;
|
|
406
|
+
screen: z.ZodString;
|
|
407
|
+
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
408
|
+
}, z.core.$strip>], "kind">>;
|
|
409
|
+
}, z.core.$strip>>;
|
|
410
|
+
}, z.core.$strip>>;
|
|
411
|
+
empty: z.ZodDefault<z.ZodString>;
|
|
368
412
|
}, z.core.$strip>], "type">;
|
|
369
413
|
type LeafScreenNode = z.infer<typeof leafNodeSchema>;
|
|
370
414
|
type LeafScreenNodeInput = z.input<typeof leafNodeSchema>;
|
|
371
415
|
export type ScreenNode = LeafScreenNode | {
|
|
372
416
|
type: 'section';
|
|
417
|
+
key?: string;
|
|
373
418
|
title?: string;
|
|
374
419
|
description?: string;
|
|
375
420
|
layout: 'stack' | 'grid' | 'row';
|
|
@@ -379,6 +424,7 @@ export type ScreenNode = LeafScreenNode | {
|
|
|
379
424
|
};
|
|
380
425
|
type ScreenNodeInput = LeafScreenNodeInput | {
|
|
381
426
|
type: 'section';
|
|
427
|
+
key?: string;
|
|
382
428
|
title?: string;
|
|
383
429
|
description?: string;
|
|
384
430
|
layout: 'stack' | 'grid' | 'row';
|
|
@@ -424,6 +470,19 @@ type ScreenRootNodeInput = ScreenNodeInput | TabsScreenNodeInput;
|
|
|
424
470
|
* MAX_SECTION_DEPTH exactly as it was before `tabs` existed.
|
|
425
471
|
*/
|
|
426
472
|
export declare const nodeSchema: z.ZodType<ScreenRootNode, ScreenRootNodeInput>;
|
|
473
|
+
export declare const screenPresentationSchema: z.ZodObject<{
|
|
474
|
+
layout: z.ZodLiteral<"workspace">;
|
|
475
|
+
actionPlacement: z.ZodDefault<z.ZodEnum<{
|
|
476
|
+
header: "header";
|
|
477
|
+
footer: "footer";
|
|
478
|
+
}>>;
|
|
479
|
+
rightRail: z.ZodOptional<z.ZodObject<{
|
|
480
|
+
sectionKey: z.ZodString;
|
|
481
|
+
label: z.ZodString;
|
|
482
|
+
collapsible: z.ZodDefault<z.ZodBoolean>;
|
|
483
|
+
defaultCollapsed: z.ZodDefault<z.ZodBoolean>;
|
|
484
|
+
}, z.core.$strip>>;
|
|
485
|
+
}, z.core.$strip>;
|
|
427
486
|
export declare const screenTreeSchema: z.ZodObject<{
|
|
428
487
|
version: z.ZodLiteral<1>;
|
|
429
488
|
title: z.ZodString;
|
|
@@ -459,6 +518,19 @@ export declare const screenTreeSchema: z.ZodObject<{
|
|
|
459
518
|
disabledReason: z.ZodOptional<z.ZodString>;
|
|
460
519
|
}, z.core.$strip>>>;
|
|
461
520
|
children: z.ZodArray<z.ZodType<ScreenRootNode, ScreenRootNodeInput, z.core.$ZodTypeInternals<ScreenRootNode, ScreenRootNodeInput>>>;
|
|
521
|
+
presentation: z.ZodOptional<z.ZodObject<{
|
|
522
|
+
layout: z.ZodLiteral<"workspace">;
|
|
523
|
+
actionPlacement: z.ZodDefault<z.ZodEnum<{
|
|
524
|
+
header: "header";
|
|
525
|
+
footer: "footer";
|
|
526
|
+
}>>;
|
|
527
|
+
rightRail: z.ZodOptional<z.ZodObject<{
|
|
528
|
+
sectionKey: z.ZodString;
|
|
529
|
+
label: z.ZodString;
|
|
530
|
+
collapsible: z.ZodDefault<z.ZodBoolean>;
|
|
531
|
+
defaultCollapsed: z.ZodDefault<z.ZodBoolean>;
|
|
532
|
+
}, z.core.$strip>>;
|
|
533
|
+
}, z.core.$strip>>;
|
|
462
534
|
refreshSeconds: z.ZodOptional<z.ZodNumber>;
|
|
463
535
|
}, z.core.$strip>;
|
|
464
536
|
/** What an action handler returns. `refresh` re-runs `load` after the action. */
|
|
@@ -509,6 +581,19 @@ export declare const screenLoadResponseSchema: z.ZodObject<{
|
|
|
509
581
|
disabledReason: z.ZodOptional<z.ZodString>;
|
|
510
582
|
}, z.core.$strip>>>;
|
|
511
583
|
children: z.ZodArray<z.ZodType<ScreenRootNode, ScreenRootNodeInput, z.core.$ZodTypeInternals<ScreenRootNode, ScreenRootNodeInput>>>;
|
|
584
|
+
presentation: z.ZodOptional<z.ZodObject<{
|
|
585
|
+
layout: z.ZodLiteral<"workspace">;
|
|
586
|
+
actionPlacement: z.ZodDefault<z.ZodEnum<{
|
|
587
|
+
header: "header";
|
|
588
|
+
footer: "footer";
|
|
589
|
+
}>>;
|
|
590
|
+
rightRail: z.ZodOptional<z.ZodObject<{
|
|
591
|
+
sectionKey: z.ZodString;
|
|
592
|
+
label: z.ZodString;
|
|
593
|
+
collapsible: z.ZodDefault<z.ZodBoolean>;
|
|
594
|
+
defaultCollapsed: z.ZodDefault<z.ZodBoolean>;
|
|
595
|
+
}, z.core.$strip>>;
|
|
596
|
+
}, z.core.$strip>>;
|
|
512
597
|
refreshSeconds: z.ZodOptional<z.ZodNumber>;
|
|
513
598
|
}, z.core.$strip>;
|
|
514
599
|
}, z.core.$strip>;
|
|
@@ -569,6 +654,7 @@ export declare const uiManifestSchema: z.ZodObject<{
|
|
|
569
654
|
}, z.core.$strip>>;
|
|
570
655
|
}, z.core.$strip>;
|
|
571
656
|
export type Press = z.infer<typeof pressSchema>;
|
|
657
|
+
export type ScreenPresentation = z.infer<typeof screenPresentationSchema>;
|
|
572
658
|
export type ScreenTree = z.infer<typeof screenTreeSchema>;
|
|
573
659
|
export type ActionResult = z.infer<typeof actionResultSchema>;
|
|
574
660
|
export type InterfaceFamily = z.infer<typeof interfaceFamilySchema>;
|
package/dist/schema.js
CHANGED
|
@@ -233,6 +233,71 @@ const timestampSchema = z.object({
|
|
|
233
233
|
/** Shown before the time, muted: "Last contacted". */
|
|
234
234
|
label: z.string().max(80).optional(),
|
|
235
235
|
});
|
|
236
|
+
/**
|
|
237
|
+
* Cards in columns by stage: a sales pipeline, installs by status, accounts by
|
|
238
|
+
* health. A card carries a title, an optional subtitle, and up to 3 values that
|
|
239
|
+
* reuse the table cell kinds (a `date` shows in the viewer's zone, a `badge`
|
|
240
|
+
* keeps its tone). A card with `onPress` opens a record or calls an action.
|
|
241
|
+
* Each column shows its card count. Moving a card between columns is not part
|
|
242
|
+
* of this block yet: call an action from the record instead.
|
|
243
|
+
*/
|
|
244
|
+
const boardSchema = z
|
|
245
|
+
.object({
|
|
246
|
+
type: z.literal('board'),
|
|
247
|
+
columns: z
|
|
248
|
+
.array(z.object({
|
|
249
|
+
key: slug,
|
|
250
|
+
label: z.string().min(1).max(40),
|
|
251
|
+
/** Colours the column's count: an at-risk stage, a done stage. */
|
|
252
|
+
tone: tone.default('default'),
|
|
253
|
+
cards: z
|
|
254
|
+
.array(z.object({
|
|
255
|
+
key: z.string().min(1).max(128),
|
|
256
|
+
title: z.string().min(1).max(120),
|
|
257
|
+
subtitle: z.string().max(200).optional(),
|
|
258
|
+
values: z.array(cellSchema).max(3).optional(),
|
|
259
|
+
onPress: pressSchema.optional(),
|
|
260
|
+
}))
|
|
261
|
+
.max(100),
|
|
262
|
+
}))
|
|
263
|
+
.min(1)
|
|
264
|
+
.max(8),
|
|
265
|
+
/** Shown in a column that has no cards. */
|
|
266
|
+
empty: z.string().min(1).max(80).default('Nothing here'),
|
|
267
|
+
})
|
|
268
|
+
.superRefine((node, ctx) => {
|
|
269
|
+
const columnKeys = new Set();
|
|
270
|
+
const cardKeys = new Set();
|
|
271
|
+
let cards = 0;
|
|
272
|
+
for (const [c, column] of node.columns.entries()) {
|
|
273
|
+
if (columnKeys.has(column.key)) {
|
|
274
|
+
ctx.addIssue({
|
|
275
|
+
code: 'custom',
|
|
276
|
+
path: ['columns', c, 'key'],
|
|
277
|
+
message: `duplicate column key "${column.key}"`,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
columnKeys.add(column.key);
|
|
281
|
+
for (const [i, card] of column.cards.entries()) {
|
|
282
|
+
if (cardKeys.has(card.key)) {
|
|
283
|
+
ctx.addIssue({
|
|
284
|
+
code: 'custom',
|
|
285
|
+
path: ['columns', c, 'cards', i, 'key'],
|
|
286
|
+
message: `duplicate card key "${card.key}"`,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
cardKeys.add(card.key);
|
|
290
|
+
}
|
|
291
|
+
cards += column.cards.length;
|
|
292
|
+
}
|
|
293
|
+
if (cards > 500) {
|
|
294
|
+
ctx.addIssue({
|
|
295
|
+
code: 'custom',
|
|
296
|
+
path: ['columns'],
|
|
297
|
+
message: `a board holds at most 500 cards, got ${cards}`,
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
});
|
|
236
301
|
/**
|
|
237
302
|
* A horizontal line that says where one part of a screen ends and the next
|
|
238
303
|
* begins. It carries no content. Use a `section` when the parts need a title.
|
|
@@ -362,12 +427,15 @@ const leafNodeSchema = z.discriminatedUnion('type', [
|
|
|
362
427
|
detailsSchema,
|
|
363
428
|
timestampSchema,
|
|
364
429
|
dividerSchema,
|
|
430
|
+
boardSchema,
|
|
365
431
|
]);
|
|
366
432
|
const MAX_SECTION_DEPTH = 8;
|
|
367
433
|
const nodeSchemas = new Map();
|
|
368
434
|
/** Shared by the chain and by the root union, so the two cannot drift apart. */
|
|
369
435
|
const sectionBranch = (children) => z.object({
|
|
370
436
|
type: z.literal('section'),
|
|
437
|
+
/** Stable identity for presentation regions that reference this root section. */
|
|
438
|
+
key: slug.optional(),
|
|
371
439
|
title: z.string().max(120).optional(),
|
|
372
440
|
description: z.string().max(300).optional(),
|
|
373
441
|
/**
|
|
@@ -448,7 +516,26 @@ export const nodeSchema = z.discriminatedUnion('type', [
|
|
|
448
516
|
sectionBranch(z.lazy(() => nodeSchemaAtDepth(1))),
|
|
449
517
|
tabsBranch(z.lazy(() => nodeSchemaAtDepth(1))),
|
|
450
518
|
]);
|
|
451
|
-
export const
|
|
519
|
+
export const screenPresentationSchema = z.object({
|
|
520
|
+
/** A full-height working surface whose chrome stays outside its content scroller. */
|
|
521
|
+
layout: z.literal('workspace'),
|
|
522
|
+
/** Where the renderer places the screen's page-level actions. */
|
|
523
|
+
actionPlacement: z.enum(['header', 'footer']).default('header'),
|
|
524
|
+
/**
|
|
525
|
+
* Promote one keyed root section into a supporting right rail. Renderers own
|
|
526
|
+
* its responsive width and visual treatment; modules only declare meaning.
|
|
527
|
+
*/
|
|
528
|
+
rightRail: z
|
|
529
|
+
.object({
|
|
530
|
+
sectionKey: slug,
|
|
531
|
+
label: z.string().min(1).max(80),
|
|
532
|
+
collapsible: z.boolean().default(false),
|
|
533
|
+
defaultCollapsed: z.boolean().default(false),
|
|
534
|
+
})
|
|
535
|
+
.optional(),
|
|
536
|
+
});
|
|
537
|
+
export const screenTreeSchema = z
|
|
538
|
+
.object({
|
|
452
539
|
version: z.literal(UI_SCHEMA_VERSION),
|
|
453
540
|
title: z.string().max(120),
|
|
454
541
|
description: z.string().max(300).optional(),
|
|
@@ -471,12 +558,34 @@ export const screenTreeSchema = z.object({
|
|
|
471
558
|
.min(1)
|
|
472
559
|
.max(5)
|
|
473
560
|
.optional(),
|
|
474
|
-
/** Page-level actions
|
|
561
|
+
/** Page-level actions. Presentation chooses header or pinned workspace footer. */
|
|
475
562
|
actions: z.array(buttonSchema).max(4).optional(),
|
|
476
563
|
children: z.array(nodeSchema).max(100),
|
|
564
|
+
/** Optional platform-owned layout semantics. Absence preserves the document layout. */
|
|
565
|
+
presentation: screenPresentationSchema.optional(),
|
|
477
566
|
// ponytail: polling keeps live views (agent activity) simple; switch to the
|
|
478
567
|
// existing run-event NDJSON stream when polling measurably falls short.
|
|
479
568
|
refreshSeconds: z.number().int().min(2).max(300).optional(),
|
|
569
|
+
})
|
|
570
|
+
.superRefine((tree, ctx) => {
|
|
571
|
+
const rail = tree.presentation?.rightRail;
|
|
572
|
+
if (!rail)
|
|
573
|
+
return;
|
|
574
|
+
if (rail.defaultCollapsed && !rail.collapsible) {
|
|
575
|
+
ctx.addIssue({
|
|
576
|
+
code: 'custom',
|
|
577
|
+
path: ['presentation', 'rightRail', 'defaultCollapsed'],
|
|
578
|
+
message: 'requires collapsible to be true',
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
const matches = tree.children.filter((node) => node.type === 'section' && node.key === rail.sectionKey);
|
|
582
|
+
if (matches.length !== 1) {
|
|
583
|
+
ctx.addIssue({
|
|
584
|
+
code: 'custom',
|
|
585
|
+
path: ['presentation', 'rightRail', 'sectionKey'],
|
|
586
|
+
message: 'must identify exactly one keyed root section',
|
|
587
|
+
});
|
|
588
|
+
}
|
|
480
589
|
});
|
|
481
590
|
/** What an action handler returns. `refresh` re-runs `load` after the action. */
|
|
482
591
|
export const actionResultSchema = z.object({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keemakr/ui-sdk",
|
|
3
|
-
"version": "1.4.0-dev-
|
|
3
|
+
"version": "1.4.0-dev-20261012082811",
|
|
4
4
|
"description": "Product screens for keemakr marketplace modules: a versioned, server-driven UI tree schema plus defineScreen() handlers. Module code never ships to the browser — keemakr-core validates the tree and renders it with platform components.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|