@tai42/studio-sdk 20.0.0 → 22.0.0
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/components/chip-group.d.ts +42 -0
- package/dist/components/chip-group.d.ts.map +1 -0
- package/dist/components/chip-group.js +85 -0
- package/dist/components/chip-group.js.map +1 -0
- package/dist/components/components.css +16 -3
- package/dist/components/execution-key-picker.js.map +1 -1
- package/dist/components/extension-grouping.d.ts +8 -12
- package/dist/components/extension-grouping.d.ts.map +1 -1
- package/dist/components/extension-grouping.js +12 -15
- package/dist/components/extension-grouping.js.map +1 -1
- package/dist/components/extension-picker.d.ts +3 -4
- package/dist/components/extension-picker.d.ts.map +1 -1
- package/dist/components/extension-picker.js +10 -10
- package/dist/components/extension-picker.js.map +1 -1
- package/dist/components/fleet-report.d.ts +10 -5
- package/dist/components/fleet-report.d.ts.map +1 -1
- package/dist/components/fleet-report.js +20 -3
- package/dist/components/fleet-report.js.map +1 -1
- package/dist/components/json-tree-model.d.ts +19 -7
- package/dist/components/json-tree-model.d.ts.map +1 -1
- package/dist/components/json-tree-model.js +32 -8
- package/dist/components/json-tree-model.js.map +1 -1
- package/dist/components/json-tree.d.ts +10 -1
- package/dist/components/json-tree.d.ts.map +1 -1
- package/dist/components/json-tree.js +7 -4
- package/dist/components/json-tree.js.map +1 -1
- package/dist/components/select.d.ts +6 -0
- package/dist/components/select.d.ts.map +1 -1
- package/dist/components/select.js +3 -2
- package/dist/components/select.js.map +1 -1
- package/dist/components/served-media.d.ts +50 -4
- package/dist/components/served-media.d.ts.map +1 -1
- package/dist/components/served-media.js +69 -19
- package/dist/components/served-media.js.map +1 -1
- package/dist/env-markers.d.ts +25 -0
- package/dist/env-markers.d.ts.map +1 -0
- package/dist/env-markers.js +63 -0
- package/dist/env-markers.js.map +1 -0
- package/dist/hooks/interactions-stream.d.ts.map +1 -1
- package/dist/hooks/interactions-stream.js +9 -7
- package/dist/hooks/interactions-stream.js.map +1 -1
- package/dist/hooks/useApi.d.ts +2264 -912
- package/dist/hooks/useApi.d.ts.map +1 -1
- package/dist/hooks/useCapabilities.d.ts +5 -9
- package/dist/hooks/useCapabilities.d.ts.map +1 -1
- package/dist/hooks/useCapabilities.js +8 -33
- package/dist/hooks/useCapabilities.js.map +1 -1
- package/dist/host.d.ts +2 -0
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +8 -0
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/plugin/coverage.d.ts +14 -0
- package/dist/plugin/coverage.d.ts.map +1 -0
- package/dist/plugin/coverage.js +14 -0
- package/dist/plugin/coverage.js.map +1 -0
- package/dist/plugin/registry.d.ts +11 -1
- package/dist/plugin/registry.d.ts.map +1 -1
- package/dist/plugin/registry.js +33 -4
- package/dist/plugin/registry.js.map +1 -1
- package/dist/plugin/types.d.ts +43 -1
- package/dist/plugin/types.d.ts.map +1 -1
- package/dist/schema-form/SchemaForm.d.ts +11 -1
- package/dist/schema-form/SchemaForm.d.ts.map +1 -1
- package/dist/schema-form/SchemaForm.js +2 -2
- package/dist/schema-form/SchemaForm.js.map +1 -1
- package/dist/schema-form/classify.d.ts.map +1 -1
- package/dist/schema-form/classify.js +54 -2
- package/dist/schema-form/classify.js.map +1 -1
- package/dist/schema-form/date-range-field.d.ts +44 -0
- package/dist/schema-form/date-range-field.d.ts.map +1 -0
- package/dist/schema-form/date-range-field.js +72 -0
- package/dist/schema-form/date-range-field.js.map +1 -0
- package/dist/schema-form/enum-field.d.ts +3 -1
- package/dist/schema-form/enum-field.d.ts.map +1 -1
- package/dist/schema-form/enum-field.js +8 -2
- package/dist/schema-form/enum-field.js.map +1 -1
- package/dist/schema-form/field-model.d.ts +20 -1
- package/dist/schema-form/field-model.d.ts.map +1 -1
- package/dist/schema-form/field-node.d.ts +10 -0
- package/dist/schema-form/field-node.d.ts.map +1 -1
- package/dist/schema-form/field-node.js +6 -3
- package/dist/schema-form/field-node.js.map +1 -1
- package/dist/schema-form/index.d.ts +3 -1
- package/dist/schema-form/index.d.ts.map +1 -1
- package/dist/schema-form/index.js +1 -0
- package/dist/schema-form/index.js.map +1 -1
- package/dist/schema-form/multiselect-field.d.ts.map +1 -1
- package/dist/schema-form/multiselect-field.js +11 -0
- package/dist/schema-form/multiselect-field.js.map +1 -1
- package/dist/schema-form/object-fields.d.ts +9 -1
- package/dist/schema-form/object-fields.d.ts.map +1 -1
- package/dist/schema-form/object-fields.js +16 -3
- package/dist/schema-form/object-fields.js.map +1 -1
- package/dist/schema-form/string-field.d.ts +6 -1
- package/dist/schema-form/string-field.d.ts.map +1 -1
- package/dist/schema-form/string-field.js +42 -8
- package/dist/schema-form/string-field.js.map +1 -1
- package/dist/schema-form/types.d.ts +36 -0
- package/dist/schema-form/types.d.ts.map +1 -1
- package/dist/state-binding/UpdateRow.d.ts +2 -2
- package/dist/state-binding/UpdateRow.js +1 -1
- package/dist/state-binding/UpdateRow.js.map +1 -1
- package/dist/state-binding/adapter.d.ts +12 -8
- package/dist/state-binding/adapter.d.ts.map +1 -1
- package/dist/state-binding/adapter.js +30 -13
- package/dist/state-binding/adapter.js.map +1 -1
- package/package.json +5 -4
- package/src/components/chip-group.tsx +157 -0
- package/src/components/components.css +16 -3
- package/src/components/execution-key-picker.tsx +1 -1
- package/src/components/extension-grouping.ts +22 -22
- package/src/components/extension-picker.tsx +13 -14
- package/src/components/fleet-report.tsx +66 -7
- package/src/components/json-tree-model.ts +36 -8
- package/src/components/json-tree.tsx +24 -4
- package/src/components/select.tsx +11 -1
- package/src/components/served-media.tsx +150 -16
- package/src/env-markers.ts +70 -0
- package/src/hooks/interactions-stream.ts +9 -7
- package/src/hooks/useCapabilities.ts +8 -31
- package/src/host.ts +2 -0
- package/src/index.ts +22 -2
- package/src/plugin/coverage.ts +23 -0
- package/src/plugin/registry.ts +58 -3
- package/src/plugin/types.ts +45 -1
- package/src/schema-form/SchemaForm.tsx +10 -0
- package/src/schema-form/classify.ts +59 -3
- package/src/schema-form/date-range-field.tsx +167 -0
- package/src/schema-form/enum-field.tsx +20 -0
- package/src/schema-form/field-model.ts +24 -2
- package/src/schema-form/field-node.tsx +19 -0
- package/src/schema-form/index.ts +10 -1
- package/src/schema-form/multiselect-field.tsx +21 -0
- package/src/schema-form/object-fields.tsx +36 -2
- package/src/schema-form/string-field.tsx +72 -1
- package/src/schema-form/types.ts +38 -0
- package/src/state-binding/README.md +10 -7
- package/src/state-binding/UpdateRow.tsx +3 -3
- package/src/state-binding/adapter.ts +31 -16
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The bounded-traversal model behind `JsonTree`: container inspection, the paged
|
|
3
3
|
* child slice, path identity, and the open-state computation for the depth
|
|
4
|
-
* default, expand-all and collapse-all baselines
|
|
4
|
+
* default, expand-all and collapse-all baselines and the caller's named paths.
|
|
5
|
+
* Pure — no React, no DOM.
|
|
5
6
|
*/
|
|
6
7
|
import { isRecord } from '../guards';
|
|
7
8
|
/** The deepest level opened by the guarded default; deeper nodes start collapsed. */
|
|
@@ -63,6 +64,23 @@ export function childPath(parent, name) {
|
|
|
63
64
|
const segment = encodeURIComponent(name);
|
|
64
65
|
return parent === '' ? segment : `${parent}/${segment}`;
|
|
65
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* The identities of every container a caller's named paths open: each path is a
|
|
69
|
+
* list of keys from the root (an array item named by its index), and the root,
|
|
70
|
+
* every container along the path and the one it ends at are all opened.
|
|
71
|
+
*/
|
|
72
|
+
export function namedOpenPaths(paths) {
|
|
73
|
+
const open = new Set();
|
|
74
|
+
for (const segments of paths) {
|
|
75
|
+
let path = '';
|
|
76
|
+
open.add(path);
|
|
77
|
+
for (const segment of segments) {
|
|
78
|
+
path = childPath(path, segment);
|
|
79
|
+
open.add(path);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return open;
|
|
83
|
+
}
|
|
66
84
|
export function initialBaseline(defaultExpanded) {
|
|
67
85
|
if (defaultExpanded === true)
|
|
68
86
|
return 'expanded';
|
|
@@ -107,19 +125,25 @@ export function expandedOpenPaths(root) {
|
|
|
107
125
|
return open;
|
|
108
126
|
}
|
|
109
127
|
/**
|
|
110
|
-
* Whether a node is open, from the baseline
|
|
111
|
-
* explicit toggle always wins;
|
|
112
|
-
*
|
|
113
|
-
*
|
|
128
|
+
* Whether a node is open, from the baseline, the caller's named paths and the
|
|
129
|
+
* reader's own toggles. An explicit toggle always wins; the reader's collapse-all
|
|
130
|
+
* opens nothing; otherwise a node on a named path is open beside any baseline a call
|
|
131
|
+
* site selects, and the baseline decides the rest — a collapsed baseline opens
|
|
132
|
+
* nothing else, the depth-guarded default opens through {@link AUTO_EXPAND_DEPTH},
|
|
133
|
+
* expand-all opens the budgeted breadth-first set.
|
|
114
134
|
*/
|
|
115
|
-
export function computeOpen(baseline, overrides, expandedPaths, path, depth) {
|
|
135
|
+
export function computeOpen(baseline, overrides, expandedPaths, namedPaths, path, depth) {
|
|
116
136
|
const override = overrides.get(path);
|
|
117
137
|
if (override !== undefined)
|
|
118
138
|
return override;
|
|
119
|
-
if (baseline === '
|
|
120
|
-
return
|
|
139
|
+
if (baseline === 'closed')
|
|
140
|
+
return false;
|
|
141
|
+
if (namedPaths.has(path))
|
|
142
|
+
return true;
|
|
121
143
|
if (baseline === 'collapsed')
|
|
122
144
|
return false;
|
|
145
|
+
if (baseline === 'expanded')
|
|
146
|
+
return expandedPaths?.has(path) ?? false;
|
|
123
147
|
return depth <= AUTO_EXPAND_DEPTH;
|
|
124
148
|
}
|
|
125
149
|
//# sourceMappingURL=json-tree-model.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-tree-model.js","sourceRoot":"","sources":["../../src/components/json-tree-model.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"json-tree-model.js","sourceRoot":"","sources":["../../src/components/json-tree-model.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* renders no children. `defaultExpanded` overrides per call site.
|
|
13
13
|
* - Expand-all opens breadth-first only until {@link AUTO_EXPAND_NODE_BUDGET} nodes
|
|
14
14
|
* render and never past {@link MAX_AUTO_DEPTH}; a node beyond either opens on click.
|
|
15
|
+
* - `openPaths` opens exactly the containers along the paths a caller names, at any
|
|
16
|
+
* depth, beside the baseline; collapse-all and the reader's toggles close them.
|
|
15
17
|
* - A container over {@link PAGE_SIZE} children renders one page plus a "show more".
|
|
16
18
|
* - Copy-whole and copy-node write JSON to the clipboard; a refused write shows an alert.
|
|
17
19
|
*
|
|
@@ -27,8 +29,15 @@ export interface JsonTreeProps {
|
|
|
27
29
|
* opens through {@link AUTO_EXPAND_DEPTH} and collapses everything below.
|
|
28
30
|
*/
|
|
29
31
|
readonly defaultExpanded?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Paths whose containers open beside the baseline, at any depth: each path lists
|
|
34
|
+
* the keys from the root (an array item by its index, as a string), and the root,
|
|
35
|
+
* every container along it and the one it ends at open. The reader's own toggle
|
|
36
|
+
* still wins, and collapse-all closes them like every other node.
|
|
37
|
+
*/
|
|
38
|
+
readonly openPaths?: readonly (readonly string[])[];
|
|
30
39
|
/** The region's accessible name, applied only while the pane actually scrolls. */
|
|
31
40
|
readonly label?: string;
|
|
32
41
|
}
|
|
33
|
-
export declare function JsonTree({ data, defaultExpanded, label }: JsonTreeProps): ReactElement;
|
|
42
|
+
export declare function JsonTree({ data, defaultExpanded, openPaths, label, }: JsonTreeProps): ReactElement;
|
|
34
43
|
//# sourceMappingURL=json-tree.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-tree.d.ts","sourceRoot":"","sources":["../../src/components/json-tree.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"json-tree.d.ts","sourceRoot":"","sources":["../../src/components/json-tree.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAmB1C,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;IACpD,kFAAkF;IAClF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAQD,wBAAgB,QAAQ,CAAC,EACvB,IAAI,EACJ,eAAe,EACf,SAAoB,EACpB,KAAK,GACN,EAAE,aAAa,GAAG,YAAY,CA6F9B"}
|
|
@@ -3,13 +3,15 @@ import { useCallback, useMemo, useState } from 'react';
|
|
|
3
3
|
import { useClipboardCopy } from '../hooks/useClipboardCopy';
|
|
4
4
|
import { XCircleIcon } from './icons';
|
|
5
5
|
import { JsonTreeContext } from './json-tree-context';
|
|
6
|
-
import { computeOpen, expandedOpenPaths, initialBaseline, isContainer, PAGE_SIZE, } from './json-tree-model';
|
|
6
|
+
import { computeOpen, expandedOpenPaths, initialBaseline, isContainer, namedOpenPaths, PAGE_SIZE, } from './json-tree-model';
|
|
7
7
|
import { COPY_LABEL, CopyButton, JsonNode } from './json-tree-nodes';
|
|
8
8
|
import { copyFailed, NO_CLIPBOARD, serializeForCopy } from './json-value-format';
|
|
9
9
|
import { useOverflowRegion } from './scroll-region';
|
|
10
10
|
/** The region's name when the caller supplies none. */
|
|
11
11
|
const DEFAULT_LABEL = 'JSON';
|
|
12
|
-
|
|
12
|
+
/** The paths a tree without `openPaths` opens beside its baseline. */
|
|
13
|
+
const NO_PATHS = [];
|
|
14
|
+
export function JsonTree({ data, defaultExpanded, openPaths = NO_PATHS, label, }) {
|
|
13
15
|
const [baseline, setBaseline] = useState(() => initialBaseline(defaultExpanded));
|
|
14
16
|
const [overrides, setOverrides] = useState(() => new Map());
|
|
15
17
|
const { error: copyError, announcement, copy: copyText, } = useClipboardCopy({
|
|
@@ -23,7 +25,8 @@ export function JsonTree({ data, defaultExpanded, label }) {
|
|
|
23
25
|
// while that baseline is active; deriving it lazily keeps a collapsed or
|
|
24
26
|
// depth-default tree from sweeping the payload at all.
|
|
25
27
|
const expandedPaths = useMemo(() => (baseline === 'expanded' ? expandedOpenPaths(data) : null), [baseline, data]);
|
|
26
|
-
const
|
|
28
|
+
const namedPaths = useMemo(() => namedOpenPaths(openPaths), [openPaths]);
|
|
29
|
+
const isOpen = useCallback((path, depth) => computeOpen(baseline, overrides, expandedPaths, namedPaths, path, depth), [baseline, overrides, expandedPaths, namedPaths]);
|
|
27
30
|
const setOpen = useCallback((path, open) => {
|
|
28
31
|
setOverrides((previous) => {
|
|
29
32
|
const next = new Map(previous);
|
|
@@ -40,7 +43,7 @@ export function JsonTree({ data, defaultExpanded, label }) {
|
|
|
40
43
|
setOverrides(new Map());
|
|
41
44
|
};
|
|
42
45
|
const collapseAll = () => {
|
|
43
|
-
setBaseline('
|
|
46
|
+
setBaseline('closed');
|
|
44
47
|
setOverrides(new Map());
|
|
45
48
|
};
|
|
46
49
|
const showToolbar = isContainer(data);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-tree.js","sourceRoot":"","sources":["../../src/components/json-tree.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"json-tree.js","sourceRoot":"","sources":["../../src/components/json-tree.tsx"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,12 @@ interface SelectSharedProps {
|
|
|
23
23
|
* the visible text out of the accessible name (WCAG 2.5.3).
|
|
24
24
|
*/
|
|
25
25
|
readonly 'aria-label'?: string;
|
|
26
|
+
/**
|
|
27
|
+
* IDREFs of elements that describe the trigger. An enclosing `Field` owns entries
|
|
28
|
+
* in the same list (its description and error), so the two are concatenated,
|
|
29
|
+
* caller's first, exactly as the text inputs do.
|
|
30
|
+
*/
|
|
31
|
+
readonly 'aria-describedby'?: string;
|
|
26
32
|
}
|
|
27
33
|
/** The FLAT listbox: one ungrouped run of options. */
|
|
28
34
|
export interface SelectProps extends SelectSharedProps {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/components/select.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,iEAAiE;AACjE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;CAC3C;AAED,wDAAwD;AACxD,UAAU,iBAAiB;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"select.d.ts","sourceRoot":"","sources":["../../src/components/select.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,iEAAiE;AACjE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;CAC3C;AAED,wDAAwD;AACxD,UAAU,iBAAiB;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,sDAAsD;AACtD,MAAM,WAAW,WAAY,SAAQ,iBAAiB;IACpD,QAAQ,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;CAC3C;AAED,6EAA6E;AAC7E,MAAM,WAAW,iBAAkB,SAAQ,iBAAiB;IAC1D,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;CACzC;AAED;;;;;;;;;;GAUG;AACH,KAAK,UAAU,GACX,CAAC,WAAW,GAAG;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC,GAC/C,CAAC,iBAAiB,GAAG;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAiB3D,wBAAgB,MAAM,CAAC,KAAK,EAAE,UAAU,+BAkEvC"}
|
|
@@ -18,15 +18,16 @@ function OptionItem({ option }) {
|
|
|
18
18
|
return (_jsxs(RadixSelect.Item, { className: "tai-select-item", value: option.value, disabled: option.disabled, children: [_jsx(RadixSelect.ItemText, { children: option.label }), _jsx(RadixSelect.ItemIndicator, { className: "tai-select-item-indicator", children: _jsx(CheckIcon, {}) })] }));
|
|
19
19
|
}
|
|
20
20
|
export function Select(props) {
|
|
21
|
-
const { value, defaultValue, onValueChange, placeholder = 'Select…', disabled, name, 'aria-label': ariaLabel, } = props;
|
|
21
|
+
const { value, defaultValue, onValueChange, placeholder = 'Select…', disabled, name, 'aria-label': ariaLabel, 'aria-describedby': ownDescribedBy, } = props;
|
|
22
22
|
const field = useFieldControl();
|
|
23
|
+
const describedBy = [ownDescribedBy, field['aria-describedby']].filter((id) => id !== undefined);
|
|
23
24
|
// Untyped JavaScript is the only caller that reaches this. Rendering one list
|
|
24
25
|
// and dropping the other would lose every entry in the discarded one silently,
|
|
25
26
|
// and an empty listbox reads as "there is nothing to choose".
|
|
26
27
|
if ((props.options === undefined) === (props.groups === undefined)) {
|
|
27
28
|
throw new Error('Select renders exactly one of `options` or `groups`; it was given both, or neither.');
|
|
28
29
|
}
|
|
29
|
-
return (_jsxs(RadixSelect.Root, { value: value, defaultValue: defaultValue, onValueChange: onValueChange, disabled: disabled, name: name, children: [_jsxs(RadixSelect.Trigger, { id: field.id, className: SELECT_TRIGGER_CLASS, "aria-label": field.id === undefined ? ariaLabel : undefined, "aria-describedby":
|
|
30
|
+
return (_jsxs(RadixSelect.Root, { value: value, defaultValue: defaultValue, onValueChange: onValueChange, disabled: disabled, name: name, children: [_jsxs(RadixSelect.Trigger, { id: field.id, className: SELECT_TRIGGER_CLASS, "aria-label": field.id === undefined ? ariaLabel : undefined, "aria-describedby": describedBy.length > 0 ? describedBy.join(' ') : undefined, "aria-invalid": field['aria-invalid'], children: [_jsx(RadixSelect.Value, { placeholder: placeholder }), _jsx(RadixSelect.Icon, { "aria-hidden": "true", children: _jsx(ChevronDownIcon, {}) })] }), _jsx(RadixSelect.Portal, { children: _jsx(RadixSelect.Content, { className: "tai-select-content", position: "popper", sideOffset: 4, children: _jsx(RadixSelect.Viewport, { className: "tai-select-viewport", children: props.groups !== undefined
|
|
30
31
|
? props.groups.map((group) => (_jsxs(RadixSelect.Group, { children: [_jsx(RadixSelect.Label, { className: "tai-select-group-label tai-label", children: group.label }), group.options.map((option) => (_jsx(OptionItem, { option: option }, option.value)))] }, group.label)))
|
|
31
32
|
: props.options.map((option) => _jsx(OptionItem, { option: option }, option.value)) }) }) })] }));
|
|
32
33
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"select.js","sourceRoot":"","sources":["../../src/components/select.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,WAAW,MAAM,wBAAwB,CAAC;AAEtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"select.js","sourceRoot":"","sources":["../../src/components/select.tsx"],"names":[],"mappings":"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"}
|
|
@@ -12,15 +12,26 @@
|
|
|
12
12
|
* url is a loud blocked notice, its text React-escaped, never a live attribute.
|
|
13
13
|
* There is no `dangerouslySetInnerHTML` here.
|
|
14
14
|
*/
|
|
15
|
+
import type { ServedConstants } from '@tai42/api-client';
|
|
15
16
|
import type { ReactNode } from 'react';
|
|
17
|
+
/**
|
|
18
|
+
* The served-media grammar the contract publishes, read from the injected client
|
|
19
|
+
* (`useApi().constants`): the served route prefix and the stored-media id pattern. Media is
|
|
20
|
+
* stored by reference and served from the API origin at `MEDIA_ROUTE_PREFIX + <id>`; a record
|
|
21
|
+
* carries it as a RELATIVE url of exactly that shape, resolved to the API base at render time
|
|
22
|
+
* ({@link resolveMediaSrc}), not assumed same-origin as the SPA page.
|
|
23
|
+
*/
|
|
24
|
+
export type ServedMediaGrammar = {
|
|
25
|
+
readonly [K in keyof Pick<ServedConstants, 'MEDIA_ROUTE_PREFIX' | 'MEDIA_ID_PATTERN'>]: string;
|
|
26
|
+
};
|
|
16
27
|
/**
|
|
17
28
|
* Whether `url` is the platform's own served-media reference: a relative
|
|
18
|
-
* `MEDIA_ROUTE_PREFIX + <id>`
|
|
29
|
+
* `MEDIA_ROUTE_PREFIX + <id>` whose id matches `MEDIA_ID_PATTERN` whole. A served
|
|
19
30
|
* reference is same-origin to the API base ({@link resolveMediaSrc}) and leaks no page
|
|
20
31
|
* context on load, so a sink with no per-element referrer control (`<audio>`/`<video>`,
|
|
21
32
|
* where `referrerPolicy` is not a valid attribute) admits ONLY this form.
|
|
22
33
|
*/
|
|
23
|
-
export declare function isServedMediaUrl(url: string): boolean;
|
|
34
|
+
export declare function isServedMediaUrl(url: string, grammar: ServedMediaGrammar): boolean;
|
|
24
35
|
/**
|
|
25
36
|
* The media src gate: a media element renders ONLY for an `https:` URL, the
|
|
26
37
|
* platform's own served-media reference (a relative `MEDIA_ROUTE_PREFIX + <id>`),
|
|
@@ -36,7 +47,7 @@ export declare function isServedMediaUrl(url: string): boolean;
|
|
|
36
47
|
* item. The gate keys on the reference form; {@link resolveMediaSrc} joins an
|
|
37
48
|
* admitted reference to the API base for the actual load.
|
|
38
49
|
*/
|
|
39
|
-
export declare function isRenderableMediaSrc(url: string, allowDataImage?: boolean): boolean;
|
|
50
|
+
export declare function isRenderableMediaSrc(url: string, grammar: ServedMediaGrammar, allowDataImage?: boolean): boolean;
|
|
40
51
|
/**
|
|
41
52
|
* The URL an admitted media element actually loads. An `https:` url is already
|
|
42
53
|
* absolute and is returned unchanged. A served-media reference is RELATIVE and is
|
|
@@ -46,7 +57,7 @@ export declare function isRenderableMediaSrc(url: string, allowDataImage?: boole
|
|
|
46
57
|
* relative, which is correct; a configured base's trailing slash is stripped so the
|
|
47
58
|
* join never double-slashes.
|
|
48
59
|
*/
|
|
49
|
-
export declare function resolveMediaSrc(url: string, baseUrl: string): string;
|
|
60
|
+
export declare function resolveMediaSrc(url: string, baseUrl: string, grammar: ServedMediaGrammar): string;
|
|
50
61
|
export interface MediaImageProps {
|
|
51
62
|
readonly url: string;
|
|
52
63
|
readonly caption?: string;
|
|
@@ -76,4 +87,39 @@ export interface MediaImageProps {
|
|
|
76
87
|
* blocked/failed states in its own presentation.
|
|
77
88
|
*/
|
|
78
89
|
export declare function MediaImage({ url, caption, altFallback, renderNotice, allowDataImage, }: MediaImageProps): ReactNode;
|
|
90
|
+
export interface MediaDocumentProps {
|
|
91
|
+
readonly url: string;
|
|
92
|
+
/** The download label: the item's filename or caption, or the caller's default. */
|
|
93
|
+
readonly label: string;
|
|
94
|
+
/** Render the notice shown when the url fails the src gate. */
|
|
95
|
+
readonly renderNotice: (url: string) => ReactNode;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* One document: a labelled download affordance. The served route sends
|
|
99
|
+
* `Content-Disposition: attachment` for documents, so the anchor `download`-hints and
|
|
100
|
+
* opens in a new tab; `rel="noreferrer"` stops the Studio URL leaking to the media
|
|
101
|
+
* host. A url that fails {@link isRenderableMediaSrc} is the caller's notice, never a
|
|
102
|
+
* live href.
|
|
103
|
+
*/
|
|
104
|
+
export declare function MediaDocument({ url, label, renderNotice }: MediaDocumentProps): ReactNode;
|
|
105
|
+
export interface MediaPlayerProps {
|
|
106
|
+
readonly kind: 'audio' | 'video';
|
|
107
|
+
readonly url: string;
|
|
108
|
+
/** The player's accessible label. */
|
|
109
|
+
readonly label: string;
|
|
110
|
+
/**
|
|
111
|
+
* Render the notice shown when no player is displayed: `state` is `'blocked'` when
|
|
112
|
+
* the url is not a served reference and `'failed'` when it fails to LOAD.
|
|
113
|
+
*/
|
|
114
|
+
readonly renderNotice: (state: 'blocked' | 'failed', url: string) => ReactNode;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* A native `<audio>`/`<video controls>` player. `referrerPolicy` is not a valid
|
|
118
|
+
* attribute on `<audio>`/`<video>`, so a remote load has no per-element referrer
|
|
119
|
+
* control; the player therefore admits ONLY the platform's own served-media reference
|
|
120
|
+
* ({@link isServedMediaUrl}: same-origin, leaking no page URL to a media host), and any
|
|
121
|
+
* other url, a bare `https:` one included, is the caller's notice. The load-failure
|
|
122
|
+
* flag keys on the url that failed so a new url at the same position gets a fresh load.
|
|
123
|
+
*/
|
|
124
|
+
export declare function MediaPlayer({ kind, url, label, renderNotice }: MediaPlayerProps): ReactNode;
|
|
79
125
|
//# sourceMappingURL=served-media.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"served-media.d.ts","sourceRoot":"","sources":["../../src/components/served-media.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"served-media.d.ts","sourceRoot":"","sources":["../../src/components/served-media.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAC;AAOtD;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,CAAC,IAAI,MAAM,IAAI,CAAC,eAAe,EAAE,oBAAoB,GAAG,kBAAkB,CAAC,GAAG,MAAM;CAC/F,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAMlF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,kBAAkB,EAC3B,cAAc,UAAQ,GACrB,OAAO,CAOT;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAEjG;AAuCD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;OAKG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,EAAE,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC;IAChF;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,EACzB,GAAG,EACH,OAAO,EACP,WAA8B,EAC9B,YAAY,EACZ,cAAsB,GACvB,EAAE,eAAe,GAAG,SAAS,CAyC7B;AAcD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,+DAA+D;IAC/D,QAAQ,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC;CACnD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,kBAAkB,GAAG,SAAS,CAkBzF;AAiBD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,YAAY,EAAE,CAAC,KAAK,EAAE,SAAS,GAAG,QAAQ,EAAE,GAAG,EAAE,MAAM,KAAK,SAAS,CAAC;CAChF;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,gBAAgB,GAAG,SAAS,CAoC3F"}
|
|
@@ -3,24 +3,17 @@ import { useState } from 'react';
|
|
|
3
3
|
import { useApi } from '../hooks/useApi';
|
|
4
4
|
import { Badge } from './badge';
|
|
5
5
|
import { isSafeHttpUrl } from './primitives';
|
|
6
|
-
/**
|
|
7
|
-
* The served-media route: media is stored by reference and served from the API
|
|
8
|
-
* origin at `MEDIA_ROUTE_PREFIX + <id>`, where the id is 43 urlsafe-base64 chars
|
|
9
|
-
* (32 random bytes). A record carries it as a RELATIVE url of exactly that shape —
|
|
10
|
-
* the platform's own media reference, resolved to the API base at render time
|
|
11
|
-
* ({@link resolveMediaSrc}), not assumed same-origin as the SPA page.
|
|
12
|
-
*/
|
|
13
|
-
const MEDIA_ROUTE_PREFIX = '/api/interactions/media/';
|
|
14
|
-
const MEDIA_ID = /^[A-Za-z0-9_-]{43}$/;
|
|
15
6
|
/**
|
|
16
7
|
* Whether `url` is the platform's own served-media reference: a relative
|
|
17
|
-
* `MEDIA_ROUTE_PREFIX + <id>`
|
|
8
|
+
* `MEDIA_ROUTE_PREFIX + <id>` whose id matches `MEDIA_ID_PATTERN` whole. A served
|
|
18
9
|
* reference is same-origin to the API base ({@link resolveMediaSrc}) and leaks no page
|
|
19
10
|
* context on load, so a sink with no per-element referrer control (`<audio>`/`<video>`,
|
|
20
11
|
* where `referrerPolicy` is not a valid attribute) admits ONLY this form.
|
|
21
12
|
*/
|
|
22
|
-
export function isServedMediaUrl(url) {
|
|
23
|
-
|
|
13
|
+
export function isServedMediaUrl(url, grammar) {
|
|
14
|
+
const prefix = grammar.MEDIA_ROUTE_PREFIX;
|
|
15
|
+
return (url.startsWith(prefix) &&
|
|
16
|
+
new RegExp(`^${grammar.MEDIA_ID_PATTERN}$`).test(url.slice(prefix.length)));
|
|
24
17
|
}
|
|
25
18
|
/**
|
|
26
19
|
* The media src gate: a media element renders ONLY for an `https:` URL, the
|
|
@@ -37,9 +30,11 @@ export function isServedMediaUrl(url) {
|
|
|
37
30
|
* item. The gate keys on the reference form; {@link resolveMediaSrc} joins an
|
|
38
31
|
* admitted reference to the API base for the actual load.
|
|
39
32
|
*/
|
|
40
|
-
export function isRenderableMediaSrc(url, allowDataImage = false) {
|
|
33
|
+
export function isRenderableMediaSrc(url, grammar, allowDataImage = false) {
|
|
41
34
|
const isHttpsUrl = isSafeHttpUrl(url) && new URL(url).protocol === 'https:';
|
|
42
|
-
return isHttpsUrl ||
|
|
35
|
+
return (isHttpsUrl ||
|
|
36
|
+
(allowDataImage && url.startsWith('data:image/')) ||
|
|
37
|
+
isServedMediaUrl(url, grammar));
|
|
43
38
|
}
|
|
44
39
|
/**
|
|
45
40
|
* The URL an admitted media element actually loads. An `https:` url is already
|
|
@@ -50,8 +45,8 @@ export function isRenderableMediaSrc(url, allowDataImage = false) {
|
|
|
50
45
|
* relative, which is correct; a configured base's trailing slash is stripped so the
|
|
51
46
|
* join never double-slashes.
|
|
52
47
|
*/
|
|
53
|
-
export function resolveMediaSrc(url, baseUrl) {
|
|
54
|
-
return isServedMediaUrl(url) ? `${baseUrl.replace(/\/+$/, '')}${url}` : url;
|
|
48
|
+
export function resolveMediaSrc(url, baseUrl, grammar) {
|
|
49
|
+
return isServedMediaUrl(url, grammar) ? `${baseUrl.replace(/\/+$/, '')}${url}` : url;
|
|
55
50
|
}
|
|
56
51
|
// -- styles ------------------------------------------------------------------
|
|
57
52
|
const itemStyle = {
|
|
@@ -96,8 +91,8 @@ export function MediaImage({ url, caption, altFallback = 'Attached image', rende
|
|
|
96
91
|
// url rendered at this same position gets a fresh load attempt instead of inheriting
|
|
97
92
|
// a stale failure notice.
|
|
98
93
|
const [failedUrl, setFailedUrl] = useState(null);
|
|
99
|
-
const { baseUrl } = useApi();
|
|
100
|
-
if (!isRenderableMediaSrc(url, allowDataImage)) {
|
|
94
|
+
const { baseUrl, constants } = useApi();
|
|
95
|
+
if (!isRenderableMediaSrc(url, constants, allowDataImage)) {
|
|
101
96
|
if (renderNotice !== undefined)
|
|
102
97
|
return renderNotice('blocked', url);
|
|
103
98
|
return (_jsxs("div", { role: "alert", "data-testid": "media-item-blocked", style: blockedStyle, children: [_jsx(Badge, { variant: "danger", children: "Blocked image" }), _jsx("span", { style: blockedUrlStyle, children: url })] }));
|
|
@@ -107,8 +102,63 @@ export function MediaImage({ url, caption, altFallback = 'Attached image', rende
|
|
|
107
102
|
return renderNotice('failed', url);
|
|
108
103
|
return (_jsxs("div", { role: "alert", "data-testid": "media-image-error", style: blockedStyle, children: [_jsx(Badge, { variant: "danger", children: "Image failed to load" }), _jsx("span", { style: blockedUrlStyle, children: url })] }));
|
|
109
104
|
}
|
|
110
|
-
return (_jsxs("div", { style: itemStyle, children: [_jsx("img", { src: resolveMediaSrc(url, baseUrl), alt: caption ?? altFallback, referrerPolicy: "no-referrer", style: imageStyle, onError: () => {
|
|
105
|
+
return (_jsxs("div", { style: itemStyle, children: [_jsx("img", { src: resolveMediaSrc(url, baseUrl, constants), alt: caption ?? altFallback, referrerPolicy: "no-referrer", style: imageStyle, onError: () => {
|
|
111
106
|
setFailedUrl(url);
|
|
112
107
|
} }), caption !== undefined ? _jsx("span", { style: captionStyle, children: caption }) : null] }));
|
|
113
108
|
}
|
|
109
|
+
// -- document item -----------------------------------------------------------
|
|
110
|
+
const chipStyle = {
|
|
111
|
+
display: 'flex',
|
|
112
|
+
alignItems: 'center',
|
|
113
|
+
gap: 'var(--tai-space-2)',
|
|
114
|
+
};
|
|
115
|
+
const labelStyle = {
|
|
116
|
+
wordBreak: 'break-all',
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* One document: a labelled download affordance. The served route sends
|
|
120
|
+
* `Content-Disposition: attachment` for documents, so the anchor `download`-hints and
|
|
121
|
+
* opens in a new tab; `rel="noreferrer"` stops the Studio URL leaking to the media
|
|
122
|
+
* host. A url that fails {@link isRenderableMediaSrc} is the caller's notice, never a
|
|
123
|
+
* live href.
|
|
124
|
+
*/
|
|
125
|
+
export function MediaDocument({ url, label, renderNotice }) {
|
|
126
|
+
const { baseUrl, constants } = useApi();
|
|
127
|
+
if (!isRenderableMediaSrc(url, constants))
|
|
128
|
+
return renderNotice(url);
|
|
129
|
+
return (_jsxs("div", { style: chipStyle, "data-testid": "media-document", children: [_jsx(Badge, { variant: "neutral", children: "Document" }), _jsx("a", { href: resolveMediaSrc(url, baseUrl, constants), target: "_blank", rel: "noreferrer", download: true, title: label, style: labelStyle, children: label })] }));
|
|
130
|
+
}
|
|
131
|
+
// -- audio / video item ------------------------------------------------------
|
|
132
|
+
const audioStyle = {
|
|
133
|
+
width: '100%',
|
|
134
|
+
};
|
|
135
|
+
const videoStyle = {
|
|
136
|
+
maxWidth: '100%',
|
|
137
|
+
maxHeight: '320px',
|
|
138
|
+
width: 'auto',
|
|
139
|
+
height: 'auto',
|
|
140
|
+
borderRadius: 'var(--tai-radius-md)',
|
|
141
|
+
border: '1px solid var(--tai-color-border)',
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* A native `<audio>`/`<video controls>` player. `referrerPolicy` is not a valid
|
|
145
|
+
* attribute on `<audio>`/`<video>`, so a remote load has no per-element referrer
|
|
146
|
+
* control; the player therefore admits ONLY the platform's own served-media reference
|
|
147
|
+
* ({@link isServedMediaUrl}: same-origin, leaking no page URL to a media host), and any
|
|
148
|
+
* other url, a bare `https:` one included, is the caller's notice. The load-failure
|
|
149
|
+
* flag keys on the url that failed so a new url at the same position gets a fresh load.
|
|
150
|
+
*/
|
|
151
|
+
export function MediaPlayer({ kind, url, label, renderNotice }) {
|
|
152
|
+
const [failedUrl, setFailedUrl] = useState(null);
|
|
153
|
+
const { baseUrl, constants } = useApi();
|
|
154
|
+
if (!isServedMediaUrl(url, constants))
|
|
155
|
+
return renderNotice('blocked', url);
|
|
156
|
+
if (failedUrl === url)
|
|
157
|
+
return renderNotice('failed', url);
|
|
158
|
+
const src = resolveMediaSrc(url, baseUrl, constants);
|
|
159
|
+
const onError = () => {
|
|
160
|
+
setFailedUrl(url);
|
|
161
|
+
};
|
|
162
|
+
return (_jsx("div", { style: itemStyle, children: kind === 'audio' ? (_jsx("audio", { controls: true, src: src, "aria-label": label, onError: onError, style: audioStyle, "data-testid": "media-audio" })) : (_jsx("video", { controls: true, src: src, "aria-label": label, onError: onError, style: videoStyle, "data-testid": "media-video" })) }));
|
|
163
|
+
}
|
|
114
164
|
//# sourceMappingURL=served-media.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"served-media.js","sourceRoot":"","sources":["../../src/components/served-media.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"served-media.js","sourceRoot":"","sources":["../../src/components/served-media.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `!ENV ${VAR[:default]}` manifest marker grammar the platform reads: a string leaf
|
|
3
|
+
* that begins with the `!ENV ` prefix (the tag and exactly one space) references an
|
|
4
|
+
* environment variable by name, never its value. A variable name carries no `{`, `}` or
|
|
5
|
+
* `:`; an optional `:default` follows it.
|
|
6
|
+
*/
|
|
7
|
+
/** The prefix that makes a string leaf an `!ENV` marker. */
|
|
8
|
+
export declare const ENV_MARKER_PREFIX = "!ENV ";
|
|
9
|
+
/** The reference of a single-reference marker leaf. */
|
|
10
|
+
export interface EnvRef {
|
|
11
|
+
readonly key: string;
|
|
12
|
+
/** The `:default` text (without the leading colon); absent for a bare reference. */
|
|
13
|
+
readonly default?: string;
|
|
14
|
+
}
|
|
15
|
+
/** The reference of `value` when it is exactly `!ENV ${KEY}` or `!ENV ${KEY:default}`, else `null`. */
|
|
16
|
+
export declare function parseEnvMarker(value: unknown): EnvRef | null;
|
|
17
|
+
/**
|
|
18
|
+
* The marker referencing `key`, carrying `defaultValue` when given. Throws when
|
|
19
|
+
* {@link parseEnvMarker} would not read the marker back as `(key, defaultValue)`: a name is
|
|
20
|
+
* non-empty and carries no `{`, `}` or `:`; a default is non-empty and carries no `}`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function formatEnvMarker(key: string, defaultValue?: string): string;
|
|
23
|
+
/** Every variable name any marker leaf anywhere under `value` references. */
|
|
24
|
+
export declare function collectEnvRefs(value: unknown): Set<string>;
|
|
25
|
+
//# sourceMappingURL=env-markers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-markers.d.ts","sourceRoot":"","sources":["../src/env-markers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,4DAA4D;AAC5D,eAAO,MAAM,iBAAiB,UAAU,CAAC;AAQzC,uDAAuD;AACvD,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,uGAAuG;AACvG,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAO5D;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAY1E;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAgB1D"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `!ENV ${VAR[:default]}` manifest marker grammar the platform reads: a string leaf
|
|
3
|
+
* that begins with the `!ENV ` prefix (the tag and exactly one space) references an
|
|
4
|
+
* environment variable by name, never its value. A variable name carries no `{`, `}` or
|
|
5
|
+
* `:`; an optional `:default` follows it.
|
|
6
|
+
*/
|
|
7
|
+
/** The prefix that makes a string leaf an `!ENV` marker. */
|
|
8
|
+
export const ENV_MARKER_PREFIX = '!ENV ';
|
|
9
|
+
/** A leaf that is exactly one reference: group 1 the name, group 2 the optional default. */
|
|
10
|
+
const SINGLE_REF = /^!ENV \$\{([^}{:]+)(?::([^}]+))?\}$/;
|
|
11
|
+
/** Every reference inside a marker leaf's expression. */
|
|
12
|
+
const ANY_REF = /\$\{([^}{:]+)(?::[^}]+)?\}/g;
|
|
13
|
+
/** The reference of `value` when it is exactly `!ENV ${KEY}` or `!ENV ${KEY:default}`, else `null`. */
|
|
14
|
+
export function parseEnvMarker(value) {
|
|
15
|
+
if (typeof value !== 'string')
|
|
16
|
+
return null;
|
|
17
|
+
const match = SINGLE_REF.exec(value);
|
|
18
|
+
if (match === null)
|
|
19
|
+
return null;
|
|
20
|
+
const key = match[1] ?? '';
|
|
21
|
+
const defaultText = match[2];
|
|
22
|
+
return defaultText === undefined ? { key } : { key, default: defaultText };
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The marker referencing `key`, carrying `defaultValue` when given. Throws when
|
|
26
|
+
* {@link parseEnvMarker} would not read the marker back as `(key, defaultValue)`: a name is
|
|
27
|
+
* non-empty and carries no `{`, `}` or `:`; a default is non-empty and carries no `}`.
|
|
28
|
+
*/
|
|
29
|
+
export function formatEnvMarker(key, defaultValue) {
|
|
30
|
+
const marker = defaultValue === undefined
|
|
31
|
+
? `${ENV_MARKER_PREFIX}\${${key}}`
|
|
32
|
+
: `${ENV_MARKER_PREFIX}\${${key}:${defaultValue}}`;
|
|
33
|
+
const parsed = parseEnvMarker(marker);
|
|
34
|
+
if (parsed?.key !== key || parsed.default !== defaultValue) {
|
|
35
|
+
throw new Error(`var ${JSON.stringify(key)} with default ${JSON.stringify(defaultValue)} cannot be written as an !ENV marker: a name is non-empty without '{', '}' or ':', a default is non-empty without '}'`);
|
|
36
|
+
}
|
|
37
|
+
return marker;
|
|
38
|
+
}
|
|
39
|
+
/** Every variable name any marker leaf anywhere under `value` references. */
|
|
40
|
+
export function collectEnvRefs(value) {
|
|
41
|
+
const refs = new Set();
|
|
42
|
+
const walk = (node) => {
|
|
43
|
+
if (typeof node === 'string') {
|
|
44
|
+
if (!node.startsWith(ENV_MARKER_PREFIX))
|
|
45
|
+
return;
|
|
46
|
+
for (const match of node.slice(ENV_MARKER_PREFIX.length).matchAll(ANY_REF)) {
|
|
47
|
+
if (match[1] !== undefined)
|
|
48
|
+
refs.add(match[1]);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else if (Array.isArray(node)) {
|
|
52
|
+
for (const item of node)
|
|
53
|
+
walk(item);
|
|
54
|
+
}
|
|
55
|
+
else if (typeof node === 'object' && node !== null) {
|
|
56
|
+
for (const nested of Object.values(node))
|
|
57
|
+
walk(nested);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
walk(value);
|
|
61
|
+
return refs;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=env-markers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env-markers.js","sourceRoot":"","sources":["../src/env-markers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC;AAEzC,4FAA4F;AAC5F,MAAM,UAAU,GAAG,qCAAqC,CAAC;AAEzD,yDAAyD;AACzD,MAAM,OAAO,GAAG,6BAA6B,CAAC;AAS9C,uGAAuG;AACvG,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3B,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7B,OAAO,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;AAC7E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW,EAAE,YAAqB;IAChE,MAAM,MAAM,GACV,YAAY,KAAK,SAAS;QACxB,CAAC,CAAC,GAAG,iBAAiB,MAAM,GAAG,GAAG;QAClC,CAAC,CAAC,GAAG,iBAAiB,MAAM,GAAG,IAAI,YAAY,GAAG,CAAC;IACvD,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,MAAM,EAAE,GAAG,KAAK,GAAG,IAAI,MAAM,CAAC,OAAO,KAAK,YAAY,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CACb,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,uHAAuH,CAC/L,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,IAAI,GAAG,CAAC,IAAa,EAAQ,EAAE;QACnC,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC;gBAAE,OAAO;YAChD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC3E,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,SAAS;oBAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YACjD,CAAC;QACH,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,KAAK,MAAM,IAAI,IAAI,IAAI;gBAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC;aAAM,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACrD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACzD,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC,KAAK,CAAC,CAAC;IACZ,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interactions-stream.d.ts","sourceRoot":"","sources":["../../src/hooks/interactions-stream.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"interactions-stream.d.ts","sourceRoot":"","sources":["../../src/hooks/interactions-stream.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,qEAAqE;AACrE,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAsG7E;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAK9D;AAED,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGnD;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACxC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED,wBAAgB,YAAY,IAAI,WAAW,CAQ1C;AAED,kFAAkF;AAClF,wBAAgB,eAAe,CAC7B,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,MAAM,GACZ,IAAI,CAGN;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,WAAW,EACpB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,WAAW,EAAE,GAC3B,IAAI,CAUN;AAED,4FAA4F;AAC5F,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAMzF;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAexD;AAED,iFAAiF;AACjF,wBAAgB,KAAK,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,EAAE,IAAI,EAAE,WAAW,GAAG,iBAAiB,EAAE,CAW1F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,WAAW,EAAE,EAC5B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,WAAW,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM,CAWR"}
|