@keemakr/ui-sdk 1.3.0 → 1.4.0-dev-20261012081643

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/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", "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
@@ -28,6 +28,8 @@ export const KNOWN_NODE_TYPES = [
28
28
  'alert',
29
29
  'details',
30
30
  'timestamp',
31
+ 'divider',
32
+ 'board',
31
33
  'section',
32
34
  'tabs',
33
35
  ];
package/dist/schema.d.ts CHANGED
@@ -42,6 +42,8 @@ export declare const buttonSchema: z.ZodObject<{
42
42
  secondary: "secondary";
43
43
  }>>;
44
44
  disabled: z.ZodDefault<z.ZodBoolean>;
45
+ hint: z.ZodOptional<z.ZodString>;
46
+ disabledReason: z.ZodOptional<z.ZodString>;
45
47
  }, z.core.$strip>;
46
48
  /** A table cell. Dates are ISO 8601; core formats them tenant-locale-aware. */
47
49
  export declare const cellSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -94,6 +96,8 @@ declare const leafNodeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
94
96
  secondary: "secondary";
95
97
  }>>;
96
98
  disabled: z.ZodDefault<z.ZodBoolean>;
99
+ hint: z.ZodOptional<z.ZodString>;
100
+ disabledReason: z.ZodOptional<z.ZodString>;
97
101
  }, z.core.$strip>, z.ZodObject<{
98
102
  type: z.ZodLiteral<"form">;
99
103
  key: z.ZodString;
@@ -185,6 +189,8 @@ declare const leafNodeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
185
189
  secondary: "secondary";
186
190
  }>>;
187
191
  disabled: z.ZodDefault<z.ZodBoolean>;
192
+ hint: z.ZodOptional<z.ZodString>;
193
+ disabledReason: z.ZodOptional<z.ZodString>;
188
194
  }, z.core.$strip>], "type">>;
189
195
  }, z.core.$strip>>;
190
196
  }, z.core.$strip>>;
@@ -287,6 +293,12 @@ declare const leafNodeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
287
293
  params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
288
294
  }, z.core.$strip>], "kind">;
289
295
  disabled: z.ZodDefault<z.ZodBoolean>;
296
+ labelPlacement: z.ZodDefault<z.ZodEnum<{
297
+ above: "above";
298
+ inline: "inline";
299
+ }>>;
300
+ hint: z.ZodOptional<z.ZodString>;
301
+ disabledReason: z.ZodOptional<z.ZodString>;
290
302
  }, z.core.$strip>, z.ZodObject<{
291
303
  type: z.ZodLiteral<"alert">;
292
304
  tone: z.ZodDefault<z.ZodEnum<{
@@ -316,6 +328,8 @@ declare const leafNodeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
316
328
  secondary: "secondary";
317
329
  }>>;
318
330
  disabled: z.ZodDefault<z.ZodBoolean>;
331
+ hint: z.ZodOptional<z.ZodString>;
332
+ disabledReason: z.ZodOptional<z.ZodString>;
319
333
  }, z.core.$strip>>;
320
334
  }, z.core.$strip>, z.ZodObject<{
321
335
  type: z.ZodLiteral<"details">;
@@ -349,6 +363,52 @@ declare const leafNodeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
349
363
  relative: "relative";
350
364
  }>>;
351
365
  label: z.ZodOptional<z.ZodString>;
366
+ }, z.core.$strip>, z.ZodObject<{
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>;
352
412
  }, z.core.$strip>], "type">;
353
413
  type LeafScreenNode = z.infer<typeof leafNodeSchema>;
354
414
  type LeafScreenNodeInput = z.input<typeof leafNodeSchema>;
@@ -356,16 +416,18 @@ export type ScreenNode = LeafScreenNode | {
356
416
  type: 'section';
357
417
  title?: string;
358
418
  description?: string;
359
- layout: 'stack' | 'grid';
419
+ layout: 'stack' | 'grid' | 'row';
360
420
  columns: number;
421
+ scroll: boolean;
361
422
  children: ScreenNode[];
362
423
  };
363
424
  type ScreenNodeInput = LeafScreenNodeInput | {
364
425
  type: 'section';
365
426
  title?: string;
366
427
  description?: string;
367
- layout: 'stack' | 'grid';
368
- columns: number;
428
+ layout: 'stack' | 'grid' | 'row';
429
+ columns?: number;
430
+ scroll?: boolean;
369
431
  children: ScreenNodeInput[];
370
432
  };
371
433
  /**
@@ -437,6 +499,8 @@ export declare const screenTreeSchema: z.ZodObject<{
437
499
  secondary: "secondary";
438
500
  }>>;
439
501
  disabled: z.ZodDefault<z.ZodBoolean>;
502
+ hint: z.ZodOptional<z.ZodString>;
503
+ disabledReason: z.ZodOptional<z.ZodString>;
440
504
  }, z.core.$strip>>>;
441
505
  children: z.ZodArray<z.ZodType<ScreenRootNode, ScreenRootNodeInput, z.core.$ZodTypeInternals<ScreenRootNode, ScreenRootNodeInput>>>;
442
506
  refreshSeconds: z.ZodOptional<z.ZodNumber>;
@@ -485,6 +549,8 @@ export declare const screenLoadResponseSchema: z.ZodObject<{
485
549
  secondary: "secondary";
486
550
  }>>;
487
551
  disabled: z.ZodDefault<z.ZodBoolean>;
552
+ hint: z.ZodOptional<z.ZodString>;
553
+ disabledReason: z.ZodOptional<z.ZodString>;
488
554
  }, z.core.$strip>>>;
489
555
  children: z.ZodArray<z.ZodType<ScreenRootNode, ScreenRootNodeInput, z.core.$ZodTypeInternals<ScreenRootNode, ScreenRootNodeInput>>>;
490
556
  refreshSeconds: z.ZodOptional<z.ZodNumber>;
package/dist/schema.js CHANGED
@@ -41,6 +41,15 @@ export const buttonSchema = z.object({
41
41
  variant: z.enum(['primary', 'secondary', 'destructive']).default('secondary'),
42
42
  /** Shown but not pressable — a Next button on the last page, an action not yet available. */
43
43
  disabled: z.boolean().default(false),
44
+ /** A short explanation shown next to the button on hover or keyboard focus. */
45
+ hint: z.string().min(1).max(200).optional(),
46
+ /**
47
+ * Why the button is disabled, shown under it while `disabled` is true:
48
+ * "Kee stopped because the patient asked for a person". Ignored when the
49
+ * button is enabled. A greyed control that does not say why leaves the
50
+ * reader guessing.
51
+ */
52
+ disabledReason: z.string().min(1).max(200).optional(),
44
53
  });
45
54
  /** A table cell. Dates are ISO 8601; core formats them tenant-locale-aware. */
46
55
  export const cellSchema = z.discriminatedUnion('kind', [
@@ -164,6 +173,16 @@ const selectSchema = z
164
173
  .max(100),
165
174
  onChange: pressSchema,
166
175
  disabled: z.boolean().default(false),
176
+ /**
177
+ * Where the label sits. `above` (the default) is a form field. `inline` puts
178
+ * the label inside the control on the same line, so a row of filters is one
179
+ * line tall: "Status: Open".
180
+ */
181
+ labelPlacement: z.enum(['above', 'inline']).default('above'),
182
+ /** A short explanation shown next to the select on hover or keyboard focus. */
183
+ hint: z.string().min(1).max(200).optional(),
184
+ /** Why the select is disabled, shown under it while `disabled` is true. */
185
+ disabledReason: z.string().min(1).max(200).optional(),
167
186
  })
168
187
  .superRefine((node, ctx) => {
169
188
  if (node.value !== undefined && !node.options.some((option) => option.value === node.value)) {
@@ -214,6 +233,76 @@ const timestampSchema = z.object({
214
233
  /** Shown before the time, muted: "Last contacted". */
215
234
  label: z.string().max(80).optional(),
216
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
+ });
301
+ /**
302
+ * A horizontal line that says where one part of a screen ends and the next
303
+ * begins. It carries no content. Use a `section` when the parts need a title.
304
+ */
305
+ const dividerSchema = z.object({ type: z.literal('divider') });
217
306
  const headingSchema = z.object({ type: z.literal('heading'), text: z.string().max(120) });
218
307
  const textSchema = z.object({
219
308
  type: z.literal('text'),
@@ -337,6 +426,8 @@ const leafNodeSchema = z.discriminatedUnion('type', [
337
426
  alertSchema,
338
427
  detailsSchema,
339
428
  timestampSchema,
429
+ dividerSchema,
430
+ boardSchema,
340
431
  ]);
341
432
  const MAX_SECTION_DEPTH = 8;
342
433
  const nodeSchemas = new Map();
@@ -345,8 +436,20 @@ const sectionBranch = (children) => z.object({
345
436
  type: z.literal('section'),
346
437
  title: z.string().max(120).optional(),
347
438
  description: z.string().max(300).optional(),
348
- layout: z.enum(['stack', 'grid']),
349
- columns: z.number().int().min(1).max(4),
439
+ /**
440
+ * `stack`: one child per line. `grid`: `columns` equal columns. `row`:
441
+ * children side by side at their own width, wrapping on a narrow screen,
442
+ * for a filter bar or a pair of buttons that belong together.
443
+ */
444
+ layout: z.enum(['stack', 'grid', 'row']),
445
+ /** Used by `grid` only. */
446
+ columns: z.number().int().min(1).max(4).default(1),
447
+ /**
448
+ * The section scrolls inside itself at a height the renderer chooses, so
449
+ * that what sits under it stays on screen: a conversation of any length
450
+ * above its reply box.
451
+ */
452
+ scroll: z.boolean().default(false),
350
453
  children: z.array(children).max(100),
351
454
  });
352
455
  const tabsBranch = (children) => z
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keemakr/ui-sdk",
3
- "version": "1.3.0",
3
+ "version": "1.4.0-dev-20261012081643",
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",