@keemakr/ui-sdk 1.6.0-dev-20261027083656 → 1.6.0-dev-20261027085115
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/dist/negotiate.d.ts +6 -2
- package/dist/negotiate.js +41 -0
- package/dist/schema.d.ts +26 -0
- package/dist/schema.js +40 -0
- package/package.json +1 -1
package/dist/negotiate.d.ts
CHANGED
|
@@ -71,8 +71,12 @@ import type { ChatPress, ScreenRootNode, ScreenTree } from './schema.js';
|
|
|
71
71
|
* published release; core's own renderer draws it). An older parse strips it without a sign,
|
|
72
72
|
* so without it the bar's legend ("Red 8 · Amber 13 · Green 29") is written into the tile's
|
|
73
73
|
* `hint`, after any hint it had, and the bar is dropped.
|
|
74
|
+
* - `section.action`: a section's `action`, a press drawn at the right end of its title line
|
|
75
|
+
* (newer than any published release; core's own renderer draws it). An older parse strips it,
|
|
76
|
+
* so without it the action becomes a `button` with the same label and press, last in the
|
|
77
|
+
* section, and the reader can still go where it led. A full section drops it.
|
|
74
78
|
*/
|
|
75
|
-
export type UiFeature = 'breadcrumbs' | 'button.disabled' | 'section.row' | 'form.field_kinds' | 'form.confirm' | 'press.chat' | 'form.radio' | 'table.cells' | 'header.meta' | 'tabs.many' | 'choice.action' | 'markdown.links' | 'form.disabled' | 'press.seek' | 'edit.inline' | 'timeline.time_tag' | 'stat.bar';
|
|
79
|
+
export type UiFeature = 'breadcrumbs' | 'button.disabled' | 'section.row' | 'form.field_kinds' | 'form.confirm' | 'press.chat' | 'form.radio' | 'table.cells' | 'header.meta' | 'tabs.many' | 'choice.action' | 'markdown.links' | 'form.disabled' | 'press.seek' | 'edit.inline' | 'timeline.time_tag' | 'stat.bar' | 'section.action';
|
|
76
80
|
/** The node types and features one ui-sdk release, or one client, draws. */
|
|
77
81
|
export interface NodeSet {
|
|
78
82
|
/** For messages: "ui-sdk 1.4.0", "mobile 0.2.5". */
|
|
@@ -109,7 +113,7 @@ export declare const UI_SDK_SNAPSHOTS: {
|
|
|
109
113
|
readonly current: {
|
|
110
114
|
readonly name: "ui-sdk (this package)";
|
|
111
115
|
readonly nodes: readonly ["heading", "text", "markdown", "stat", "button", "form", "table", "timeline", "pending_request", "pending_requests", "choice_cards", "progress", "chart", "section", "tabs", "pagination", "select", "alert", "details", "timestamp", "divider", "board", "link", "tags", "toggle", "segmented", "progress_bar", "conversation", "composer", "calendar", "image", "video", "stacked_bar", "heatmap", "audio"];
|
|
112
|
-
readonly features: readonly ["breadcrumbs", "button.disabled", "section.row", "form.field_kinds", "form.confirm", "press.chat", "form.radio", "table.cells", "header.meta", "tabs.many", "choice.action", "markdown.links", "form.disabled", "press.seek", "edit.inline", "timeline.time_tag", "stat.bar"];
|
|
116
|
+
readonly features: readonly ["breadcrumbs", "button.disabled", "section.row", "form.field_kinds", "form.confirm", "press.chat", "form.radio", "table.cells", "header.meta", "tabs.many", "choice.action", "markdown.links", "form.disabled", "press.seek", "edit.inline", "timeline.time_tag", "stat.bar", "section.action"];
|
|
113
117
|
};
|
|
114
118
|
};
|
|
115
119
|
export type UiSdkSnapshot = keyof typeof UI_SDK_SNAPSHOTS;
|
package/dist/negotiate.js
CHANGED
|
@@ -51,6 +51,7 @@ const ALL_FEATURES = [
|
|
|
51
51
|
'edit.inline',
|
|
52
52
|
'timeline.time_tag',
|
|
53
53
|
'stat.bar',
|
|
54
|
+
'section.action',
|
|
54
55
|
];
|
|
55
56
|
/** The nodes of ui-sdk 1.1.0 and 1.2.0 (one schema). */
|
|
56
57
|
const NODES_1_1_0 = [
|
|
@@ -339,6 +340,9 @@ export function negotiateTree(tree, set) {
|
|
|
339
340
|
fallbacks.push({ path, type: 'section', reason: 'section.row', as: 'folded' });
|
|
340
341
|
next = { ...next, layout: 'stack' };
|
|
341
342
|
}
|
|
343
|
+
if (next.action && !features.has('section.action')) {
|
|
344
|
+
next = foldSectionAction(next, path, fallbacks);
|
|
345
|
+
}
|
|
342
346
|
return next;
|
|
343
347
|
}
|
|
344
348
|
case 'form': {
|
|
@@ -617,6 +621,38 @@ export function foldChoiceActions(node, path, fallbacks) {
|
|
|
617
621
|
});
|
|
618
622
|
return { ...node, choices };
|
|
619
623
|
}
|
|
624
|
+
/**
|
|
625
|
+
* A section with its `action` moved into its body as a last `button`, for a client that does not
|
|
626
|
+
* draw the action on the title line (`section.action`). Dropped when the section is full.
|
|
627
|
+
*/
|
|
628
|
+
function foldSectionAction(node, path, fallbacks) {
|
|
629
|
+
const { action, ...rest } = node;
|
|
630
|
+
if (!action)
|
|
631
|
+
return node;
|
|
632
|
+
if (rest.children.length >= CHILDREN_MAX) {
|
|
633
|
+
fallbacks.push({
|
|
634
|
+
path: `${path}.action`,
|
|
635
|
+
type: 'section',
|
|
636
|
+
reason: 'section.action',
|
|
637
|
+
as: 'dropped',
|
|
638
|
+
});
|
|
639
|
+
return rest;
|
|
640
|
+
}
|
|
641
|
+
fallbacks.push({
|
|
642
|
+
path: `${path}.action`,
|
|
643
|
+
type: 'section',
|
|
644
|
+
reason: 'section.action',
|
|
645
|
+
as: 'folded',
|
|
646
|
+
});
|
|
647
|
+
const button = {
|
|
648
|
+
type: 'button',
|
|
649
|
+
label: action.label,
|
|
650
|
+
onPress: action.onPress,
|
|
651
|
+
variant: 'secondary',
|
|
652
|
+
disabled: false,
|
|
653
|
+
};
|
|
654
|
+
return { ...rest, children: [...rest.children, button] };
|
|
655
|
+
}
|
|
620
656
|
/**
|
|
621
657
|
* The node as a client that draws no edit control should get it (`edit.inline`): the same block,
|
|
622
658
|
* read-only. Drops `edit` from a `text`, `markdown` or `details` block and its items, and from each
|
|
@@ -687,6 +723,11 @@ function withoutPresses(node, path, fallbacks, matches, reason) {
|
|
|
687
723
|
return node;
|
|
688
724
|
dropped(`${path}.action`, 'button');
|
|
689
725
|
return omit(node, 'action');
|
|
726
|
+
case 'section':
|
|
727
|
+
if (!node.action || !matches(node.action.onPress))
|
|
728
|
+
return node;
|
|
729
|
+
dropped(`${path}.action`, 'section');
|
|
730
|
+
return omit(node, 'action');
|
|
690
731
|
case 'image':
|
|
691
732
|
if (!matches(node.onPress))
|
|
692
733
|
return node;
|
package/dist/schema.d.ts
CHANGED
|
@@ -1919,6 +1919,11 @@ export type ScreenNode = LeafScreenNode | {
|
|
|
1919
1919
|
badge?: string;
|
|
1920
1920
|
badgeTone?: 'default' | 'success' | 'warning' | 'destructive' | 'info' | 'accent';
|
|
1921
1921
|
icon?: UiIcon;
|
|
1922
|
+
action?: {
|
|
1923
|
+
label: string;
|
|
1924
|
+
onPress: Press;
|
|
1925
|
+
};
|
|
1926
|
+
tone?: 'default' | 'success' | 'warning' | 'destructive' | 'info' | 'accent';
|
|
1922
1927
|
children: ScreenNode[];
|
|
1923
1928
|
};
|
|
1924
1929
|
type ScreenNodeInput = LeafScreenNodeInput | {
|
|
@@ -1939,6 +1944,11 @@ type ScreenNodeInput = LeafScreenNodeInput | {
|
|
|
1939
1944
|
badge?: string;
|
|
1940
1945
|
badgeTone?: 'default' | 'success' | 'warning' | 'destructive' | 'info' | 'accent';
|
|
1941
1946
|
icon?: UiIcon;
|
|
1947
|
+
action?: {
|
|
1948
|
+
label: string;
|
|
1949
|
+
onPress: z.input<typeof pressSchema>;
|
|
1950
|
+
};
|
|
1951
|
+
tone?: 'default' | 'success' | 'warning' | 'destructive' | 'info' | 'accent';
|
|
1942
1952
|
children: ScreenNodeInput[];
|
|
1943
1953
|
};
|
|
1944
1954
|
/**
|
|
@@ -2044,6 +2054,10 @@ export declare const screenTreeSchema: z.ZodObject<{
|
|
|
2044
2054
|
dot: z.ZodOptional<z.ZodBoolean>;
|
|
2045
2055
|
hint: z.ZodOptional<z.ZodString>;
|
|
2046
2056
|
}, z.core.$strip>>>;
|
|
2057
|
+
avatar: z.ZodOptional<z.ZodObject<{
|
|
2058
|
+
initials: z.ZodString;
|
|
2059
|
+
src: z.ZodOptional<z.ZodString>;
|
|
2060
|
+
}, z.core.$strip>>;
|
|
2047
2061
|
meta: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
2048
2062
|
label: z.ZodString;
|
|
2049
2063
|
value: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -2268,6 +2282,10 @@ export declare const actionResultSchema: z.ZodObject<{
|
|
|
2268
2282
|
dot: z.ZodOptional<z.ZodBoolean>;
|
|
2269
2283
|
hint: z.ZodOptional<z.ZodString>;
|
|
2270
2284
|
}, z.core.$strip>>>;
|
|
2285
|
+
avatar: z.ZodOptional<z.ZodObject<{
|
|
2286
|
+
initials: z.ZodString;
|
|
2287
|
+
src: z.ZodOptional<z.ZodString>;
|
|
2288
|
+
}, z.core.$strip>>;
|
|
2271
2289
|
meta: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
2272
2290
|
label: z.ZodString;
|
|
2273
2291
|
value: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -2485,6 +2503,10 @@ export declare const jobStatusSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
2485
2503
|
dot: z.ZodOptional<z.ZodBoolean>;
|
|
2486
2504
|
hint: z.ZodOptional<z.ZodString>;
|
|
2487
2505
|
}, z.core.$strip>>>;
|
|
2506
|
+
avatar: z.ZodOptional<z.ZodObject<{
|
|
2507
|
+
initials: z.ZodString;
|
|
2508
|
+
src: z.ZodOptional<z.ZodString>;
|
|
2509
|
+
}, z.core.$strip>>;
|
|
2488
2510
|
meta: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
2489
2511
|
label: z.ZodString;
|
|
2490
2512
|
value: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -2692,6 +2714,10 @@ export declare const screenLoadResponseSchema: z.ZodObject<{
|
|
|
2692
2714
|
dot: z.ZodOptional<z.ZodBoolean>;
|
|
2693
2715
|
hint: z.ZodOptional<z.ZodString>;
|
|
2694
2716
|
}, z.core.$strip>>>;
|
|
2717
|
+
avatar: z.ZodOptional<z.ZodObject<{
|
|
2718
|
+
initials: z.ZodString;
|
|
2719
|
+
src: z.ZodOptional<z.ZodString>;
|
|
2720
|
+
}, z.core.$strip>>;
|
|
2695
2721
|
meta: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
2696
2722
|
label: z.ZodString;
|
|
2697
2723
|
value: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
package/dist/schema.js
CHANGED
|
@@ -2275,6 +2275,22 @@ const sectionShape = (children) => z.object({
|
|
|
2275
2275
|
* apart at a glance. Decorative: the title says what the section is. Needs a `title`.
|
|
2276
2276
|
*/
|
|
2277
2277
|
icon: z.enum(UI_ICONS).optional(),
|
|
2278
|
+
/**
|
|
2279
|
+
* A press at the right end of the title line: "All", "Open", "See all 13 amber". Drawn as a
|
|
2280
|
+
* quiet text button, so the label says where it goes. Needs a `title`.
|
|
2281
|
+
*/
|
|
2282
|
+
action: z
|
|
2283
|
+
.object({
|
|
2284
|
+
label: z.string().min(1).max(40),
|
|
2285
|
+
onPress: pressSchema,
|
|
2286
|
+
})
|
|
2287
|
+
.optional(),
|
|
2288
|
+
/**
|
|
2289
|
+
* Tints a `card` section in a tone: its border and a faint wash behind it, so one card reads
|
|
2290
|
+
* as marked (a red diagnosis on an account). Colour is never the only signal: the title says
|
|
2291
|
+
* why ("Red · onboarding stalled"). Needs `surface: 'card'`. Default `default`, no tint.
|
|
2292
|
+
*/
|
|
2293
|
+
tone: tone.optional(),
|
|
2278
2294
|
children: z.array(children).max(100),
|
|
2279
2295
|
});
|
|
2280
2296
|
/** The rules between a section's fields, which a field's own schema cannot state. */
|
|
@@ -2344,6 +2360,20 @@ function checkSection(node, ctx) {
|
|
|
2344
2360
|
message: 'a section icon sits before its title, so it needs a title',
|
|
2345
2361
|
});
|
|
2346
2362
|
}
|
|
2363
|
+
if (node.action !== undefined && !node.title) {
|
|
2364
|
+
ctx.addIssue({
|
|
2365
|
+
code: 'custom',
|
|
2366
|
+
path: ['action'],
|
|
2367
|
+
message: 'a section action sits on its title line, so it needs a title',
|
|
2368
|
+
});
|
|
2369
|
+
}
|
|
2370
|
+
if (node.tone !== undefined && node.surface !== 'card') {
|
|
2371
|
+
ctx.addIssue({
|
|
2372
|
+
code: 'custom',
|
|
2373
|
+
path: ['tone'],
|
|
2374
|
+
message: "a section tone tints a card, so it needs surface: 'card'",
|
|
2375
|
+
});
|
|
2376
|
+
}
|
|
2347
2377
|
}
|
|
2348
2378
|
const sectionBranch = (children) => sectionShape(children).superRefine(checkSection);
|
|
2349
2379
|
/**
|
|
@@ -2453,6 +2483,16 @@ export const screenTreeSchema = z
|
|
|
2453
2483
|
* only signal, so each label says the status in words ("At risk").
|
|
2454
2484
|
*/
|
|
2455
2485
|
badges: z.array(tagSchema).min(1).max(4).optional(),
|
|
2486
|
+
/**
|
|
2487
|
+
* A square before the title that says whose screen this is at a glance: an account's
|
|
2488
|
+
* initials, or its logo from the module's media. Decorative: the title names it. With an
|
|
2489
|
+
* avatar the header is drawn as one card (avatar, title and badges, facts, actions), the
|
|
2490
|
+
* top of a record's page. `initials` are drawn when there is no `src`, and while it loads
|
|
2491
|
+
* or when it fails.
|
|
2492
|
+
*/
|
|
2493
|
+
avatar: z
|
|
2494
|
+
.object({ initials: z.string().min(1).max(3), src: mediaPathSchema.optional() })
|
|
2495
|
+
.optional(),
|
|
2456
2496
|
/**
|
|
2457
2497
|
* Up to eight facts drawn as a compact row under the title (and its
|
|
2458
2498
|
* description): ARR, renewal date, reps, NPS. Each value is a cell, so a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keemakr/ui-sdk",
|
|
3
|
-
"version": "1.6.0-dev-
|
|
3
|
+
"version": "1.6.0-dev-20261027085115",
|
|
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",
|