@keemakr/ui-sdk 1.5.0 → 1.6.0-dev-20261026112734
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 +118 -0
- package/dist/badges.d.ts +65 -0
- package/dist/badges.js +96 -0
- package/dist/build.js +2 -2
- package/dist/cells.d.ts +31 -0
- package/dist/cells.js +45 -0
- package/dist/chart.d.ts +20 -0
- package/dist/chart.js +38 -0
- package/dist/conform.d.ts +21 -0
- package/dist/conform.js +310 -0
- package/dist/dataset.d.ts +6 -6
- package/dist/date-format.d.ts +64 -0
- package/dist/date-format.js +132 -0
- package/dist/drawer.d.ts +22 -0
- package/dist/drawer.js +21 -0
- package/dist/edit.d.ts +60 -0
- package/dist/edit.js +60 -0
- package/dist/filters.d.ts +54 -0
- package/dist/filters.js +81 -0
- package/dist/form.d.ts +45 -1
- package/dist/form.js +39 -0
- package/dist/heatmap.d.ts +36 -0
- package/dist/heatmap.js +56 -0
- package/dist/icon.d.ts +20 -0
- package/dist/icon.js +43 -0
- package/dist/index.d.ts +37 -4
- package/dist/index.js +21 -2
- package/dist/markdown-links.d.ts +29 -0
- package/dist/markdown-links.js +99 -0
- package/dist/media.js +7 -1
- package/dist/negotiate.d.ts +302 -0
- package/dist/negotiate.js +1092 -0
- package/dist/nodes.d.ts +1 -1
- package/dist/nodes.js +3 -0
- package/dist/pager.d.ts +2 -2
- package/dist/player.d.ts +46 -0
- package/dist/player.js +86 -0
- package/dist/published/index.d.ts +4 -0
- package/dist/published/index.js +18 -0
- package/dist/published/v1.1.0/schema.d.ts +439 -0
- package/dist/published/v1.1.0/schema.js +405 -0
- package/dist/published/v1.4.0/dataset-view.d.ts +160 -0
- package/dist/published/v1.4.0/dataset-view.js +702 -0
- package/dist/published/v1.4.0/dataset.d.ts +331 -0
- package/dist/published/v1.4.0/dataset.js +628 -0
- package/dist/published/v1.4.0/schema.d.ts +1132 -0
- package/dist/published/v1.4.0/schema.js +1410 -0
- package/dist/schema.d.ts +1638 -20
- package/dist/schema.js +1275 -72
- package/dist/section.d.ts +65 -0
- package/dist/section.js +81 -0
- package/dist/stacked-bar.d.ts +34 -0
- package/dist/stacked-bar.js +20 -0
- package/dist/stat.d.ts +24 -0
- package/dist/stat.js +27 -0
- package/dist/table-row.d.ts +24 -0
- package/dist/table-row.js +23 -0
- package/dist/tabs.d.ts +36 -0
- package/dist/tabs.js +83 -0
- package/dist/testkit.d.ts +38 -0
- package/dist/testkit.js +68 -0
- package/dist/timeline.d.ts +5 -0
- package/dist/timeline.js +35 -0
- package/examples/campaigns/README.md +22 -1
- package/examples/campaigns/approvals/index.ts +23 -0
- package/examples/campaigns/approvals/launch.ts +38 -0
- package/examples/campaigns/brief/index.ts +23 -0
- package/examples/campaigns/brief/ready.ts +43 -0
- package/examples/campaigns/demo-approvals.ts +35 -0
- package/examples/campaigns/demo-brief.ts +23 -0
- package/examples/campaigns/entry.json +71 -1
- package/examples/campaigns/tsconfig.json +8 -1
- package/package.json +6 -1
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// UI-20 (Covalent): a word inside markdown that opens a screen of the same module. The morning
|
|
2
|
+
// brief says "[Northside Surgical](screen:northside) is at risk", and the account name opens
|
|
3
|
+
// that account's screen.
|
|
4
|
+
//
|
|
5
|
+
// The markdown names a REF only. What the ref opens is the node's own `links` map, a normal
|
|
6
|
+
// navigate press the module wrote beside the text and the schema checked like any other press:
|
|
7
|
+
//
|
|
8
|
+
// { type: 'markdown',
|
|
9
|
+
// value: '[Northside Surgical](screen:northside) is at risk.',
|
|
10
|
+
// links: { northside: { kind: 'navigate', screen: 'account', params: { id: 'acc_9' } } } }
|
|
11
|
+
//
|
|
12
|
+
// So the markdown itself can never carry a screen name, params or a URL that opens anything. A
|
|
13
|
+
// `screen:` link whose ref is not in the map is drawn as its words, plain text. Every other link
|
|
14
|
+
// (https, mailto) is drawn exactly as before.
|
|
15
|
+
/** The scheme a link to a screen is written with: `[words](screen:ref)`. */
|
|
16
|
+
export const SCREEN_LINK_SCHEME = 'screen:';
|
|
17
|
+
/** The most refs one markdown node's `links` map holds. */
|
|
18
|
+
export const MARKDOWN_LINKS_MAX = 50;
|
|
19
|
+
/** A ref: the same lowercase slug as an action or a screen key. */
|
|
20
|
+
const REF = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
21
|
+
/**
|
|
22
|
+
* True for any link written with the `screen:` scheme, in any case and with any spaces around
|
|
23
|
+
* it. A renderer never hands such a link to the browser or the phone as a URL: it is either a
|
|
24
|
+
* ref in the node's map (a press) or plain text.
|
|
25
|
+
*/
|
|
26
|
+
export function isScreenLink(href) {
|
|
27
|
+
return typeof href === 'string' && href.trim().toLowerCase().startsWith(SCREEN_LINK_SCHEME);
|
|
28
|
+
}
|
|
29
|
+
/** The ref a `screen:` link names, or null when it is not exactly `screen:<slug>`. */
|
|
30
|
+
export function screenLinkRef(href) {
|
|
31
|
+
if (typeof href !== 'string' || !href.startsWith(SCREEN_LINK_SCHEME))
|
|
32
|
+
return null;
|
|
33
|
+
const ref = href.slice(SCREEN_LINK_SCHEME.length);
|
|
34
|
+
return REF.test(ref) ? ref : null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The press a `screen:` link opens: the entry of `links` its ref names, or null. Only the map's
|
|
38
|
+
* own entries count (never `constructor` or anything else an object inherits), and only an entry
|
|
39
|
+
* that is a navigate press, so a renderer that is handed a loosely typed map still opens nothing
|
|
40
|
+
* else. Null means: draw the link's words as plain text.
|
|
41
|
+
*/
|
|
42
|
+
export function resolveScreenLink(href, links) {
|
|
43
|
+
const ref = screenLinkRef(href);
|
|
44
|
+
if (!ref || !links || typeof links !== 'object')
|
|
45
|
+
return null;
|
|
46
|
+
if (!Object.prototype.hasOwnProperty.call(links, ref))
|
|
47
|
+
return null;
|
|
48
|
+
const press = links[ref];
|
|
49
|
+
return press && typeof press === 'object' && press.kind === 'navigate' ? press : null;
|
|
50
|
+
}
|
|
51
|
+
// ── the fold for a client that does not draw them ────────────────────────────
|
|
52
|
+
/** Fenced code and inline code: left as written, a `screen:` inside code is code. */
|
|
53
|
+
const CODE = /(```[\s\S]*?(?:```|$)|~~~[\s\S]*?(?:~~~|$)|`[^`\n]*`)/;
|
|
54
|
+
/** The scheme, case-insensitive, as a regex source. */
|
|
55
|
+
const SCHEME = '[sS][cC][rR][eE][eE][nN]:';
|
|
56
|
+
/** `[words](screen:ref)`, `[words](<screen:ref>)`, with or without a title. */
|
|
57
|
+
const INLINE = new RegExp(String.raw `\[((?:\\.|[^\[\]\\\n])*)\]\(\s*(?:<\s*${SCHEME}[^>\n]*>|${SCHEME}[^\s)]*)(?:\s+(?:"[^"\n]*"|'[^'\n]*'|\([^)\n]*\)))?\s*\)`, 'g');
|
|
58
|
+
/** `<screen:ref>`: an autolink, drawn as its ref. */
|
|
59
|
+
const AUTOLINK = new RegExp(String.raw `<${SCHEME}([^\s<>]*)>`, 'g');
|
|
60
|
+
/** `[label]: screen:ref`, a reference definition, on a line of its own. */
|
|
61
|
+
const DEFINITION = new RegExp(String.raw `^ {0,3}\[((?:\\.|[^\[\]\\\n])+)\]:[ \t]*(?:<${SCHEME}[^>\n]*>|${SCHEME}\S*)(?:[ \t]+(?:"[^"\n]*"|'[^'\n]*'|\([^)\n]*\)))?[ \t]*(?:\n|$)`, 'gm');
|
|
62
|
+
/** How CommonMark matches a reference label: case-folded, inner spaces collapsed. */
|
|
63
|
+
const labelKey = (label) => label.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
64
|
+
/**
|
|
65
|
+
* The markdown with every `screen:` link turned into its words, for a client that does not draw
|
|
66
|
+
* them (negotiate.ts, `markdown.links`). An older renderer would draw such a link as a broken link
|
|
67
|
+
* or as the raw `[words](screen:ref)`; this way it reads as the sentence the module wrote. Inline
|
|
68
|
+
* links, autolinks and reference definitions (with the links that use them) are folded; code is
|
|
69
|
+
* left as written. Pure; returns the same string when there is nothing to fold.
|
|
70
|
+
*/
|
|
71
|
+
export function withoutScreenLinks(value) {
|
|
72
|
+
if (!/screen:/i.test(value))
|
|
73
|
+
return value;
|
|
74
|
+
const parts = value.split(CODE);
|
|
75
|
+
// Reference labels whose definition points at a screen, from the prose parts only.
|
|
76
|
+
const labels = new Set();
|
|
77
|
+
for (let i = 0; i < parts.length; i += 2) {
|
|
78
|
+
for (const m of parts[i].matchAll(DEFINITION))
|
|
79
|
+
labels.add(labelKey(m[1]));
|
|
80
|
+
}
|
|
81
|
+
const folded = parts.map((part, i) => {
|
|
82
|
+
if (i % 2 === 1)
|
|
83
|
+
return part;
|
|
84
|
+
let next = part
|
|
85
|
+
.replace(DEFINITION, '')
|
|
86
|
+
.replace(INLINE, (_all, words) => words)
|
|
87
|
+
.replace(AUTOLINK, (_all, ref) => ref);
|
|
88
|
+
if (labels.size > 0) {
|
|
89
|
+
// [words][label], [label][] and [label], for a label defined as a screen link above.
|
|
90
|
+
next = next.replace(/\[((?:\\.|[^\[\]\\\n])*)\](?:\[((?:\\.|[^\[\]\\\n])*)\])?(?!\(|:)/g, (all, words, label) => {
|
|
91
|
+
const key = labelKey(label === undefined || label === '' ? words : label);
|
|
92
|
+
return labels.has(key) ? words : all;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return next;
|
|
96
|
+
});
|
|
97
|
+
const out = folded.join('');
|
|
98
|
+
return out === value ? value : out;
|
|
99
|
+
}
|
package/dist/media.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Module-side helpers for the `image` and `
|
|
1
|
+
// Module-side helpers for the `image`, `video` and `audio` blocks. A module serves its
|
|
2
2
|
// media at GET /screens/media/<path> in its screens channel, after verifying the
|
|
3
3
|
// capability grant exactly as its load and action routes do. Core fetches from
|
|
4
4
|
// there and passes on only these types, so a module that labels a file with
|
|
@@ -14,6 +14,12 @@ export const MEDIA_CONTENT_TYPES = {
|
|
|
14
14
|
avif: 'image/avif',
|
|
15
15
|
mp4: 'video/mp4',
|
|
16
16
|
webm: 'video/webm',
|
|
17
|
+
mp3: 'audio/mpeg',
|
|
18
|
+
m4a: 'audio/mp4',
|
|
19
|
+
ogg: 'audio/ogg',
|
|
20
|
+
oga: 'audio/ogg',
|
|
21
|
+
wav: 'audio/wav',
|
|
22
|
+
weba: 'audio/webm',
|
|
17
23
|
vtt: 'text/vtt',
|
|
18
24
|
};
|
|
19
25
|
/** The Content-Type to serve a media path with, or null when core would refuse it. */
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import type { ChatPress, ScreenRootNode, ScreenTree } from './schema.js';
|
|
2
|
+
/**
|
|
3
|
+
* Tree features below the node level that a client may or may not draw.
|
|
4
|
+
*
|
|
5
|
+
* - `breadcrumbs`: drawn as a trail. Without it, one muted line.
|
|
6
|
+
* - `button.disabled`: a disabled button is drawn disabled and refuses the press. Without it, a
|
|
7
|
+
* disabled button becomes muted text and a disabled header action is dropped.
|
|
8
|
+
* - `section.row`: NEWER ENUM VALUE `layout: 'row'`. Without it, `stack`.
|
|
9
|
+
* - `form.field_kinds`: the field kinds added after 1.2.0 (`number`, `date`, `datetime`,
|
|
10
|
+
* `checkbox`, `multiselect`, `combobox`, `list`). Without it, `text`, `select` or `textarea`.
|
|
11
|
+
* - `form.confirm`: the client asks `submit.confirm` (and a `secondary` send's `confirm`) before
|
|
12
|
+
* the form posts. Without it, a form that carries one becomes one calm line, so nothing posts
|
|
13
|
+
* without asking.
|
|
14
|
+
* - `press.chat`: a `chat` press opens the client's chat with the prompt typed in, never sent.
|
|
15
|
+
* Without it, nothing offers to open the chat: a body button with one becomes its label as
|
|
16
|
+
* muted text, a header, row, calendar entry or alert button with one is dropped, and a row,
|
|
17
|
+
* card, entry or image keeps its place but is no longer pressable. A choice with one is
|
|
18
|
+
* dropped, and choices with nothing left become one line of their titles.
|
|
19
|
+
* - `form.radio`: the `radio` field kind and an option's `badge` and `description` (newer than
|
|
20
|
+
* any published release; core's own renderer draws them). Without it, a radio is a `select`
|
|
21
|
+
* with the same options and value, so the reader can still answer; a badge goes into its
|
|
22
|
+
* option's label ("Acme (Recommended)"); a description is dropped.
|
|
23
|
+
* - `table.cells`: the `sparkline` and `meter` table cells and a `text` or `badge` cell's
|
|
24
|
+
* `secondary` line (newer than any published release; core's own renderer draws them).
|
|
25
|
+
* Without it, a sparkline is a text cell with its label, a meter one with its value label,
|
|
26
|
+
* and the second line is dropped (cells.ts `foldTableCell`).
|
|
27
|
+
* - `header.meta`: the tree's `badges` beside the title and `meta` facts under it (newer than
|
|
28
|
+
* any published release; core's own renderer draws them). An older parse strips both without
|
|
29
|
+
* a sign, so without it they become one muted line first in the body ("At risk · Tier 1 ·
|
|
30
|
+
* ARR: $1.2M · Renewal: 15 Mar 2027"), where the breadcrumbs fold puts its own line above.
|
|
31
|
+
* - `tabs.many`: a strip of up to 10 tabs (newer than any published release, which takes 6).
|
|
32
|
+
* Without it, the first five stay tabs and a sixth, "More", holds the rest as titled sections
|
|
33
|
+
* in order (tabs.ts `foldTabs`). A strip's `param` needs no feature: a parse that predates it
|
|
34
|
+
* strips it, and the strip keeps its open tab as local state, as it always did.
|
|
35
|
+
* - `choice.action`: a choice card's own button (`action`), and a card with no `onPress` (newer
|
|
36
|
+
* than any published release; core's own renderer draws them). Without it, a card that has
|
|
37
|
+
* its own `onPress` keeps it and loses the button; a card with only the button becomes a card
|
|
38
|
+
* whose press is the button's, so every card can still be acted on. The card's `tone` and
|
|
39
|
+
* `badge`, a timeline item's `icon`, `tone` and `actor`, and a badge's `dot` are new optional
|
|
40
|
+
* keys: an older parse strips them and draws the rest, so they need no fold.
|
|
41
|
+
* - `markdown.links`: a markdown node's `links` map, and the `[words](screen:ref)` links in its
|
|
42
|
+
* text that open a screen through it (newer than any published release; core's own renderer
|
|
43
|
+
* draws them). Without it, each `screen:` link becomes its words as plain text and the map is
|
|
44
|
+
* dropped (markdown-links.ts `withoutScreenLinks`), so an older renderer draws the sentence
|
|
45
|
+
* the module wrote, never a broken link or the raw `[words](screen:ref)`. https links stay.
|
|
46
|
+
* - `form.disabled`: a form send (`submit` or a `secondary`) that is `disabled`, with its
|
|
47
|
+
* `disabledReason` (newer than any published release; core's own renderer draws them). An
|
|
48
|
+
* older parse strips `disabled` and would post the send, so without it a disabled `secondary`
|
|
49
|
+
* is dropped, and a form whose `submit` is disabled becomes one calm line (`submit` is
|
|
50
|
+
* required, and promoting a `secondary` would change what Enter posts). A reason on an
|
|
51
|
+
* enabled send means nothing and is left for the parse to strip.
|
|
52
|
+
* - `press.seek`: a `seek` press moves an `audio` or `video` playhead on the same screen, and a
|
|
53
|
+
* timeline item's or conversation turn's `seek` draws its time as a button that does the same
|
|
54
|
+
* (newer than any published release; core's own renderer draws them). Without it, each seek
|
|
55
|
+
* press goes exactly as a chat press does without `press.chat` (a body button becomes its
|
|
56
|
+
* label as muted text, a header, row, entry or alert button is dropped, a row, card, entry or
|
|
57
|
+
* image stays but is no longer pressable), and a moment's or turn's time is written in front
|
|
58
|
+
* of its words ("4:10 · Allergies discussed"), so the reader still learns where it is. A
|
|
59
|
+
* video's `key` is a new optional key an older parse strips; nothing on such a client names it.
|
|
60
|
+
* - `edit.inline`: an `edit` on a `text`, `markdown` or `details` block (and on its items), or on
|
|
61
|
+
* a `table` column, drawn as a pencil that turns the block into a field in place (newer than
|
|
62
|
+
* any published release; core's own renderer draws it). An older parse strips it, so the block
|
|
63
|
+
* would draw read-only anyway; core drops it on purpose all the same (`withoutInlineEdit`), so
|
|
64
|
+
* a client never gets half an edit (a column's options without its action, say) and the
|
|
65
|
+
* fallback list says the block was made read-only. The data is all still there.
|
|
66
|
+
*/
|
|
67
|
+
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';
|
|
68
|
+
/** The node types and features one ui-sdk release, or one client, draws. */
|
|
69
|
+
export interface NodeSet {
|
|
70
|
+
/** For messages: "ui-sdk 1.4.0", "mobile 0.2.5". */
|
|
71
|
+
name: string;
|
|
72
|
+
nodes: readonly string[];
|
|
73
|
+
features: readonly UiFeature[];
|
|
74
|
+
/**
|
|
75
|
+
* The published schema the client parses with. Its tree is fitted to it (conform.ts). Absent, or
|
|
76
|
+
* `current`, means this package's own schema: nothing to fit.
|
|
77
|
+
*/
|
|
78
|
+
parses?: UiSdkSnapshot;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Nodes a module may send that core draws itself before any client sees the tree: a `dataset`
|
|
82
|
+
* node becomes plain blocks from the module's rows (dataset-view.ts). No client parses them, so
|
|
83
|
+
* no snapshot lists them; one that reached negotiation undrawn gets its own fallback.
|
|
84
|
+
*/
|
|
85
|
+
export declare const CORE_DRAWN_NODE_TYPES: readonly ["dataset"];
|
|
86
|
+
/**
|
|
87
|
+
* What each schema a client can parse with admits. `1.1.0` and `1.4.0` are published releases
|
|
88
|
+
* (frozen copies in published/); `current` is this package, newer than any released app.
|
|
89
|
+
*/
|
|
90
|
+
export declare const UI_SDK_SNAPSHOTS: {
|
|
91
|
+
readonly '1.1.0': {
|
|
92
|
+
readonly name: "ui-sdk 1.1.0";
|
|
93
|
+
readonly nodes: readonly ["heading", "text", "markdown", "stat", "button", "form", "table", "timeline", "pending_request", "pending_requests", "choice_cards", "progress", "chart", "section", "tabs"];
|
|
94
|
+
readonly features: readonly [];
|
|
95
|
+
};
|
|
96
|
+
readonly '1.4.0': {
|
|
97
|
+
readonly name: "ui-sdk 1.4.0";
|
|
98
|
+
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"];
|
|
99
|
+
readonly features: readonly ["breadcrumbs", "button.disabled", "section.row", "form.field_kinds", "form.confirm"];
|
|
100
|
+
};
|
|
101
|
+
readonly current: {
|
|
102
|
+
readonly name: "ui-sdk (this package)";
|
|
103
|
+
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"];
|
|
104
|
+
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"];
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
export type UiSdkSnapshot = keyof typeof UI_SDK_SNAPSHOTS;
|
|
108
|
+
/** The snapshot this package is. Core's own renderer draws all of it. */
|
|
109
|
+
export declare const CURRENT_UI_SDK_SNAPSHOT: UiSdkSnapshot;
|
|
110
|
+
/** The clients that send `X-Kee-Client`. Same names as core's ClientPlatform. */
|
|
111
|
+
export type ClientTarget = 'web' | 'mobile' | 'desktop' | 'extension' | 'cli';
|
|
112
|
+
export interface ClientRelease {
|
|
113
|
+
client: ClientTarget;
|
|
114
|
+
/** The first client version (x.y.z) this row applies to. */
|
|
115
|
+
since: string;
|
|
116
|
+
/** The schema this release parses with: the ui-sdk inside the sdk it was built with. */
|
|
117
|
+
parses: UiSdkSnapshot;
|
|
118
|
+
/** What its renderer draws: always within `parses`. */
|
|
119
|
+
draws: Omit<NodeSet, 'name' | 'parses'>;
|
|
120
|
+
/** Why the row says what it says. */
|
|
121
|
+
note: string;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Every client release core negotiates for, oldest first per client. A client
|
|
125
|
+
* version below every row of its client gets the floor (ui-sdk 1.1.0).
|
|
126
|
+
*
|
|
127
|
+
* Checked 2026-10-20 against kee-clients' release tags and their lockfiles (which sdk, and so which
|
|
128
|
+
* ui-sdk, each build parses with) and their renderers.
|
|
129
|
+
*/
|
|
130
|
+
export declare const CLIENT_RELEASES: readonly ClientRelease[];
|
|
131
|
+
/** The floor: what every client that sends `X-Kee-Client` can take. */
|
|
132
|
+
export declare const FLOOR_NODE_SET: NodeSet;
|
|
133
|
+
/** The rows of one client, oldest first. */
|
|
134
|
+
export declare function releasesOf(client: string, releases?: readonly ClientRelease[]): ClientRelease[];
|
|
135
|
+
/**
|
|
136
|
+
* What a client draws, from its `X-Kee-Client` platform and version: the newest
|
|
137
|
+
* row of that client whose `since` it has reached. An unknown platform, or a
|
|
138
|
+
* version below every row, gets the floor. A version core cannot read counts as
|
|
139
|
+
* 0.0.0: a client core cannot identify is treated as the oldest, never the newest.
|
|
140
|
+
*/
|
|
141
|
+
export declare function nodeSetForClient(platform: string, version: string, releases?: readonly ClientRelease[]): NodeSet;
|
|
142
|
+
/**
|
|
143
|
+
* What a client build says about the schema it parses, in the build metadata of its
|
|
144
|
+
* `X-Kee-Client` version: `web/0.2.0+abc1234.ui-current`. Only a build that installed the @dev
|
|
145
|
+
* sdk channel says it, because only that install parses the current schema (the web row note
|
|
146
|
+
* above). A build from the committed lockfile says nothing, so its header is unchanged.
|
|
147
|
+
*/
|
|
148
|
+
export type UiSchemaClaim = 'current';
|
|
149
|
+
/** The build metadata identifier a build that parses the current schema adds to its version. */
|
|
150
|
+
export declare const CURRENT_SCHEMA_CLAIM = "ui-current";
|
|
151
|
+
/**
|
|
152
|
+
* The schema claim in an `X-Kee-Client` value, or null. Read from the build metadata (after the
|
|
153
|
+
* `+`) only, so a claim never changes the version core compares, and a header without one, or
|
|
154
|
+
* with anything else, claims nothing.
|
|
155
|
+
*/
|
|
156
|
+
export declare function uiSchemaClaimOf(header: string | null | undefined): UiSchemaClaim | null;
|
|
157
|
+
/** What a channel build of a client parses and draws, when it claims the current schema. */
|
|
158
|
+
export interface ChannelBuild {
|
|
159
|
+
client: ClientTarget;
|
|
160
|
+
parses: UiSdkSnapshot;
|
|
161
|
+
draws: Omit<NodeSet, 'name' | 'parses'>;
|
|
162
|
+
note: string;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The client builds core may send the current schema, when they claim it and core honours claims
|
|
166
|
+
* (core's own deployment decides that: src/lib/modules/ui-negotiation.ts). A client with no row
|
|
167
|
+
* here gets its release row whatever it claims.
|
|
168
|
+
*/
|
|
169
|
+
export declare const CHANNEL_BUILDS: readonly ChannelBuild[];
|
|
170
|
+
/**
|
|
171
|
+
* What a client draws, given the schema it claims. A `current` claim from a client with a channel
|
|
172
|
+
* build row gets that row; anything else gets `nodeSetForClient`, exactly as with no claim. A
|
|
173
|
+
* forged claim gets the forger a screen its own parser may refuse, nothing else: the tree is the
|
|
174
|
+
* same data for the same session, only less folded.
|
|
175
|
+
*/
|
|
176
|
+
export declare function nodeSetForClaim(platform: string, version: string, claim: UiSchemaClaim | null, builds?: readonly ChannelBuild[]): NodeSet;
|
|
177
|
+
/** The newest release row of a client: what a module targets when it names only the client. */
|
|
178
|
+
export declare function latestRelease(client: ClientTarget): ClientRelease;
|
|
179
|
+
/** One node core replaced, or one header action it dropped. */
|
|
180
|
+
export interface Fallback {
|
|
181
|
+
/** Where it was, as a property path: `children[2].children[0]`, `actions[1]`, `breadcrumbs`. */
|
|
182
|
+
path: string;
|
|
183
|
+
/** The node type (or `breadcrumbs`, or `header` for badges and meta) the client cannot draw. */
|
|
184
|
+
type: string;
|
|
185
|
+
/**
|
|
186
|
+
* Why: the node type is not drawn, a feature of it is not, the client's published schema
|
|
187
|
+
* refuses something in it (`parse`), or that schema would strip a `confirm` or a `disabled`
|
|
188
|
+
* flag from a control it keeps (`safety`).
|
|
189
|
+
*/
|
|
190
|
+
reason: 'node' | UiFeature | 'parse' | 'safety';
|
|
191
|
+
/**
|
|
192
|
+
* What took its place. A header action can only be dropped: the header holds buttons only. A
|
|
193
|
+
* newer enum value is `folded`: the node stays, with the nearest value the client knows. A
|
|
194
|
+
* heatmap becomes a `table` of the same numbers.
|
|
195
|
+
*/
|
|
196
|
+
as: 'text' | 'markdown' | 'table' | 'dropped' | 'folded';
|
|
197
|
+
}
|
|
198
|
+
export interface NegotiatedTree {
|
|
199
|
+
tree: ScreenTree;
|
|
200
|
+
fallbacks: Fallback[];
|
|
201
|
+
}
|
|
202
|
+
/** The fallback for a node type that has no specific one: a sign that something is there. */
|
|
203
|
+
export declare const GENERIC_FALLBACK_TEXT = "Open this in the web app to see this part of the screen.";
|
|
204
|
+
/**
|
|
205
|
+
* What stands in for a control that would act without its safety on this client: a form that
|
|
206
|
+
* would post without its confirm, a button that would press although it is disabled.
|
|
207
|
+
*/
|
|
208
|
+
export declare const SAFETY_FALLBACK_TEXT = "Open this in the web app to finish this step.";
|
|
209
|
+
/**
|
|
210
|
+
* The tree a client with `set` receives: every node it cannot draw replaced with
|
|
211
|
+
* `text` or `markdown`, every feature it cannot draw folded away. The result
|
|
212
|
+
* still validates against the ui-sdk schema. A tree that needs nothing is
|
|
213
|
+
* returned as the same object.
|
|
214
|
+
*/
|
|
215
|
+
export declare function negotiateTree(tree: ScreenTree, set: NodeSet): NegotiatedTree;
|
|
216
|
+
/** True for a press that opens the chat with a prompt typed in. */
|
|
217
|
+
export declare function isChatPress(press: {
|
|
218
|
+
kind: string;
|
|
219
|
+
} | undefined): press is ChatPress;
|
|
220
|
+
type ChoiceCardsNode = Extract<ScreenRootNode, {
|
|
221
|
+
type: 'choice_cards';
|
|
222
|
+
}>;
|
|
223
|
+
/**
|
|
224
|
+
* A form for a client that draws no disabled send (`form.disabled`): a disabled `secondary` is
|
|
225
|
+
* dropped, and a form whose `submit` is disabled becomes one calm line. Returns the same object
|
|
226
|
+
* when no send is disabled.
|
|
227
|
+
*/
|
|
228
|
+
export declare function foldDisabledSends(node: FormNode, path: string, fallbacks: Fallback[]): FormNode | {
|
|
229
|
+
type: 'text';
|
|
230
|
+
value: string;
|
|
231
|
+
muted: true;
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Choice cards for a client that draws no card button (`choice.action`). A card with its own
|
|
235
|
+
* `onPress` keeps it and loses the button; a card with only the button gets the button's press as
|
|
236
|
+
* its own, so it can still be acted on (the button's label is not drawn). Returns the same object
|
|
237
|
+
* when no card has a button.
|
|
238
|
+
*/
|
|
239
|
+
export declare function foldChoiceActions(node: ChoiceCardsNode, path: string, fallbacks: Fallback[]): ChoiceCardsNode;
|
|
240
|
+
/**
|
|
241
|
+
* The node as a client that draws no edit control should get it (`edit.inline`): the same block,
|
|
242
|
+
* read-only. Drops `edit` from a `text`, `markdown` or `details` block and its items, and from each
|
|
243
|
+
* `table` column. Only the node's own fields; a row's expanded children are visited on their own.
|
|
244
|
+
* Returns the same object when there is nothing to take away.
|
|
245
|
+
*/
|
|
246
|
+
export declare function withoutInlineEdit(node: ScreenRootNode, path: string, fallbacks: Fallback[]): ScreenRootNode;
|
|
247
|
+
type FormNode = Extract<ScreenRootNode, {
|
|
248
|
+
type: 'form';
|
|
249
|
+
}>;
|
|
250
|
+
type FormField = FormNode['fields'][number];
|
|
251
|
+
/**
|
|
252
|
+
* A radio, or a multiselect with option extras, for a client that draws neither (`form.radio`).
|
|
253
|
+
* A radio becomes a `select` with the same options and starting value, so it posts the same
|
|
254
|
+
* string. Each badge moves into its option's label, so "Recommended" still reaches the reader;
|
|
255
|
+
* each description is dropped, as a select has no line for it.
|
|
256
|
+
*/
|
|
257
|
+
export declare function foldChoiceField(field: FormField): FormField;
|
|
258
|
+
/**
|
|
259
|
+
* A form field of a kind the client's parse does not know, as the nearest kind it does. It keeps
|
|
260
|
+
* the label, the name and the starting value; what it posts becomes a string (the older kinds post
|
|
261
|
+
* strings), so a module that types its input must accept that from an older client.
|
|
262
|
+
* number, date, datetime -> text (the value is already a string)
|
|
263
|
+
* combobox, radio -> select (the same options; typed text is not offered)
|
|
264
|
+
* checkbox -> select with "Yes" and "No"
|
|
265
|
+
* multiselect -> select, starting at the first chosen value
|
|
266
|
+
* list -> textarea, one item per line
|
|
267
|
+
*/
|
|
268
|
+
export declare function foldFormField(field: FormField): FormField;
|
|
269
|
+
type FallbackNode = {
|
|
270
|
+
type: 'text';
|
|
271
|
+
value: string;
|
|
272
|
+
muted: boolean;
|
|
273
|
+
} | {
|
|
274
|
+
type: 'markdown';
|
|
275
|
+
value: string;
|
|
276
|
+
} | {
|
|
277
|
+
type: 'table';
|
|
278
|
+
columns: Array<{
|
|
279
|
+
key: string;
|
|
280
|
+
label: string;
|
|
281
|
+
}>;
|
|
282
|
+
rows: Array<{
|
|
283
|
+
key: string;
|
|
284
|
+
cells: Record<string, {
|
|
285
|
+
kind: 'text';
|
|
286
|
+
value: string;
|
|
287
|
+
}>;
|
|
288
|
+
}>;
|
|
289
|
+
empty: string;
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* What a node becomes on a client that cannot draw it. It keeps what the node
|
|
293
|
+
* SAYS; it cannot keep what the node DOES, so a pager, a select or an alert's
|
|
294
|
+
* button is not pressable in the fallback. Times are written in `timeZone` (the
|
|
295
|
+
* screen's; a calendar's own wins), or in UTC when there is none.
|
|
296
|
+
*/
|
|
297
|
+
export declare function fallbackNode(node: ScreenRootNode | {
|
|
298
|
+
type: string;
|
|
299
|
+
}, timeZone?: string): FallbackNode;
|
|
300
|
+
/** Module text is plain text: escape it so it reads the same inside a markdown fallback. */
|
|
301
|
+
export declare function escapeMarkdown(value: string): string;
|
|
302
|
+
export {};
|