@keemakr/ui-sdk 1.6.0-dev-20261027202014 → 1.6.0-dev-20261027204717

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/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { UI_SCHEMA_VERSION, pressSchema, chatPressSchema, valuePressSchema, NAVIGATE_OPENS, buttonSchema, cellSchema, tableCellSchema, dateFormatSchema, nodeSchema, screenPresentationSchema, screenTreeSchema, actionResultSchema, screenLoadRequestSchema, screenLoadResponseSchema, screenActionRequestSchema, interfaceFamilySchema, surfaceDefinitionSchema, surfaceSchema, screenRegistrationSchema, uiManifestSchema, jobIdSchema, jobStatusSchema, screenJobRequestSchema, mediaPathSchema, seekPointSchema, seekPressSchema, BADGE_COUNT_MAX, surfaceBadgeSchema, screenBadgesRequestSchema, screenBadgesResponseSchema, TABS_MAX, } from './schema.js';
1
+ export { UI_SCHEMA_VERSION, pressSchema, chatPressSchema, valuePressSchema, NAVIGATE_OPENS, buttonSchema, cellSchema, factCellSchema, tableCellSchema, dateFormatSchema, nodeSchema, screenPresentationSchema, screenTreeSchema, actionResultSchema, screenLoadRequestSchema, screenLoadResponseSchema, screenActionRequestSchema, interfaceFamilySchema, surfaceDefinitionSchema, surfaceSchema, screenRegistrationSchema, uiManifestSchema, jobIdSchema, jobStatusSchema, screenJobRequestSchema, mediaPathSchema, seekPointSchema, seekPressSchema, BADGE_COUNT_MAX, surfaceBadgeSchema, screenBadgesRequestSchema, screenBadgesResponseSchema, TABS_MAX, } from './schema.js';
2
2
  export type { Press, ChatPress, SeekPress, SeekPoint, ValuePress, NavigatePress, NavigateOpen, ScreenPresentation, ScreenNode, ScreenRootNode, ScreenTree, ActionResult, InterfaceFamily, SurfaceDefinition, Surface, ScreenRegistration, UiManifest, ScreenTreeInput, ActionResultInput, JobStatus, JobStatusInput, SurfaceBadge, SurfaceBadgeInput, ScreenBadgesRequest, ScreenBadgesResponse, } from './schema.js';
3
3
  export { BADGE_DISPLAY_MAX, answerBadges, badgeSpokenLabel, badgeText, defineBadges, parseScreenBadges, } from './badges.js';
4
4
  export type { BadgesHandler } from './badges.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, chatPressSchema, valuePressSchema, NAVIGATE_OPENS, buttonSchema, cellSchema, tableCellSchema, dateFormatSchema, nodeSchema, screenPresentationSchema, screenTreeSchema, actionResultSchema, screenLoadRequestSchema, screenLoadResponseSchema, screenActionRequestSchema, interfaceFamilySchema, surfaceDefinitionSchema, surfaceSchema, screenRegistrationSchema, uiManifestSchema, jobIdSchema, jobStatusSchema, screenJobRequestSchema, mediaPathSchema, seekPointSchema, seekPressSchema, BADGE_COUNT_MAX, surfaceBadgeSchema, screenBadgesRequestSchema, screenBadgesResponseSchema, TABS_MAX, } from './schema.js';
9
+ export { UI_SCHEMA_VERSION, pressSchema, chatPressSchema, valuePressSchema, NAVIGATE_OPENS, buttonSchema, cellSchema, factCellSchema, tableCellSchema, dateFormatSchema, nodeSchema, screenPresentationSchema, screenTreeSchema, actionResultSchema, screenLoadRequestSchema, screenLoadResponseSchema, screenActionRequestSchema, interfaceFamilySchema, surfaceDefinitionSchema, surfaceSchema, screenRegistrationSchema, uiManifestSchema, jobIdSchema, jobStatusSchema, screenJobRequestSchema, mediaPathSchema, seekPointSchema, seekPressSchema, BADGE_COUNT_MAX, surfaceBadgeSchema, screenBadgesRequestSchema, screenBadgesResponseSchema, TABS_MAX, } from './schema.js';
10
10
  export { BADGE_DISPLAY_MAX, answerBadges, badgeSpokenLabel, badgeText, defineBadges, parseScreenBadges, } from './badges.js';
11
11
  export { CHART_POINTS_MAX, thinChart, thinnedIndices } from './chart.js';
12
12
  export { TABS_MAX_1_4_0, foldTabs, onlyTabParamsDiffer, openingTab, tabParamsOf } from './tabs.js';
@@ -75,8 +75,12 @@ import type { ChatPress, ScreenRootNode, ScreenTree } from './schema.js';
75
75
  * (newer than any published release; core's own renderer draws it). An older parse strips it,
76
76
  * so without it the action becomes a `button` with the same label and press, last in the
77
77
  * section, and the reader can still go where it led. A full section drops it.
78
+ * - `facts.meter`: a `meter` cell as a calendar entry's `details` value or a header `meta` value
79
+ * (newer than any published release; core's own renderer draws it). An older parse refuses it,
80
+ * so without it the meter becomes a `text` cell with its value label ("Intake 3/5"). Header
81
+ * facts a client draws as one line (no `header.meta`) are written the same way.
78
82
  */
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';
83
+ 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' | 'facts.meter';
80
84
  /** The node types and features one ui-sdk release, or one client, draws. */
81
85
  export interface NodeSet {
82
86
  /** For messages: "ui-sdk 1.4.0", "mobile 0.2.5". */
@@ -113,7 +117,7 @@ export declare const UI_SDK_SNAPSHOTS: {
113
117
  readonly current: {
114
118
  readonly name: "ui-sdk (this package)";
115
119
  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"];
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"];
120
+ 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", "facts.meter"];
117
121
  };
118
122
  };
119
123
  export type UiSdkSnapshot = keyof typeof UI_SDK_SNAPSHOTS;
package/dist/negotiate.js CHANGED
@@ -20,7 +20,7 @@
20
20
  // (tests/sdk/ui-negotiation.test.ts) fails until the current snapshot matches KNOWN_NODE_TYPES,
21
21
  // until every node type has a fallback, and until every sample tree, negotiated for every row,
22
22
  // parses with that row's published schema with no safety field lost.
23
- import { foldTableCell, isNewerTableCell } from './cells.js';
23
+ import { foldTableCell, isNewerTableCell, meterLabel } from './cells.js';
24
24
  import { conformTree } from './conform.js';
25
25
  import { withoutScreenLinks } from './markdown-links.js';
26
26
  import { pageRangeLabel } from './pager.js';
@@ -52,6 +52,7 @@ const ALL_FEATURES = [
52
52
  'timeline.time_tag',
53
53
  'stat.bar',
54
54
  'section.action',
55
+ 'facts.meter',
55
56
  ];
56
57
  /** The nodes of ui-sdk 1.1.0 and 1.2.0 (one schema). */
57
58
  const NODES_1_1_0 = [
@@ -400,6 +401,22 @@ export function negotiateTree(tree, set) {
400
401
  return features.has('choice.action') ? node : foldChoiceActions(node, path, fallbacks);
401
402
  case 'timeline':
402
403
  return features.has('timeline.time_tag') ? node : foldTimeAndTag(node, path, fallbacks);
404
+ case 'calendar': {
405
+ if (features.has('facts.meter'))
406
+ return node;
407
+ let changed = false;
408
+ const events = node.events.map((event, i) => {
409
+ if (!event.details)
410
+ return event;
411
+ const at = `${path}.events[${i}].details`;
412
+ const details = foldFactMeters(event.details, at, 'calendar', fallbacks);
413
+ if (details === event.details)
414
+ return event;
415
+ changed = true;
416
+ return { ...event, details };
417
+ });
418
+ return changed ? { ...node, events } : node;
419
+ }
403
420
  case 'markdown': {
404
421
  if (features.has('markdown.links'))
405
422
  return node;
@@ -501,6 +518,11 @@ export function negotiateTree(tree, set) {
501
518
  delete next.actions;
502
519
  }
503
520
  }
521
+ if (tree.meta && features.has('header.meta') && !features.has('facts.meter')) {
522
+ const meta = foldFactMeters(tree.meta, 'meta', 'header', fallbacks);
523
+ if (meta !== tree.meta)
524
+ next = { ...next, meta };
525
+ }
504
526
  // Before the breadcrumbs fold, so a trail line (which says where the screen sits) lands above it.
505
527
  if ((tree.badges || tree.meta) && !features.has('header.meta')) {
506
528
  const line = headerFactsLine(tree, zone);
@@ -621,6 +643,21 @@ export function foldChoiceActions(node, path, fallbacks) {
621
643
  });
622
644
  return { ...node, choices };
623
645
  }
646
+ /**
647
+ * Facts with each `meter` value as a `text` cell of its value label, for a client that draws no
648
+ * meter outside a table (`facts.meter`). The same array when there is none.
649
+ */
650
+ function foldFactMeters(facts, path, type, fallbacks) {
651
+ let changed = false;
652
+ const folded = facts.map((fact, i) => {
653
+ if (fact.value.kind !== 'meter')
654
+ return fact;
655
+ changed = true;
656
+ fallbacks.push({ path: `${path}[${i}].value`, type, reason: 'facts.meter', as: 'folded' });
657
+ return { ...fact, value: { kind: 'text', value: meterLabel(fact.value) } };
658
+ });
659
+ return changed ? folded : facts;
660
+ }
624
661
  /**
625
662
  * A section with its `action` moved into its body as a last `button`, for a client that does not
626
663
  * draw the action on the title line (`section.action`). Dropped when the section is full.
@@ -1091,6 +1128,8 @@ const TONE_WORD = {
1091
1128
  destructive: 'Error',
1092
1129
  };
1093
1130
  function cellText(cell, timeZone) {
1131
+ if (cell.kind === 'meter')
1132
+ return meterLabel(cell);
1094
1133
  return cell.kind === 'date' ? formatMoment(cell.value, 'datetime', timeZone) : cell.value;
1095
1134
  }
1096
1135
  /**
package/dist/schema.d.ts CHANGED
@@ -250,8 +250,8 @@ export declare const cellSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
250
250
  }, z.core.$strip>], "kind">;
251
251
  /**
252
252
  * A table cell: every value cell kind, plus `sparkline` and `meter`, and a
253
- * `secondary` line on `text` and `badge`. Table cells only: a `details` row, a
254
- * board card and a calendar entry keep `cellSchema`.
253
+ * `secondary` line on `text` and `badge`. Table cells only: a `details` row and a board card
254
+ * keep `cellSchema`; a calendar entry's details and the header's meta take `factCellSchema`.
255
255
  *
256
256
  * Neither a sparkline nor a meter is sortable by itself: the renderer never
257
257
  * reorders rows, so a sortable column of them sorts by whatever the module
@@ -320,6 +320,55 @@ export declare const tableCellSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
320
320
  }>>;
321
321
  valueLabel: z.ZodOptional<z.ZodString>;
322
322
  }, z.core.$strip>], "kind">;
323
+ /**
324
+ * A fact's value: a value cell, or a meter ("Intake 3/5"). A calendar entry's `details` and the
325
+ * screen header's `meta` take it. A client that draws no meter there gets a `text` cell with the
326
+ * meter's value label (negotiate.ts, `facts.meter`).
327
+ */
328
+ export declare const factCellSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
329
+ kind: z.ZodLiteral<"text">;
330
+ value: z.ZodString;
331
+ }, z.core.$strip>, z.ZodObject<{
332
+ kind: z.ZodLiteral<"badge">;
333
+ value: z.ZodString;
334
+ tone: z.ZodDefault<z.ZodEnum<{
335
+ default: "default";
336
+ success: "success";
337
+ warning: "warning";
338
+ destructive: "destructive";
339
+ info: "info";
340
+ accent: "accent";
341
+ }>>;
342
+ outline: z.ZodOptional<z.ZodBoolean>;
343
+ dot: z.ZodOptional<z.ZodBoolean>;
344
+ hint: z.ZodOptional<z.ZodString>;
345
+ }, z.core.$strip>, z.ZodObject<{
346
+ kind: z.ZodLiteral<"date">;
347
+ value: z.ZodISODateTime;
348
+ dateFormat: z.ZodOptional<z.ZodObject<{
349
+ weekday: z.ZodOptional<z.ZodBoolean>;
350
+ year: z.ZodOptional<z.ZodEnum<{
351
+ auto: "auto";
352
+ always: "always";
353
+ never: "never";
354
+ }>>;
355
+ hour12: z.ZodOptional<z.ZodBoolean>;
356
+ locale: z.ZodOptional<z.ZodString>;
357
+ }, z.core.$strip>>;
358
+ }, z.core.$strip>, z.ZodObject<{
359
+ kind: z.ZodLiteral<"meter">;
360
+ value: z.ZodNumber;
361
+ max: z.ZodDefault<z.ZodNumber>;
362
+ tone: z.ZodDefault<z.ZodEnum<{
363
+ default: "default";
364
+ success: "success";
365
+ warning: "warning";
366
+ destructive: "destructive";
367
+ info: "info";
368
+ accent: "accent";
369
+ }>>;
370
+ valueLabel: z.ZodOptional<z.ZodString>;
371
+ }, z.core.$strip>], "kind">;
323
372
  /**
324
373
  * A file inside the module's own runtime: "patients/p-17/scan.jpg". Never a
325
374
  * URL. Core fetches it from the runtime's GET /screens/media/<path> with the
@@ -1698,6 +1747,19 @@ declare const leafNodeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
1698
1747
  hour12: z.ZodOptional<z.ZodBoolean>;
1699
1748
  locale: z.ZodOptional<z.ZodString>;
1700
1749
  }, z.core.$strip>>;
1750
+ }, z.core.$strip>, z.ZodObject<{
1751
+ kind: z.ZodLiteral<"meter">;
1752
+ value: z.ZodNumber;
1753
+ max: z.ZodDefault<z.ZodNumber>;
1754
+ tone: z.ZodDefault<z.ZodEnum<{
1755
+ default: "default";
1756
+ success: "success";
1757
+ warning: "warning";
1758
+ destructive: "destructive";
1759
+ info: "info";
1760
+ accent: "accent";
1761
+ }>>;
1762
+ valueLabel: z.ZodOptional<z.ZodString>;
1701
1763
  }, z.core.$strip>], "kind">;
1702
1764
  }, z.core.$strip>>>;
1703
1765
  actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -2090,6 +2152,19 @@ export declare const screenTreeSchema: z.ZodObject<{
2090
2152
  hour12: z.ZodOptional<z.ZodBoolean>;
2091
2153
  locale: z.ZodOptional<z.ZodString>;
2092
2154
  }, z.core.$strip>>;
2155
+ }, z.core.$strip>, z.ZodObject<{
2156
+ kind: z.ZodLiteral<"meter">;
2157
+ value: z.ZodNumber;
2158
+ max: z.ZodDefault<z.ZodNumber>;
2159
+ tone: z.ZodDefault<z.ZodEnum<{
2160
+ default: "default";
2161
+ success: "success";
2162
+ warning: "warning";
2163
+ destructive: "destructive";
2164
+ info: "info";
2165
+ accent: "accent";
2166
+ }>>;
2167
+ valueLabel: z.ZodOptional<z.ZodString>;
2093
2168
  }, z.core.$strip>], "kind">;
2094
2169
  }, z.core.$strip>>>;
2095
2170
  breadcrumbs: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -2318,6 +2393,19 @@ export declare const actionResultSchema: z.ZodObject<{
2318
2393
  hour12: z.ZodOptional<z.ZodBoolean>;
2319
2394
  locale: z.ZodOptional<z.ZodString>;
2320
2395
  }, z.core.$strip>>;
2396
+ }, z.core.$strip>, z.ZodObject<{
2397
+ kind: z.ZodLiteral<"meter">;
2398
+ value: z.ZodNumber;
2399
+ max: z.ZodDefault<z.ZodNumber>;
2400
+ tone: z.ZodDefault<z.ZodEnum<{
2401
+ default: "default";
2402
+ success: "success";
2403
+ warning: "warning";
2404
+ destructive: "destructive";
2405
+ info: "info";
2406
+ accent: "accent";
2407
+ }>>;
2408
+ valueLabel: z.ZodOptional<z.ZodString>;
2321
2409
  }, z.core.$strip>], "kind">;
2322
2410
  }, z.core.$strip>>>;
2323
2411
  breadcrumbs: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -2539,6 +2627,19 @@ export declare const jobStatusSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
2539
2627
  hour12: z.ZodOptional<z.ZodBoolean>;
2540
2628
  locale: z.ZodOptional<z.ZodString>;
2541
2629
  }, z.core.$strip>>;
2630
+ }, z.core.$strip>, z.ZodObject<{
2631
+ kind: z.ZodLiteral<"meter">;
2632
+ value: z.ZodNumber;
2633
+ max: z.ZodDefault<z.ZodNumber>;
2634
+ tone: z.ZodDefault<z.ZodEnum<{
2635
+ default: "default";
2636
+ success: "success";
2637
+ warning: "warning";
2638
+ destructive: "destructive";
2639
+ info: "info";
2640
+ accent: "accent";
2641
+ }>>;
2642
+ valueLabel: z.ZodOptional<z.ZodString>;
2542
2643
  }, z.core.$strip>], "kind">;
2543
2644
  }, z.core.$strip>>>;
2544
2645
  breadcrumbs: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -2750,6 +2851,19 @@ export declare const screenLoadResponseSchema: z.ZodObject<{
2750
2851
  hour12: z.ZodOptional<z.ZodBoolean>;
2751
2852
  locale: z.ZodOptional<z.ZodString>;
2752
2853
  }, z.core.$strip>>;
2854
+ }, z.core.$strip>, z.ZodObject<{
2855
+ kind: z.ZodLiteral<"meter">;
2856
+ value: z.ZodNumber;
2857
+ max: z.ZodDefault<z.ZodNumber>;
2858
+ tone: z.ZodDefault<z.ZodEnum<{
2859
+ default: "default";
2860
+ success: "success";
2861
+ warning: "warning";
2862
+ destructive: "destructive";
2863
+ info: "info";
2864
+ accent: "accent";
2865
+ }>>;
2866
+ valueLabel: z.ZodOptional<z.ZodString>;
2753
2867
  }, z.core.$strip>], "kind">;
2754
2868
  }, z.core.$strip>>>;
2755
2869
  breadcrumbs: z.ZodOptional<z.ZodArray<z.ZodObject<{
package/dist/schema.js CHANGED
@@ -297,6 +297,17 @@ export const cellSchema = z.discriminatedUnion('kind', [
297
297
  badgeCellSchema,
298
298
  dateCellSchema,
299
299
  ]);
300
+ /** A bar of `value` out of `max`, its value always written beside it. */
301
+ const meterCellSchema = z.object({
302
+ kind: z.literal('meter'),
303
+ /** A value past `max` draws a full bar; say so in `valueLabel`. */
304
+ value: z.number().min(0),
305
+ max: z.number().positive().default(100),
306
+ /** The bar's colour. */
307
+ tone: tone.default('default'),
308
+ /** Drawn beside the bar: "72%", "36 of 50". Without it, the percent of `max`. */
309
+ valueLabel: z.string().min(1).max(40).optional(),
310
+ });
300
311
  /**
301
312
  * A muted second line under a table cell's value, one line, cut short with an
302
313
  * ellipsis when it does not fit: an account's city and tier under its name, the
@@ -305,8 +316,8 @@ export const cellSchema = z.discriminatedUnion('kind', [
305
316
  const cellSecondary = z.string().min(1).max(200).optional();
306
317
  /**
307
318
  * A table cell: every value cell kind, plus `sparkline` and `meter`, and a
308
- * `secondary` line on `text` and `badge`. Table cells only: a `details` row, a
309
- * board card and a calendar entry keep `cellSchema`.
319
+ * `secondary` line on `text` and `badge`. Table cells only: a `details` row and a board card
320
+ * keep `cellSchema`; a calendar entry's details and the header's meta take `factCellSchema`.
310
321
  *
311
322
  * Neither a sparkline nor a meter is sortable by itself: the renderer never
312
323
  * reorders rows, so a sortable column of them sorts by whatever the module
@@ -330,16 +341,18 @@ export const tableCellSchema = z.discriminatedUnion('kind', [
330
341
  /** What the line says, in words: "Usage down 18% in 30 days". Required. */
331
342
  label: z.string().min(1).max(120),
332
343
  }),
333
- z.object({
334
- kind: z.literal('meter'),
335
- /** A value past `max` draws a full bar; say so in `valueLabel`. */
336
- value: z.number().min(0),
337
- max: z.number().positive().default(100),
338
- /** The bar's colour. */
339
- tone: tone.default('default'),
340
- /** Drawn beside the bar: "72%", "36 of 50". Without it, the percent of `max`. */
341
- valueLabel: z.string().min(1).max(40).optional(),
342
- }),
344
+ meterCellSchema,
345
+ ]);
346
+ /**
347
+ * A fact's value: a value cell, or a meter ("Intake 3/5"). A calendar entry's `details` and the
348
+ * screen header's `meta` take it. A client that draws no meter there gets a `text` cell with the
349
+ * meter's value label (negotiate.ts, `facts.meter`).
350
+ */
351
+ export const factCellSchema = z.discriminatedUnion('kind', [
352
+ textCellSchema,
353
+ badgeCellSchema,
354
+ dateCellSchema,
355
+ meterCellSchema,
343
356
  ]);
344
357
  /**
345
358
  * Cards to choose from, or a list of things to act on (a My Day action list). Each card is a
@@ -855,8 +868,9 @@ const calendarSchema = z
855
868
  /** Colours the entry: a confirmed visit, an unconfirmed one, a no-show. */
856
869
  tone: tone.default('default'),
857
870
  subtitle: z.string().max(200).optional(),
871
+ /** Drawn in the entry's panel. A `meter` value shows progress: "Intake 3/5". */
858
872
  details: z
859
- .array(z.object({ label: z.string().min(1).max(80), value: cellSchema }))
873
+ .array(z.object({ label: z.string().min(1).max(80), value: factCellSchema }))
860
874
  .max(12)
861
875
  .optional(),
862
876
  actions: z.array(buttonSchema).max(4).optional(),
@@ -2496,12 +2510,13 @@ export const screenTreeSchema = z
2496
2510
  /**
2497
2511
  * Up to eight facts drawn as a compact row under the title (and its
2498
2512
  * description): ARR, renewal date, reps, NPS. Each value is a cell, so a
2499
- * `date` follows the screen's `timeZone` and `dateFormat` and a `badge`
2500
- * keeps its tone. A renderer draws them as a description list (term and
2501
- * value) that wraps on a narrow screen.
2513
+ * `date` follows the screen's `timeZone` and `dateFormat`, a `badge`
2514
+ * keeps its tone and a `meter` draws its bar ("Onboarding 3/5"). A renderer
2515
+ * draws them as a description list (term and value) that wraps on a narrow
2516
+ * screen.
2502
2517
  */
2503
2518
  meta: z
2504
- .array(z.object({ label: z.string().min(1).max(40), value: cellSchema }))
2519
+ .array(z.object({ label: z.string().min(1).max(40), value: factCellSchema }))
2505
2520
  .min(1)
2506
2521
  .max(8)
2507
2522
  .optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keemakr/ui-sdk",
3
- "version": "1.6.0-dev-20261027202014",
3
+ "version": "1.6.0-dev-20261027204717",
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",