@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
package/dist/edit.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Renderer-side helpers for editing one block in place (`edit` on text, markdown, details and a
|
|
2
|
+
// table column). They live in the SDK because core and every client draw the edit: what Save
|
|
3
|
+
// posts, what a field starts with, and which cells can be edited must be one rule, or the web
|
|
4
|
+
// saves a field the phone would have left alone.
|
|
5
|
+
/** The edit control's accessible name and tooltip when the module gave no `label`. */
|
|
6
|
+
export const INLINE_EDIT_DEFAULT_LABEL = 'Edit';
|
|
7
|
+
/** The edit control's accessible name and tooltip: the module's `label`, or "Edit". */
|
|
8
|
+
export function inlineEditLabel(edit) {
|
|
9
|
+
return edit.label?.trim() || INLINE_EDIT_DEFAULT_LABEL;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The ONE action press Save posts: the edit's `action`, its `input`, and each field's key set to
|
|
13
|
+
* its new value. A value wins over `input` on the same key, because it is what the reader just
|
|
14
|
+
* typed.
|
|
15
|
+
*/
|
|
16
|
+
export function inlineEditPress(edit, values) {
|
|
17
|
+
return { kind: 'action', action: edit.action, input: { ...edit.input, ...values } };
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Save for one table cell: `row` is the row's key, and the column's key holds the new value.
|
|
21
|
+
*/
|
|
22
|
+
export function tableCellEditPress(edit, column, rowKey, value) {
|
|
23
|
+
return inlineEditPress(edit, { row: rowKey, [column]: value });
|
|
24
|
+
}
|
|
25
|
+
/** True for a cell an editable column lets the reader change: a `text` or `badge` cell. */
|
|
26
|
+
export function cellEditable(cell) {
|
|
27
|
+
return cell?.kind === 'text' || cell?.kind === 'badge';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* What a field starts with. An explicit `start` (a details item's `edit.value`) wins. With
|
|
31
|
+
* options, the option whose value or label is the shown text (a badge shows the label), or the
|
|
32
|
+
* first option. Without, the shown text.
|
|
33
|
+
*/
|
|
34
|
+
export function inlineEditStart(shown, options, start) {
|
|
35
|
+
if (start !== undefined)
|
|
36
|
+
return start;
|
|
37
|
+
if (!options || options.length === 0)
|
|
38
|
+
return shown;
|
|
39
|
+
return (options.find((o) => o.value === shown)?.value ??
|
|
40
|
+
options.find((o) => o.label === shown)?.value ??
|
|
41
|
+
options[0].value);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* What each field of an editable `details` block starts with, by field key. Items without `edit`
|
|
45
|
+
* (and date items, which cannot be edited) are left out.
|
|
46
|
+
*/
|
|
47
|
+
export function detailsEditStart(items) {
|
|
48
|
+
const start = {};
|
|
49
|
+
for (const item of items) {
|
|
50
|
+
if (!item.edit || !cellEditable(item.value))
|
|
51
|
+
continue;
|
|
52
|
+
const shown = typeof item.value.value === 'string' ? item.value.value : '';
|
|
53
|
+
start[item.edit.field] = inlineEditStart(shown, item.edit.options, item.edit.value);
|
|
54
|
+
}
|
|
55
|
+
return start;
|
|
56
|
+
}
|
|
57
|
+
/** True when any field differs from what it started with: an edit that holds the screen still. */
|
|
58
|
+
export function inlineEditDirty(start, draft) {
|
|
59
|
+
return Object.keys({ ...start, ...draft }).some((key) => (start[key] ?? '') !== (draft[key] ?? ''));
|
|
60
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ValuePress } from './schema.js';
|
|
2
|
+
/**
|
|
3
|
+
* How long a search box waits after the last keystroke before it fires. The
|
|
4
|
+
* renderer decides this, not the module, so every app feels the same.
|
|
5
|
+
*/
|
|
6
|
+
export declare const SEARCH_DEBOUNCE_MS = 300;
|
|
7
|
+
/**
|
|
8
|
+
* The shortest non-empty query a pause fires. One letter matches most of a
|
|
9
|
+
* list and costs a reload; Enter fires any length, and clearing always fires.
|
|
10
|
+
*/
|
|
11
|
+
export declare const SEARCH_MIN_LENGTH = 2;
|
|
12
|
+
/** "red,amber" → ['red', 'amber']. Empty or absent → []. Blank parts are dropped. */
|
|
13
|
+
export declare function splitValues(param: string | null | undefined): string[];
|
|
14
|
+
/**
|
|
15
|
+
* The set of chips that are on after `value` is pressed: added when it was off,
|
|
16
|
+
* removed when it was on. Kept in option order, so the same set always joins
|
|
17
|
+
* to the same param (one URL per filter, whatever order the chips were pressed).
|
|
18
|
+
*/
|
|
19
|
+
export declare function toggleValue(values: readonly string[], value: string, options: ReadonlyArray<{
|
|
20
|
+
value: string;
|
|
21
|
+
}>): string[];
|
|
22
|
+
/**
|
|
23
|
+
* The press a set of chips fires: the declared press with the whole set merged
|
|
24
|
+
* in under `name` — comma-joined into `params` for a navigate ('' when none is
|
|
25
|
+
* on), an array of strings into `input` for an action.
|
|
26
|
+
*/
|
|
27
|
+
export declare function pressWithValues(press: ValuePress, name: string, values: readonly string[]): ValuePress;
|
|
28
|
+
/** What a renderer needs of a form to treat it as a live search box. */
|
|
29
|
+
export interface LiveSearchFormLike {
|
|
30
|
+
fields: ReadonlyArray<{
|
|
31
|
+
kind: string;
|
|
32
|
+
name: string;
|
|
33
|
+
label: string;
|
|
34
|
+
value?: string;
|
|
35
|
+
}>;
|
|
36
|
+
onChange?: ValuePress;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The one text field of a live search form, or undefined when the form is not
|
|
40
|
+
* one (no `onChange`, or not exactly one text field), so the renderer draws it
|
|
41
|
+
* as an ordinary form.
|
|
42
|
+
*/
|
|
43
|
+
export declare function liveSearchField<F extends LiveSearchFormLike>(form: F): F['fields'][number] | undefined;
|
|
44
|
+
/** What made a search box want to fire. */
|
|
45
|
+
export type SearchTrigger = 'pause' | 'enter' | 'clear';
|
|
46
|
+
/**
|
|
47
|
+
* Whether a search box fires `text` now, given the query it last sent (or the
|
|
48
|
+
* one the screen was loaded with). Nothing fires when the trimmed text is the
|
|
49
|
+
* query already applied. A pause does not fire a non-empty query shorter than
|
|
50
|
+
* SEARCH_MIN_LENGTH; Enter and the clear button always fire a change.
|
|
51
|
+
*/
|
|
52
|
+
export declare function searchFires(text: string, sent: string, trigger: SearchTrigger): boolean;
|
|
53
|
+
/** The press a live search form fires for `text`: trimmed, under the field's name. */
|
|
54
|
+
export declare function searchPress(form: LiveSearchFormLike, text: string): ValuePress | undefined;
|
package/dist/filters.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Renderer-side helpers for a live filter bar: toggle chips (a `multiple`
|
|
2
|
+
// select) and a live search box (a `form` with `onChange`). They live in the
|
|
3
|
+
// SDK, not in a renderer, because core and every client draw these controls:
|
|
4
|
+
// how several chosen values travel in a string-only params map, and when a
|
|
5
|
+
// search box fires, must be one rule, or the same screen filters differently on
|
|
6
|
+
// web and in core.
|
|
7
|
+
import { pressWithValue } from './pager.js';
|
|
8
|
+
/**
|
|
9
|
+
* How long a search box waits after the last keystroke before it fires. The
|
|
10
|
+
* renderer decides this, not the module, so every app feels the same.
|
|
11
|
+
*/
|
|
12
|
+
export const SEARCH_DEBOUNCE_MS = 300;
|
|
13
|
+
/**
|
|
14
|
+
* The shortest non-empty query a pause fires. One letter matches most of a
|
|
15
|
+
* list and costs a reload; Enter fires any length, and clearing always fires.
|
|
16
|
+
*/
|
|
17
|
+
export const SEARCH_MIN_LENGTH = 2;
|
|
18
|
+
/** "red,amber" → ['red', 'amber']. Empty or absent → []. Blank parts are dropped. */
|
|
19
|
+
export function splitValues(param) {
|
|
20
|
+
if (!param)
|
|
21
|
+
return [];
|
|
22
|
+
return param
|
|
23
|
+
.split(',')
|
|
24
|
+
.map((part) => part.trim())
|
|
25
|
+
.filter((part) => part !== '');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The set of chips that are on after `value` is pressed: added when it was off,
|
|
29
|
+
* removed when it was on. Kept in option order, so the same set always joins
|
|
30
|
+
* to the same param (one URL per filter, whatever order the chips were pressed).
|
|
31
|
+
*/
|
|
32
|
+
export function toggleValue(values, value, options) {
|
|
33
|
+
const on = new Set(values);
|
|
34
|
+
if (on.has(value))
|
|
35
|
+
on.delete(value);
|
|
36
|
+
else
|
|
37
|
+
on.add(value);
|
|
38
|
+
return options.map((option) => option.value).filter((v) => on.has(v));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The press a set of chips fires: the declared press with the whole set merged
|
|
42
|
+
* in under `name` — comma-joined into `params` for a navigate ('' when none is
|
|
43
|
+
* on), an array of strings into `input` for an action.
|
|
44
|
+
*/
|
|
45
|
+
export function pressWithValues(press, name, values) {
|
|
46
|
+
if (press.kind === 'navigate')
|
|
47
|
+
return pressWithValue(press, name, values.join(','));
|
|
48
|
+
return { ...press, input: { ...press.input, [name]: [...values] } };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The one text field of a live search form, or undefined when the form is not
|
|
52
|
+
* one (no `onChange`, or not exactly one text field), so the renderer draws it
|
|
53
|
+
* as an ordinary form.
|
|
54
|
+
*/
|
|
55
|
+
export function liveSearchField(form) {
|
|
56
|
+
if (!form.onChange || form.fields.length !== 1)
|
|
57
|
+
return undefined;
|
|
58
|
+
const only = form.fields[0];
|
|
59
|
+
return only && only.kind === 'text' ? only : undefined;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Whether a search box fires `text` now, given the query it last sent (or the
|
|
63
|
+
* one the screen was loaded with). Nothing fires when the trimmed text is the
|
|
64
|
+
* query already applied. A pause does not fire a non-empty query shorter than
|
|
65
|
+
* SEARCH_MIN_LENGTH; Enter and the clear button always fire a change.
|
|
66
|
+
*/
|
|
67
|
+
export function searchFires(text, sent, trigger) {
|
|
68
|
+
const query = text.trim();
|
|
69
|
+
if (query === sent.trim())
|
|
70
|
+
return false;
|
|
71
|
+
if (trigger === 'pause' && query.length > 0 && query.length < SEARCH_MIN_LENGTH)
|
|
72
|
+
return false;
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
/** The press a live search form fires for `text`: trimmed, under the field's name. */
|
|
76
|
+
export function searchPress(form, text) {
|
|
77
|
+
const field = liveSearchField(form);
|
|
78
|
+
if (!field || !form.onChange)
|
|
79
|
+
return undefined;
|
|
80
|
+
return pressWithValue(form.onChange, field.name, text.trim());
|
|
81
|
+
}
|
package/dist/form.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Press } from './schema.js';
|
|
2
|
+
export type FormFieldKind = 'text' | 'textarea' | 'select' | 'number' | 'date' | 'datetime' | 'checkbox' | 'multiselect' | 'combobox' | 'list' | 'radio';
|
|
2
3
|
export interface FormFieldLike {
|
|
3
4
|
kind: FormFieldKind;
|
|
4
5
|
value?: string;
|
|
@@ -17,3 +18,46 @@ export declare function localInputToIso(local: string): string;
|
|
|
17
18
|
export declare function initialDraft(field: FormFieldLike): FormDraft;
|
|
18
19
|
/** What the module receives for a field. See the form schema for each kind. */
|
|
19
20
|
export declare function postedValue(field: Pick<FormFieldLike, 'kind'>, draft: FormDraft): string | number | boolean | string[] | null;
|
|
21
|
+
/** One send button of a form: its `submit` or one of its `secondary`. */
|
|
22
|
+
export interface FormSubmit {
|
|
23
|
+
label: string;
|
|
24
|
+
action: string;
|
|
25
|
+
input?: Record<string, unknown>;
|
|
26
|
+
confirm?: string;
|
|
27
|
+
/** Check required fields before posting. */
|
|
28
|
+
validate: boolean;
|
|
29
|
+
/** The form's own `submit`: Enter presses it, and it is drawn as the main button. */
|
|
30
|
+
primary: boolean;
|
|
31
|
+
/** Drawn in its place but refuses the press, as a disabled button does. */
|
|
32
|
+
disabled: boolean;
|
|
33
|
+
/** Why it is disabled; present only while it is. */
|
|
34
|
+
disabledReason?: string;
|
|
35
|
+
}
|
|
36
|
+
interface SendLike {
|
|
37
|
+
label: string;
|
|
38
|
+
action: string;
|
|
39
|
+
confirm?: string;
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
disabledReason?: string;
|
|
42
|
+
}
|
|
43
|
+
/** Every send button of a form in drawing order: `submit` first, then `secondary`. */
|
|
44
|
+
export declare function formSubmits(form: {
|
|
45
|
+
submit: SendLike;
|
|
46
|
+
secondary?: ReadonlyArray<SendLike & {
|
|
47
|
+
input?: Record<string, unknown>;
|
|
48
|
+
validate?: boolean;
|
|
49
|
+
}>;
|
|
50
|
+
}): FormSubmit[];
|
|
51
|
+
/**
|
|
52
|
+
* The send a submit of the form presses, or null when it must not post: the button the reader
|
|
53
|
+
* pressed (`at`, its index in `formSubmits`), or the primary for Enter in a field. A disabled send
|
|
54
|
+
* posts nothing, and Enter with a disabled primary does not fall through to a secondary.
|
|
55
|
+
*/
|
|
56
|
+
export declare function chosenSubmit(submits: readonly FormSubmit[], at: number | null | undefined): FormSubmit | null;
|
|
57
|
+
/**
|
|
58
|
+
* The press a form's send button fires: its action, with its own static input
|
|
59
|
+
* under the posted field values (a field wins over a same-named key), and its
|
|
60
|
+
* confirm.
|
|
61
|
+
*/
|
|
62
|
+
export declare function formPress(submit: Pick<FormSubmit, 'action' | 'input' | 'confirm'>, values: Record<string, unknown>): Press;
|
|
63
|
+
export {};
|
package/dist/form.js
CHANGED
|
@@ -51,3 +51,42 @@ export function postedValue(field, draft) {
|
|
|
51
51
|
return typeof draft === 'string' ? draft : '';
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
+
/** Every send button of a form in drawing order: `submit` first, then `secondary`. */
|
|
55
|
+
export function formSubmits(form) {
|
|
56
|
+
// A reason is only for a disabled send, as on a button: an enabled one says nothing.
|
|
57
|
+
const state = ({ disabled, disabledReason, ...rest }) => ({
|
|
58
|
+
...rest,
|
|
59
|
+
disabled: disabled === true,
|
|
60
|
+
...(disabled && disabledReason ? { disabledReason } : {}),
|
|
61
|
+
});
|
|
62
|
+
return [
|
|
63
|
+
{ ...state(form.submit), validate: true, primary: true },
|
|
64
|
+
...(form.secondary ?? []).map(({ validate, ...more }) => ({
|
|
65
|
+
...state(more),
|
|
66
|
+
validate: validate ?? true,
|
|
67
|
+
primary: false,
|
|
68
|
+
})),
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The send a submit of the form presses, or null when it must not post: the button the reader
|
|
73
|
+
* pressed (`at`, its index in `formSubmits`), or the primary for Enter in a field. A disabled send
|
|
74
|
+
* posts nothing, and Enter with a disabled primary does not fall through to a secondary.
|
|
75
|
+
*/
|
|
76
|
+
export function chosenSubmit(submits, at) {
|
|
77
|
+
const chosen = (at == null ? undefined : submits[at]) ?? submits[0];
|
|
78
|
+
return chosen && !chosen.disabled ? chosen : null;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The press a form's send button fires: its action, with its own static input
|
|
82
|
+
* under the posted field values (a field wins over a same-named key), and its
|
|
83
|
+
* confirm.
|
|
84
|
+
*/
|
|
85
|
+
export function formPress(submit, values) {
|
|
86
|
+
return {
|
|
87
|
+
kind: 'action',
|
|
88
|
+
action: submit.action,
|
|
89
|
+
input: { ...submit.input, ...values },
|
|
90
|
+
...(submit.confirm ? { confirm: submit.confirm } : {}),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** The parts of a heatmap node the scale reads. */
|
|
2
|
+
export interface HeatmapLike {
|
|
3
|
+
rows: ReadonlyArray<{
|
|
4
|
+
values: ReadonlyArray<number | null>;
|
|
5
|
+
}>;
|
|
6
|
+
min?: number;
|
|
7
|
+
max?: number;
|
|
8
|
+
}
|
|
9
|
+
/** The two ends of a heatmap's scale. */
|
|
10
|
+
export interface HeatmapRange {
|
|
11
|
+
low: number;
|
|
12
|
+
high: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The ends of the scale: the node's `min` and `max`, and where either is left off, the smallest
|
|
16
|
+
* or largest value present. A grid with no values at all gets 0 to 1.
|
|
17
|
+
*/
|
|
18
|
+
export declare function heatmapRange(node: HeatmapLike): HeatmapRange;
|
|
19
|
+
/**
|
|
20
|
+
* How strongly a cell is shaded, from 0 (the low end) to 1 (the high end), or null for a cell
|
|
21
|
+
* with no data. A value outside the range takes the nearer end. When every value is the same,
|
|
22
|
+
* each cell is shaded fully.
|
|
23
|
+
*/
|
|
24
|
+
export declare function heatmapIntensity(value: number | null, range: HeatmapRange): number | null;
|
|
25
|
+
/**
|
|
26
|
+
* The share of the tone a cell's background mixes in, as a percent: 8 at the low end so a low
|
|
27
|
+
* value still reads as a cell with data, 80 at the high end.
|
|
28
|
+
*/
|
|
29
|
+
export declare function heatmapMixPercent(intensity: number): number;
|
|
30
|
+
/**
|
|
31
|
+
* Whether a cell's number is written in the surface colour the tone is mixed into (the card's),
|
|
32
|
+
* rather than the page's text colour: on a strong shade that is what contrasts, in either theme.
|
|
33
|
+
*/
|
|
34
|
+
export declare function heatmapStrong(intensity: number): boolean;
|
|
35
|
+
/** A cell's words: "92%", or "No data" for null. */
|
|
36
|
+
export declare function heatmapCellText(value: number | null, suffix?: string): string;
|
package/dist/heatmap.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// How a heatmap cell is shaded and written, shared by core and the clients so every renderer
|
|
2
|
+
// shades the same number the same way.
|
|
3
|
+
/**
|
|
4
|
+
* The ends of the scale: the node's `min` and `max`, and where either is left off, the smallest
|
|
5
|
+
* or largest value present. A grid with no values at all gets 0 to 1.
|
|
6
|
+
*/
|
|
7
|
+
export function heatmapRange(node) {
|
|
8
|
+
let low = Infinity;
|
|
9
|
+
let high = -Infinity;
|
|
10
|
+
for (const row of node.rows) {
|
|
11
|
+
for (const value of row.values) {
|
|
12
|
+
if (value === null)
|
|
13
|
+
continue;
|
|
14
|
+
if (value < low)
|
|
15
|
+
low = value;
|
|
16
|
+
if (value > high)
|
|
17
|
+
high = value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (low === Infinity) {
|
|
21
|
+
low = 0;
|
|
22
|
+
high = 1;
|
|
23
|
+
}
|
|
24
|
+
return { low: node.min ?? low, high: node.max ?? high };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* How strongly a cell is shaded, from 0 (the low end) to 1 (the high end), or null for a cell
|
|
28
|
+
* with no data. A value outside the range takes the nearer end. When every value is the same,
|
|
29
|
+
* each cell is shaded fully.
|
|
30
|
+
*/
|
|
31
|
+
export function heatmapIntensity(value, range) {
|
|
32
|
+
if (value === null)
|
|
33
|
+
return null;
|
|
34
|
+
const span = range.high - range.low;
|
|
35
|
+
if (!(span > 0))
|
|
36
|
+
return 1;
|
|
37
|
+
return Math.min(1, Math.max(0, (value - range.low) / span));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The share of the tone a cell's background mixes in, as a percent: 8 at the low end so a low
|
|
41
|
+
* value still reads as a cell with data, 80 at the high end.
|
|
42
|
+
*/
|
|
43
|
+
export function heatmapMixPercent(intensity) {
|
|
44
|
+
return Math.round(8 + intensity * 72);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Whether a cell's number is written in the surface colour the tone is mixed into (the card's),
|
|
48
|
+
* rather than the page's text colour: on a strong shade that is what contrasts, in either theme.
|
|
49
|
+
*/
|
|
50
|
+
export function heatmapStrong(intensity) {
|
|
51
|
+
return intensity >= 0.6;
|
|
52
|
+
}
|
|
53
|
+
/** A cell's words: "92%", or "No data" for null. */
|
|
54
|
+
export function heatmapCellText(value, suffix) {
|
|
55
|
+
return value === null ? 'No data' : `${value}${suffix ?? ''}`;
|
|
56
|
+
}
|
package/dist/icon.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** The icon names a heading, a section title and a button take. Each is a Lucide icon on web. */
|
|
2
|
+
export declare const UI_ICONS: readonly ["phone", "mail", "message", "calendar", "user", "building", "bot", "wrench", "note", "pencil", "stethoscope", "clipboard", "camera", "image", "check", "alert", "clock", "plus", "trash"];
|
|
3
|
+
export type UiIcon = (typeof UI_ICONS)[number];
|
|
4
|
+
/** The button fields `buttonIcon` reads. Loose, so a client whose installed SDK predates them can pass its own node. */
|
|
5
|
+
export interface IconButtonLike {
|
|
6
|
+
label?: string;
|
|
7
|
+
icon?: string;
|
|
8
|
+
iconOnly?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* How a renderer draws a button's icon: `icon` is a name from the list or undefined (an unknown
|
|
12
|
+
* name draws nothing), and `iconOnly` is true only with an icon and a label to name it by, so a
|
|
13
|
+
* button is never drawn as a bare glyph nobody can name.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buttonIcon(button: IconButtonLike): {
|
|
16
|
+
icon?: UiIcon;
|
|
17
|
+
iconOnly: boolean;
|
|
18
|
+
};
|
|
19
|
+
/** A name from the list, or undefined. */
|
|
20
|
+
export declare function uiIconOf(value: unknown): UiIcon | undefined;
|
package/dist/icon.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// The icons a `heading`, a `section` title and a `button` may carry. A closed list, not free text,
|
|
2
|
+
// like the timeline's: every renderer (core, web, mobile) maps each name to its own drawing, so a
|
|
3
|
+
// name outside the list would draw nothing on one platform and something on another. Add a name
|
|
4
|
+
// here only with a drawing on every renderer.
|
|
5
|
+
//
|
|
6
|
+
// It starts from the timeline's icons, so a module uses one vocabulary, and adds the glyphs a
|
|
7
|
+
// clinic screen needs to tell its parts apart at a glance: a note's five blocks (assessment,
|
|
8
|
+
// plan, photos, follow-up, alerts), an edit pencil, a camera.
|
|
9
|
+
//
|
|
10
|
+
// On a heading or a section title the icon is decorative: the words say what the part is, so it
|
|
11
|
+
// is hidden from screen readers. On an icon-only button the label is still required: it is the
|
|
12
|
+
// button's accessible name and its tooltip, and what an app that predates `icon` shows instead.
|
|
13
|
+
import { TIMELINE_ICONS } from './timeline.js';
|
|
14
|
+
/** The icon names a heading, a section title and a button take. Each is a Lucide icon on web. */
|
|
15
|
+
export const UI_ICONS = [
|
|
16
|
+
...TIMELINE_ICONS,
|
|
17
|
+
'pencil',
|
|
18
|
+
'stethoscope',
|
|
19
|
+
'clipboard',
|
|
20
|
+
'camera',
|
|
21
|
+
'image',
|
|
22
|
+
'check',
|
|
23
|
+
'alert',
|
|
24
|
+
'clock',
|
|
25
|
+
'plus',
|
|
26
|
+
'trash',
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* How a renderer draws a button's icon: `icon` is a name from the list or undefined (an unknown
|
|
30
|
+
* name draws nothing), and `iconOnly` is true only with an icon and a label to name it by, so a
|
|
31
|
+
* button is never drawn as a bare glyph nobody can name.
|
|
32
|
+
*/
|
|
33
|
+
export function buttonIcon(button) {
|
|
34
|
+
const icon = uiIconOf(button.icon);
|
|
35
|
+
const named = typeof button.label === 'string' && button.label.trim() !== '';
|
|
36
|
+
return { ...(icon ? { icon } : {}), iconOnly: !!icon && button.iconOnly === true && named };
|
|
37
|
+
}
|
|
38
|
+
/** A name from the list, or undefined. */
|
|
39
|
+
export function uiIconOf(value) {
|
|
40
|
+
return typeof value === 'string' && UI_ICONS.includes(value)
|
|
41
|
+
? value
|
|
42
|
+
: undefined;
|
|
43
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,48 @@
|
|
|
1
|
-
export { UI_SCHEMA_VERSION, pressSchema, buttonSchema, cellSchema, nodeSchema, screenPresentationSchema, screenTreeSchema, actionResultSchema, screenLoadRequestSchema, screenLoadResponseSchema, screenActionRequestSchema, interfaceFamilySchema, surfaceDefinitionSchema, surfaceSchema, screenRegistrationSchema, uiManifestSchema, jobIdSchema, jobStatusSchema, screenJobRequestSchema, mediaPathSchema, } from './schema.js';
|
|
2
|
-
export type { Press, ScreenPresentation, ScreenNode, ScreenRootNode, ScreenTree, ActionResult, InterfaceFamily, SurfaceDefinition, Surface, ScreenRegistration, UiManifest, ScreenTreeInput, ActionResultInput, JobStatus, JobStatusInput, } from './schema.js';
|
|
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';
|
|
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
|
+
export { BADGE_DISPLAY_MAX, answerBadges, badgeSpokenLabel, badgeText, defineBadges, parseScreenBadges, } from './badges.js';
|
|
4
|
+
export type { BadgesHandler } from './badges.js';
|
|
5
|
+
export { CHART_POINTS_MAX, thinChart, thinnedIndices } from './chart.js';
|
|
6
|
+
export { TABS_MAX_1_4_0, foldTabs, onlyTabParamsDiffer, openingTab, tabParamsOf } from './tabs.js';
|
|
3
7
|
export { childNodesOf, KNOWN_CONTAINERS, KNOWN_NODE_TYPES } from './nodes.js';
|
|
4
8
|
export type { ContainerChild, ContainerNodeType, KnownNodeType } from './nodes.js';
|
|
5
9
|
export { pageRangeLabel, pageWindow, pressWithValue } from './pager.js';
|
|
10
|
+
export { MARKDOWN_LINKS_MAX, SCREEN_LINK_SCHEME, isScreenLink, resolveScreenLink, screenLinkRef, withoutScreenLinks, } from './markdown-links.js';
|
|
11
|
+
export { foldTableCell, isNewerTableCell, meterLabel, meterPercent } from './cells.js';
|
|
12
|
+
export { opensInDrawer, screenPlace } from './drawer.js';
|
|
13
|
+
export type { ScreenPlace } from './drawer.js';
|
|
14
|
+
export { SEARCH_DEBOUNCE_MS, SEARCH_MIN_LENGTH, liveSearchField, pressWithValues, searchFires, searchPress, splitValues, toggleValue, } from './filters.js';
|
|
15
|
+
export type { LiveSearchFormLike, SearchTrigger } from './filters.js';
|
|
16
|
+
export { CHANNEL_BUILDS, CLIENT_RELEASES, CORE_DRAWN_NODE_TYPES, CURRENT_SCHEMA_CLAIM, CURRENT_UI_SDK_SNAPSHOT, FLOOR_NODE_SET, GENERIC_FALLBACK_TEXT, SAFETY_FALLBACK_TEXT, UI_SDK_SNAPSHOTS, fallbackNode, isChatPress, latestRelease, negotiateTree, nodeSetForClaim, nodeSetForClient, uiSchemaClaimOf, } from './negotiate.js';
|
|
17
|
+
export type { ChannelBuild, ClientRelease, ClientTarget, Fallback, NegotiatedTree, NodeSet, UiFeature, UiSchemaClaim, UiSdkSnapshot, } from './negotiate.js';
|
|
18
|
+
export { PUBLISHED_PARSERS, type PublishedParser } from './published/index.js';
|
|
6
19
|
export { MEDIA_CONTENT_TYPES, mediaContentType, mediaPathOf } from './media.js';
|
|
7
|
-
export {
|
|
8
|
-
export type {
|
|
20
|
+
export { PLAYBACK_RATES, SEEK_AT_MAX, formatPlayhead, isSeekPress, nextPlaybackRate, playbackRateLabel, playheadWords, seekTarget, seeksIn, } from './player.js';
|
|
21
|
+
export type { PlaybackRate, PlayerKey, SeekReference } from './player.js';
|
|
22
|
+
export { heatmapCellText, heatmapIntensity, heatmapMixPercent, heatmapRange, heatmapStrong, } from './heatmap.js';
|
|
23
|
+
export type { HeatmapLike, HeatmapRange } from './heatmap.js';
|
|
24
|
+
export { chosenSubmit, formPress, formSubmits, initialDraft, isoToLocalInput, localInputToIso, postedValue, } from './form.js';
|
|
25
|
+
export type { FormDraft, FormFieldKind, FormFieldLike, FormSubmit } from './form.js';
|
|
26
|
+
export { stackedBarParts, stackedBarValue } from './stacked-bar.js';
|
|
27
|
+
export type { StackedBarLike, StackedBarParts, StackedBarSegmentPart } from './stacked-bar.js';
|
|
28
|
+
export { statTrend } from './stat.js';
|
|
29
|
+
export type { StatTrend, StatTrendFeel, StatTrendLike } from './stat.js';
|
|
30
|
+
export { createSectionMemory, sectionBadge, sectionCollapsible, sectionColumnWeights, sectionGridTemplate, sectionScrollHeight, } from './section.js';
|
|
31
|
+
export type { SectionBadge, SectionLike, SectionMemory, SectionScrollHeight } from './section.js';
|
|
32
|
+
export { tableRowEmphasis } from './table-row.js';
|
|
33
|
+
export type { TableRowEmphasis, TableRowLike, TableRowTone } from './table-row.js';
|
|
34
|
+
export { TIMELINE_ICONS, timelineIconWord } from './timeline.js';
|
|
35
|
+
export type { TimelineIcon } from './timeline.js';
|
|
36
|
+
export { cellEditable, detailsEditStart, INLINE_EDIT_DEFAULT_LABEL, inlineEditDirty, inlineEditLabel, inlineEditPress, inlineEditStart, tableCellEditPress, } from './edit.js';
|
|
37
|
+
export type { EditableCellLike, EditableDetailsItemLike, InlineEditLike, InlineEditOption, InlineEditPress, } from './edit.js';
|
|
38
|
+
export { UI_ICONS, buttonIcon, uiIconOf } from './icon.js';
|
|
39
|
+
export type { IconButtonLike, UiIcon } from './icon.js';
|
|
9
40
|
export { composerPress, composerShortcut } from './composer.js';
|
|
10
41
|
export type { ComposerKey, ComposerShortcut } from './composer.js';
|
|
11
42
|
export { addDays, calendarDays, dayStartMs, entriesOnDay, intlTimeZone, isTimeZone, placeEntries, shiftCalendarDate, slotIsFree, slotTimes, todayIn, } from './calendar.js';
|
|
12
43
|
export type { CalendarEntryLike, CalendarView, CalendarZone, PlacedEntry } from './calendar.js';
|
|
44
|
+
export { dateTimeFormatOptions, formatMoment, isDateLocale, mergeDateFormat, } from './date-format.js';
|
|
45
|
+
export type { DateFormat, MomentStyle } from './date-format.js';
|
|
13
46
|
export { DATASET_FIELD_ROLES, DATASET_FIELD_TYPES, DATASET_LIMITS, answerDatasetCreate, answerDatasetQuery, datasetCreateInput, datasetCreateRequestSchema, datasetCreateResponseSchema, datasetDeclarationSchema, datasetFieldSchema, datasetManifestSchema, datasetQueryRequestSchema, datasetQueryResponseSchema, datasetRequestIssues, datasetResponseIssues, datasetRowSchema, defineDataset, } from './dataset.js';
|
|
14
47
|
export type { DatasetCell, DatasetCreateCheck, DatasetCreateHandler, DatasetCreateRequest, DatasetCreateResponse, DatasetCreateResponseInput, DatasetDeclaration, DatasetDeclarationInput, DatasetDefinition, DatasetField, DatasetFieldRole, DatasetFieldType, DatasetQueryHandler, DatasetQueryRequest, DatasetQueryRequestInput, DatasetQueryResponse, DatasetQueryResponseInput, DatasetRow, } from './dataset.js';
|
|
15
48
|
export { DATASET_CREATE_ACTION, DATASET_LAYOUTS, DATASET_MAX_PAGE, DATASET_NEW_PARAM, DATASET_RECORD_SECTION, compileDatasetCreate, compileDatasetNode, compileDatasetRecord, compileDatasetRecordPanel, datasetCell, datasetNodeIssues, datasetNodeSchema, datasetPage, datasetQueryFor, datasetRecordKey, datasetRecordQuery, } from './dataset-view.js';
|
package/dist/index.js
CHANGED
|
@@ -6,13 +6,32 @@
|
|
|
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, screenPresentationSchema, screenTreeSchema, actionResultSchema, screenLoadRequestSchema, screenLoadResponseSchema, screenActionRequestSchema, interfaceFamilySchema, surfaceDefinitionSchema, surfaceSchema, screenRegistrationSchema, uiManifestSchema, jobIdSchema, jobStatusSchema, screenJobRequestSchema, mediaPathSchema, } from './schema.js';
|
|
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';
|
|
10
|
+
export { BADGE_DISPLAY_MAX, answerBadges, badgeSpokenLabel, badgeText, defineBadges, parseScreenBadges, } from './badges.js';
|
|
11
|
+
export { CHART_POINTS_MAX, thinChart, thinnedIndices } from './chart.js';
|
|
12
|
+
export { TABS_MAX_1_4_0, foldTabs, onlyTabParamsDiffer, openingTab, tabParamsOf } from './tabs.js';
|
|
10
13
|
export { childNodesOf, KNOWN_CONTAINERS, KNOWN_NODE_TYPES } from './nodes.js';
|
|
11
14
|
export { pageRangeLabel, pageWindow, pressWithValue } from './pager.js';
|
|
15
|
+
export { MARKDOWN_LINKS_MAX, SCREEN_LINK_SCHEME, isScreenLink, resolveScreenLink, screenLinkRef, withoutScreenLinks, } from './markdown-links.js';
|
|
16
|
+
export { foldTableCell, isNewerTableCell, meterLabel, meterPercent } from './cells.js';
|
|
17
|
+
export { opensInDrawer, screenPlace } from './drawer.js';
|
|
18
|
+
export { SEARCH_DEBOUNCE_MS, SEARCH_MIN_LENGTH, liveSearchField, pressWithValues, searchFires, searchPress, splitValues, toggleValue, } from './filters.js';
|
|
19
|
+
export { CHANNEL_BUILDS, CLIENT_RELEASES, CORE_DRAWN_NODE_TYPES, CURRENT_SCHEMA_CLAIM, CURRENT_UI_SDK_SNAPSHOT, FLOOR_NODE_SET, GENERIC_FALLBACK_TEXT, SAFETY_FALLBACK_TEXT, UI_SDK_SNAPSHOTS, fallbackNode, isChatPress, latestRelease, negotiateTree, nodeSetForClaim, nodeSetForClient, uiSchemaClaimOf, } from './negotiate.js';
|
|
20
|
+
export { PUBLISHED_PARSERS } from './published/index.js';
|
|
12
21
|
export { MEDIA_CONTENT_TYPES, mediaContentType, mediaPathOf } from './media.js';
|
|
13
|
-
export {
|
|
22
|
+
export { PLAYBACK_RATES, SEEK_AT_MAX, formatPlayhead, isSeekPress, nextPlaybackRate, playbackRateLabel, playheadWords, seekTarget, seeksIn, } from './player.js';
|
|
23
|
+
export { heatmapCellText, heatmapIntensity, heatmapMixPercent, heatmapRange, heatmapStrong, } from './heatmap.js';
|
|
24
|
+
export { chosenSubmit, formPress, formSubmits, initialDraft, isoToLocalInput, localInputToIso, postedValue, } from './form.js';
|
|
25
|
+
export { stackedBarParts, stackedBarValue } from './stacked-bar.js';
|
|
26
|
+
export { statTrend } from './stat.js';
|
|
27
|
+
export { createSectionMemory, sectionBadge, sectionCollapsible, sectionColumnWeights, sectionGridTemplate, sectionScrollHeight, } from './section.js';
|
|
28
|
+
export { tableRowEmphasis } from './table-row.js';
|
|
29
|
+
export { TIMELINE_ICONS, timelineIconWord } from './timeline.js';
|
|
30
|
+
export { cellEditable, detailsEditStart, INLINE_EDIT_DEFAULT_LABEL, inlineEditDirty, inlineEditLabel, inlineEditPress, inlineEditStart, tableCellEditPress, } from './edit.js';
|
|
31
|
+
export { UI_ICONS, buttonIcon, uiIconOf } from './icon.js';
|
|
14
32
|
export { composerPress, composerShortcut } from './composer.js';
|
|
15
33
|
export { addDays, calendarDays, dayStartMs, entriesOnDay, intlTimeZone, isTimeZone, placeEntries, shiftCalendarDate, slotIsFree, slotTimes, todayIn, } from './calendar.js';
|
|
34
|
+
export { dateTimeFormatOptions, formatMoment, isDateLocale, mergeDateFormat, } from './date-format.js';
|
|
16
35
|
export { DATASET_FIELD_ROLES, DATASET_FIELD_TYPES, DATASET_LIMITS, answerDatasetCreate, answerDatasetQuery, datasetCreateInput, datasetCreateRequestSchema, datasetCreateResponseSchema, datasetDeclarationSchema, datasetFieldSchema, datasetManifestSchema, datasetQueryRequestSchema, datasetQueryResponseSchema, datasetRequestIssues, datasetResponseIssues, datasetRowSchema, defineDataset, } from './dataset.js';
|
|
17
36
|
export { DATASET_CREATE_ACTION, DATASET_LAYOUTS, DATASET_MAX_PAGE, DATASET_NEW_PARAM, DATASET_RECORD_SECTION, compileDatasetCreate, compileDatasetNode, compileDatasetRecord, compileDatasetRecordPanel, datasetCell, datasetNodeIssues, datasetNodeSchema, datasetPage, datasetQueryFor, datasetRecordKey, datasetRecordQuery, } from './dataset-view.js';
|
|
18
37
|
export { CHAT_BLOCK_EXAMPLES, CHAT_BLOCK_FENCE, CHAT_BLOCK_LIMITS, CHAT_BLOCK_TYPES, chatBlocksGuide, checkChatBlocks, findChatBlockFences, parseChatBlocks, settleChatBlocks, } from './chat-blocks.js';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** The scheme a link to a screen is written with: `[words](screen:ref)`. */
|
|
2
|
+
export declare const SCREEN_LINK_SCHEME = "screen:";
|
|
3
|
+
/** The most refs one markdown node's `links` map holds. */
|
|
4
|
+
export declare const MARKDOWN_LINKS_MAX = 50;
|
|
5
|
+
/**
|
|
6
|
+
* True for any link written with the `screen:` scheme, in any case and with any spaces around
|
|
7
|
+
* it. A renderer never hands such a link to the browser or the phone as a URL: it is either a
|
|
8
|
+
* ref in the node's map (a press) or plain text.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isScreenLink(href: string | null | undefined): boolean;
|
|
11
|
+
/** The ref a `screen:` link names, or null when it is not exactly `screen:<slug>`. */
|
|
12
|
+
export declare function screenLinkRef(href: string | null | undefined): string | null;
|
|
13
|
+
/**
|
|
14
|
+
* The press a `screen:` link opens: the entry of `links` its ref names, or null. Only the map's
|
|
15
|
+
* own entries count (never `constructor` or anything else an object inherits), and only an entry
|
|
16
|
+
* that is a navigate press, so a renderer that is handed a loosely typed map still opens nothing
|
|
17
|
+
* else. Null means: draw the link's words as plain text.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveScreenLink<P extends {
|
|
20
|
+
kind: string;
|
|
21
|
+
}>(href: string | null | undefined, links: Readonly<Record<string, P>> | null | undefined): P | null;
|
|
22
|
+
/**
|
|
23
|
+
* The markdown with every `screen:` link turned into its words, for a client that does not draw
|
|
24
|
+
* them (negotiate.ts, `markdown.links`). An older renderer would draw such a link as a broken link
|
|
25
|
+
* or as the raw `[words](screen:ref)`; this way it reads as the sentence the module wrote. Inline
|
|
26
|
+
* links, autolinks and reference definitions (with the links that use them) are folded; code is
|
|
27
|
+
* left as written. Pure; returns the same string when there is nothing to fold.
|
|
28
|
+
*/
|
|
29
|
+
export declare function withoutScreenLinks(value: string): string;
|