@theoremjs/react 0.3.0 → 0.3.1
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 +728 -202
- package/client/composer-hints.js +1 -1
- package/client/decision-images.d.ts +76 -0
- package/client/decision-images.js +139 -0
- package/client/decision-transport.d.ts +26 -4
- package/client/decision-transport.js +79 -6
- package/client/embedding-input.d.ts +84 -0
- package/client/embedding-input.js +194 -0
- package/client/embedding-space.d.ts +82 -0
- package/client/embedding-space.js +389 -0
- package/client/embedding-transport.d.ts +40 -0
- package/client/embedding-transport.js +71 -0
- package/client/failure.d.ts +7 -10
- package/client/failure.js +9 -9
- package/client/host-call.d.ts +1 -1
- package/client/host-call.js +7 -2
- package/client/host-transport.d.ts +2 -0
- package/client/host-transport.js +3 -2
- package/client/index.d.ts +19 -11
- package/client/index.js +14 -10
- package/client/ink-waveform.d.ts +1 -1
- package/client/ink-waveform.js +1 -2
- package/client/live/apply-live-turn-tool-event.js +1 -1
- package/client/live/live-captions.d.ts +30 -0
- package/client/live/live-captions.js +105 -1
- package/client/live/live-mic-forward.d.ts +1 -0
- package/client/live/live-mic-forward.js +11 -2
- package/client/live/live-page-tool.d.ts +34 -0
- package/client/live/live-page-tool.js +11 -0
- package/client/live/live-state.d.ts +4 -2
- package/client/live/live-state.js +2 -0
- package/client/live/live-video.js +1 -1
- package/client/live/run-live-tool-call.d.ts +7 -2
- package/client/live/run-live-tool-call.js +48 -10
- package/client/live/use-live-caption-log.d.ts +17 -0
- package/client/live/use-live-caption-log.js +51 -0
- package/client/live-client.d.ts +52 -4
- package/client/live-client.js +250 -59
- package/client/live-messages.d.ts +51 -13
- package/client/live-messages.js +42 -9
- package/client/page-dispatch.d.ts +15 -0
- package/client/page-dispatch.js +63 -0
- package/client/run-commit.d.ts +16 -43
- package/client/run-commit.js +26 -101
- package/client/run-session.d.ts +40 -41
- package/client/run-session.js +104 -112
- package/client/schema-fields.js +27 -6
- package/client/shaped-data.js +37 -8
- package/client/source-citations.js +1 -1
- package/client/space-geometry.d.ts +39 -0
- package/client/space-geometry.js +183 -0
- package/client/state-fields.js +1 -1
- package/client/tool-result.js +30 -12
- package/client/tool-resume.d.ts +18 -1
- package/client/tool-resume.js +4 -1
- package/client/trace-feed.d.ts +1 -1
- package/client/trace-story.d.ts +2 -2
- package/client/trace-story.js +43 -19
- package/client/trace-view.d.ts +2 -2
- package/client/trace-view.js +56 -16
- package/client/transcript-groups.d.ts +28 -2
- package/client/transcript-groups.js +63 -13
- package/client/transcript-schema.d.ts +10 -0
- package/client/transcript-schema.js +120 -0
- package/client/transport.d.ts +114 -26
- package/client/transport.js +106 -32
- package/client/turn-client.d.ts +17 -8
- package/client/turn-client.js +26 -31
- package/client/voice-recorder.d.ts +1 -1
- package/client/voice-recorder.js +2 -5
- package/client/wire-line.js +5 -5
- package/client/worklets/mic-capture.js +9 -9
- package/components/InkWaveform.d.ts +12 -1
- package/components/InkWaveform.js +65 -42
- package/components/live/use-live-runner-controls.d.ts +3 -4
- package/components/live/use-live-runner-controls.js +6 -10
- package/components/live/use-live-runner-model.d.ts +10 -6
- package/components/live/use-live-runner-model.js +29 -14
- package/components/live/use-live-runner-ui.d.ts +10 -8
- package/components/live/use-live-runner-ui.js +19 -11
- package/components/live/use-live-session-client.d.ts +4 -4
- package/components/live/use-live-session-client.js +11 -8
- package/components/use-composer-voice.d.ts +4 -4
- package/components/use-composer-voice.js +8 -8
- package/components/use-editor-selection.js +4 -1
- package/components/use-voice-playback.d.ts +2 -1
- package/components/use-voice-playback.js +4 -4
- package/hooks/use-described.d.ts +5 -1
- package/hooks/use-described.js +11 -9
- package/hooks/use-embedding-index.d.ts +27 -0
- package/hooks/use-embedding-index.js +43 -0
- package/hooks/use-one-call.d.ts +31 -0
- package/hooks/use-one-call.js +76 -0
- package/hooks/use-page-tool-warnings.d.ts +6 -0
- package/hooks/use-page-tool-warnings.js +25 -0
- package/hooks/use-second-ticker.d.ts +2 -0
- package/hooks/use-second-ticker.js +13 -0
- package/hooks/use-theorem-chat-actions.d.ts +25 -21
- package/hooks/use-theorem-chat-actions.js +67 -45
- package/hooks/use-theorem-chat-state.d.ts +6 -6
- package/hooks/use-theorem-chat-state.js +13 -9
- package/hooks/use-theorem-chat.d.ts +48 -14
- package/hooks/use-theorem-chat.js +329 -141
- package/hooks/use-theorem-decision.d.ts +5 -10
- package/hooks/use-theorem-decision.js +4 -67
- package/hooks/use-theorem-embedding.d.ts +18 -0
- package/hooks/use-theorem-embedding.js +12 -0
- package/hooks/use-theorem-host.d.ts +5 -4
- package/hooks/use-theorem-host.js +26 -8
- package/hooks/use-theorem-interface.d.ts +6 -3
- package/hooks/use-theorem-interface.js +12 -22
- package/index.d.ts +6 -3
- package/index.js +5 -2
- package/live.d.ts +1 -0
- package/live.js +1 -1
- package/package.json +6 -5
- package/server/checkpoint-ownership.d.ts +6 -0
- package/server/checkpoint-ownership.js +42 -0
- package/server/conversation-store.d.ts +92 -0
- package/server/conversation-store.js +131 -0
- package/server/conversation.d.ts +193 -0
- package/server/conversation.js +552 -0
- package/server/decision-handler.d.ts +12 -4
- package/server/decision-handler.js +29 -38
- package/server/embedding-handler.d.ts +33 -0
- package/server/embedding-handler.js +45 -0
- package/server/handler.d.ts +58 -20
- package/server/handler.js +441 -238
- package/server/mod.d.ts +15 -6
- package/server/mod.js +13 -5
- package/server/page-dispatch.d.ts +22 -0
- package/server/page-dispatch.js +127 -0
- package/server/request-body.d.ts +2 -0
- package/server/request-body.js +42 -0
- package/server/request-check.d.ts +6 -1
- package/server/request-check.js +27 -12
- package/server/session-authority.d.ts +8 -0
- package/server/session-authority.js +78 -0
- package/server/session-boundary.d.ts +26 -0
- package/server/session-boundary.js +64 -0
- package/server/session-identity.d.ts +16 -0
- package/server/session-identity.js +41 -0
- package/server/session-limits.d.ts +12 -0
- package/server/session-limits.js +59 -0
- package/server/session-store.d.ts +69 -20
- package/server/session-store.js +220 -5
- package/server/single-action.d.ts +21 -0
- package/server/single-action.js +30 -0
- package/server/steer-inbox.js +1 -1
- package/server/tool-operations.d.ts +6 -0
- package/server/tool-operations.js +132 -0
- package/server/turn-input.d.ts +10 -0
- package/server/turn-input.js +29 -0
- package/styles/ink-controls.css +78 -78
- package/ui/CardWall.d.ts +10 -0
- package/ui/CardWall.js +61 -0
- package/ui/ChatComposerBar.d.ts +28 -5
- package/ui/ChatComposerBar.js +85 -30
- package/ui/ChatTranscript.d.ts +14 -1
- package/ui/ChatTranscript.js +110 -57
- package/ui/ConsoleFrame.d.ts +61 -4
- package/ui/ConsoleFrame.js +109 -9
- package/ui/DecisionAnswers.d.ts +40 -2
- package/ui/DecisionAnswers.js +166 -35
- package/ui/DecisionImages.d.ts +16 -0
- package/ui/DecisionImages.js +46 -0
- package/ui/EmbeddingDocuments.d.ts +78 -0
- package/ui/EmbeddingDocuments.js +278 -0
- package/ui/EmbeddingQuery.d.ts +27 -0
- package/ui/EmbeddingQuery.js +95 -0
- package/ui/EmbeddingResults.d.ts +41 -0
- package/ui/EmbeddingResults.js +165 -0
- package/ui/EmbeddingSpace.d.ts +26 -0
- package/ui/EmbeddingSpace.js +106 -0
- package/ui/LiveCaptionsPanel.d.ts +22 -0
- package/ui/LiveCaptionsPanel.js +27 -0
- package/ui/LiveRunner.d.ts +6 -4
- package/ui/LiveRunner.js +42 -55
- package/ui/PaneState.d.ts +33 -0
- package/ui/PaneState.js +50 -0
- package/ui/SchemaFields.d.ts +1 -1
- package/ui/SchemaFields.js +54 -26
- package/ui/ShapedData.js +73 -28
- package/ui/SidePanel.d.ts +74 -5
- package/ui/SidePanel.js +82 -9
- package/ui/StateFields.d.ts +10 -2
- package/ui/StateFields.js +56 -13
- package/ui/TheoremChat.d.ts +65 -14
- package/ui/TheoremChat.js +66 -78
- package/ui/TheoremDecision.d.ts +12 -20
- package/ui/TheoremDecision.js +78 -35
- package/ui/TheoremEmbedding.d.ts +27 -0
- package/ui/TheoremEmbedding.js +122 -0
- package/ui/TheoremHost.d.ts +22 -5
- package/ui/TheoremHost.js +127 -37
- package/ui/ToolGateCard.d.ts +5 -1
- package/ui/ToolGateCard.js +30 -23
- package/ui/ToolResult.js +12 -10
- package/ui/ToolResultCharts.js +29 -12
- package/ui/TraceCharts.d.ts +2 -2
- package/ui/TraceCharts.js +48 -17
- package/ui/TraceGuardrails.d.ts +3 -2
- package/ui/TraceGuardrails.js +24 -6
- package/ui/TraceInspector.d.ts +11 -1
- package/ui/TraceInspector.js +45 -28
- package/ui/TraceInspectorPanel.d.ts +31 -9
- package/ui/TraceInspectorPanel.js +77 -39
- package/ui/TraceOverview.d.ts +7 -3
- package/ui/TraceOverview.js +44 -14
- package/ui/TraceSpanDetail.js +24 -22
- package/ui/TraceStory.js +9 -8
- package/ui/TraceValues.js +19 -9
- package/ui/TraceWaterfall.js +12 -9
- package/ui/VoiceNote.js +4 -4
- package/ui/approval-heading.d.ts +4 -0
- package/ui/approval-heading.js +9 -0
- package/ui/arrive.d.ts +18 -0
- package/ui/arrive.js +46 -0
- package/ui/built/theme.css +86 -68
- package/ui/built/theme.css.d.ts +9 -0
- package/ui/built/theorem.d.ts +3 -3
- package/ui/built/theorem.js +34 -5
- package/ui/built/theorem.variants.d.ts +8 -2
- package/ui/code-language.d.ts +12 -0
- package/ui/code-language.js +50 -0
- package/ui/disclosure-motion.js +53 -9
- package/ui/embedding-space-gl.d.ts +37 -0
- package/ui/embedding-space-gl.js +663 -0
- package/ui/embedding-space-view.d.ts +8 -0
- package/ui/embedding-space-view.js +8 -0
- package/ui/glide.d.ts +7 -0
- package/ui/glide.js +44 -0
- package/ui/icons.js +2 -1
- package/ui/index.d.ts +20 -9
- package/ui/index.js +20 -10
- package/ui/labels-provider.js +5 -2
- package/ui/labels.d.ts +374 -85
- package/ui/labels.js +1028 -188
- package/ui/markdown-plugins.d.ts +9 -0
- package/ui/markdown-plugins.js +13 -0
- package/ui/mcp-icon.d.ts +6 -0
- package/ui/mcp-icon.js +8 -0
- package/ui/row-keys.d.ts +19 -0
- package/ui/row-keys.js +33 -0
- package/ui/theme.d.ts +1 -1
- package/ui/theme.js +2 -2
- package/ui/theorem-theme.js +32 -10
- package/ui/trace-palette.d.ts +2 -16
- package/ui/trace-palette.js +2 -16
- package/ui/trace-patterns.d.ts +11 -3
- package/ui/trace-patterns.js +16 -3
- package/ui/transcript-copy-text.js +1 -0
- package/ui/wall-columns.d.ts +8 -0
- package/ui/wall-columns.js +19 -0
- package/client/walk-away.d.ts +0 -34
- package/client/walk-away.js +0 -48
- package/server/walk-away.d.ts +0 -22
- package/server/walk-away.js +0 -41
package/ui/SchemaFields.js
CHANGED
|
@@ -9,27 +9,35 @@ import { Selector } from '@astryxdesign/core/Selector';
|
|
|
9
9
|
import { Slider } from '@astryxdesign/core/Slider';
|
|
10
10
|
import { StackItem } from '@astryxdesign/core/Stack';
|
|
11
11
|
import { Switch } from '@astryxdesign/core/Switch';
|
|
12
|
+
import { Text } from '@astryxdesign/core/Text';
|
|
12
13
|
import { TextArea } from '@astryxdesign/core/TextArea';
|
|
13
14
|
import { TextInput } from '@astryxdesign/core/TextInput';
|
|
14
15
|
import { Tokenizer } from '@astryxdesign/core/Tokenizer';
|
|
15
16
|
import { VStack } from '@astryxdesign/core/VStack';
|
|
16
17
|
import { useState } from 'react';
|
|
17
|
-
import { blankRow, schemaFields, withField } from "../client/schema-fields.js";
|
|
18
|
+
import { blankRow, schemaFields, withField, } from "../client/schema-fields.js";
|
|
18
19
|
import { isRow, json } from "../client/shaped-data.js";
|
|
19
20
|
import { useLabels } from "./labels-provider.js";
|
|
21
|
+
import { useKeyedRows } from "./row-keys.js";
|
|
20
22
|
import { AddRow, Group, Labelled, RemoveRow } from "./StateFields.js";
|
|
21
23
|
const NO_SUGGESTIONS = { search: () => [], bootstrap: () => [] };
|
|
22
24
|
const CODE_FONT = { '--font-family-body': 'var(--font-family-code)' };
|
|
25
|
+
/** Lines a description shows before it is cut short. */
|
|
26
|
+
const DESCRIPTION_LINES = 2;
|
|
27
|
+
/** What a field is for, cut to a couple of lines. Hovering a cut one shows all of it. */
|
|
28
|
+
function Description({ text }) {
|
|
29
|
+
return (_jsx(Text, { type: "supporting", color: "secondary", maxLines: DESCRIPTION_LINES, children: text }));
|
|
30
|
+
}
|
|
23
31
|
/** A field whose input carries no label of its own, labelled as one. */
|
|
24
32
|
function Described({ field, children }) {
|
|
25
|
-
return (_jsx(Labelled, { label: field.label, isRequired: field.isRequired,
|
|
33
|
+
return (_jsx(Labelled, { label: field.label, isRequired: field.isRequired, children: children }));
|
|
26
34
|
}
|
|
27
35
|
/** A field the schema doesn't shape (no type, a union, a free object), as its JSON. */
|
|
28
36
|
function JsonField({ field, value, onChange }) {
|
|
29
37
|
const t = useLabels();
|
|
30
38
|
const [text, setText] = useState(() => (value === undefined ? '' : json(value)));
|
|
31
39
|
const [isValid, setValid] = useState(true);
|
|
32
|
-
return (_jsx(TextArea, { label: field.label,
|
|
40
|
+
return (_jsx(TextArea, { label: field.label, isRequired: field.isRequired, size: "sm", rows: 3, value: text, hasSpellCheck: false, status: isValid ? undefined : { type: 'error', message: t('@theorem.decision.invalid_json') }, controlStyle: CODE_FONT, onChange: (next) => {
|
|
33
41
|
setText(next);
|
|
34
42
|
if (next.trim() === '') {
|
|
35
43
|
setValid(true);
|
|
@@ -46,51 +54,69 @@ function JsonField({ field, value, onChange }) {
|
|
|
46
54
|
} }));
|
|
47
55
|
}
|
|
48
56
|
/** A list of objects: each row its own fields, removable, and one more at the end. */
|
|
49
|
-
function RowsField({ field, item, value, onChange }) {
|
|
57
|
+
function RowsField({ field, item, value, onChange, }) {
|
|
50
58
|
const rows = Array.isArray(value) ? value : [];
|
|
51
|
-
|
|
59
|
+
const keyed = useKeyedRows(rows);
|
|
60
|
+
return (_jsx(Group, { label: field.label, isRequired: field.isRequired, description: field.description && _jsx(Description, { text: field.description }), children: _jsxs(VStack, { gap: 3, children: [keyed.rows.map(({ item: row, index, key }) => (_jsxs(HStack, { gap: 2, vAlign: "start", children: [_jsx(StackItem, { size: "fill", children: _jsx(ObjectFields, { schema: item, value: isRow(row) ? row : {}, onChange: (next) => onChange(rows.map((old, at) => (at === index ? next : old))) }) }), _jsx(RemoveRow, { index: index, onRemove: () => {
|
|
61
|
+
keyed.drop(index);
|
|
62
|
+
onChange(rows.filter((_, at) => at !== index));
|
|
63
|
+
} })] }, key))), _jsx(AddRow, { field: field.label, onAdd: () => onChange([...rows, blankRow(item)]) })] }) }));
|
|
52
64
|
}
|
|
53
65
|
function common(field) {
|
|
54
|
-
return {
|
|
66
|
+
return {
|
|
67
|
+
label: field.label,
|
|
68
|
+
isRequired: field.isRequired,
|
|
69
|
+
size: 'sm',
|
|
70
|
+
};
|
|
55
71
|
}
|
|
56
72
|
/** One of a few options as segments; more as a menu. */
|
|
57
|
-
function ChoiceField({ field, options, isMenu, value, onChange }) {
|
|
73
|
+
function ChoiceField({ field, options, isMenu, value, onChange, }) {
|
|
58
74
|
if (isMenu) {
|
|
59
|
-
return _jsx(Selector, { ...common(field), options: options, value: typeof value === 'string' ? value : undefined, hasSearch: options.length > 10, onChange: onChange });
|
|
75
|
+
return (_jsx(Selector, { ...common(field), options: options, value: typeof value === 'string' ? value : undefined, hasSearch: options.length > 10, onChange: onChange }));
|
|
60
76
|
}
|
|
61
77
|
return (_jsx(Described, { field: field, children: _jsx(HStack, { children: _jsx(SegmentedControl, { label: field.label, size: "sm", value: typeof value === 'string' ? value : '', onChange: onChange, children: options.map((option) => (_jsx(SegmentedControlItem, { value: option, label: option }, option))) }) }) }));
|
|
62
78
|
}
|
|
63
|
-
function TextField({ field, isLong, type, value, onChange }) {
|
|
79
|
+
function TextField({ field, isLong, type, value, onChange, }) {
|
|
64
80
|
const text = typeof value === 'string' ? value : '';
|
|
65
|
-
return isLong ? _jsx(TextArea, { ...common(field), rows: 3, value: text, onChange: onChange }) : _jsx(TextInput, { ...common(field), type: type, value: text, onChange: onChange });
|
|
81
|
+
return isLong ? (_jsx(TextArea, { ...common(field), rows: 3, value: text, onChange: onChange })) : (_jsx(TextInput, { ...common(field), type: type, value: text, onChange: onChange }));
|
|
66
82
|
}
|
|
67
83
|
/** A list of words or numbers as tokens. */
|
|
68
84
|
function WordsField({ field, value, onChange }) {
|
|
69
85
|
const t = useLabels();
|
|
70
86
|
const words = Array.isArray(value) ? value : [];
|
|
71
|
-
return (_jsx(Tokenizer, { ...common(field), value: words.map((word, index) => ({
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
return (_jsx(Tokenizer, { ...common(field), value: words.map((word, index) => ({
|
|
88
|
+
id: `${String(index)}:${String(word)}`,
|
|
89
|
+
label: String(word),
|
|
90
|
+
})), searchSource: NO_SUGGESTIONS, hasCreate: true, debounceMs: 0, placeholder: t('@theorem.decision.add_value'), onChange: (items) => onChange(items.map((item) => typeof words[0] === 'number' && Number.isFinite(Number(item.label))
|
|
91
|
+
? Number(item.label)
|
|
92
|
+
: item.label)) }));
|
|
74
93
|
}
|
|
75
94
|
/** Each control's input, as the schema asks for it. */
|
|
76
95
|
const CONTROLS = {
|
|
77
96
|
choice: ({ field, control, value, onChange }) => (_jsx(ChoiceField, { field: field, options: control.options, isMenu: control.isMenu, value: value, onChange: onChange })),
|
|
78
|
-
range: ({ field, control, value, onChange }) => (_jsx(Slider, { label: field.label,
|
|
97
|
+
range: ({ field, control, value, onChange }) => (_jsx(Slider, { label: field.label, isRequired: field.isRequired, min: control.min, max: control.max, step: 1, value: typeof value === 'number' ? value : control.min, valueDisplay: "text", onChange: onChange })),
|
|
79
98
|
number: ({ field, control, value, onChange }) => (_jsx(NumberInput, { ...common(field), value: typeof value === 'number' ? value : undefined, min: control.min, max: control.max, isIntegerOnly: control.isInteger, isWheelEnabled: false, onChange: (next) => onChange(next ?? undefined) })),
|
|
80
|
-
switch: ({ field, value, onChange }) => _jsx(Switch, { label: field.label,
|
|
99
|
+
switch: ({ field, value, onChange }) => (_jsx(Switch, { label: field.label, value: value === true, onChange: onChange })),
|
|
81
100
|
date: ({ field, value, onChange }) => (_jsx(DateInput, { ...common(field), value: typeof value === 'string' ? value : undefined, onChange: onChange })),
|
|
82
|
-
text: ({ field, control, value, onChange }) => _jsx(TextField, { field: field, isLong: control.isLong, type: control.type, value: value, onChange: onChange }),
|
|
83
|
-
choices: ({ field, control, value, onChange }) => (_jsx(CheckboxList, { label: field.label,
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
text: ({ field, control, value, onChange }) => (_jsx(TextField, { field: field, isLong: control.isLong, type: control.type, value: value, onChange: onChange })),
|
|
102
|
+
choices: ({ field, control, value, onChange }) => (_jsx(CheckboxList, { label: field.label, value: Array.isArray(value)
|
|
103
|
+
? value.filter((entry) => typeof entry === 'string')
|
|
104
|
+
: [], onChange: onChange, children: control.options.map((option) => (_jsx(CheckboxListItem, { label: option, value: option }, option))) })),
|
|
105
|
+
words: ({ field, value, onChange }) => (_jsx(WordsField, { field: field, value: value, onChange: onChange })),
|
|
106
|
+
rows: ({ field, control, value, onChange }) => (_jsx(RowsField, { field: field, item: control.item, value: value, onChange: onChange })),
|
|
107
|
+
group: ({ field, control, value, onChange }) => (_jsx(Group, { label: field.label, isRequired: field.isRequired, description: field.description && _jsx(Description, { text: field.description }), children: _jsx(ObjectFields, { schema: control.schema, value: isRow(value) ? value : {}, onChange: onChange }) })),
|
|
108
|
+
json: ({ field, value, onChange }) => (_jsx(JsonField, { field: field, value: value, onChange: onChange })),
|
|
88
109
|
};
|
|
89
110
|
function Field({ field, value, onChange }) {
|
|
90
111
|
const control = CONTROLS[field.control.kind];
|
|
91
|
-
|
|
112
|
+
const input = control({ field, control: field.control, value, onChange });
|
|
113
|
+
// why: A group already says its description under its name.
|
|
114
|
+
const isGroup = field.control.kind === 'rows' || field.control.kind === 'group';
|
|
115
|
+
if (!field.description || isGroup)
|
|
116
|
+
return input;
|
|
117
|
+
return (_jsxs(VStack, { gap: 1, children: [input, _jsx(Description, { text: field.description })] }));
|
|
92
118
|
}
|
|
93
|
-
/** Room a short field takes: numbers, dates, menus and one-line text sit side by side. */
|
|
119
|
+
/** Room a short field takes: numbers, dates, menus, and one-line text sit side by side. */
|
|
94
120
|
const SHORT_FIELD_PX = 200;
|
|
95
121
|
/** Segments whose labels run this long in all still fit a short field's width. */
|
|
96
122
|
const SHORT_SEGMENT_CHARS = 18;
|
|
@@ -109,14 +135,16 @@ function isShort({ control }) {
|
|
|
109
135
|
return false;
|
|
110
136
|
}
|
|
111
137
|
}
|
|
112
|
-
function ObjectFields({ schema, value, onChange }) {
|
|
113
|
-
return (_jsx(Grid, { columns: { minWidth: SHORT_FIELD_PX, repeat: 'fill' }, gap: 3, children: schemaFields(schema).map((field) => (_jsx("div", { style: isShort(field)
|
|
138
|
+
function ObjectFields({ schema, value, onChange, }) {
|
|
139
|
+
return (_jsx(Grid, { columns: { minWidth: SHORT_FIELD_PX, repeat: 'fill' }, gap: 3, children: schemaFields(schema).map((field) => (_jsx("div", { style: isShort(field)
|
|
140
|
+
? { minWidth: 0, alignSelf: 'start' }
|
|
141
|
+
: { gridColumn: '1 / -1', minWidth: 0 }, children: _jsx(Field, { field: field, value: value[field.key], onChange: (next) => onChange(withField(value, field.key, next)) }) }, field.key))) }));
|
|
114
142
|
}
|
|
115
143
|
/**
|
|
116
144
|
* A tool's request as a form drawn from its input schema: each field the
|
|
117
145
|
* input its type asks for, labelled by its title or key and described by its
|
|
118
146
|
* description. Optional fields left empty aren't sent.
|
|
119
147
|
*/
|
|
120
|
-
export function SchemaFields({ schema, value, onChange }) {
|
|
148
|
+
export function SchemaFields({ schema, value, onChange, }) {
|
|
121
149
|
return _jsx(ObjectFields, { schema: schema, value: value, onChange: onChange });
|
|
122
150
|
}
|
package/ui/ShapedData.js
CHANGED
|
@@ -14,8 +14,14 @@ import { Token } from '@astryxdesign/core/Token';
|
|
|
14
14
|
import { VStack } from '@astryxdesign/core/VStack';
|
|
15
15
|
import { Component, useMemo, useState } from 'react';
|
|
16
16
|
import { fieldLabel, fieldReading, isPlain, isProse, isRow, json, MAX_ROWS, plainReading, rowHeading, shapeOf, splitFields, unpacked, withUnit, } from "../client/shaped-data.js";
|
|
17
|
+
import { Arrive, useArrive } from "./arrive.js";
|
|
17
18
|
import { useLabels } from "./labels-provider.js";
|
|
19
|
+
import { useMarkdownPlugins } from "./markdown-plugins.js";
|
|
20
|
+
import { keyedByContent } from "./row-keys.js";
|
|
18
21
|
/** Each way a plain value reads, drawn. */
|
|
22
|
+
function ProseReading({ text }) {
|
|
23
|
+
return (_jsx(Markdown, { density: "compact", headingLevelStart: 4, plugins: useMarkdownPlugins(), children: text }));
|
|
24
|
+
}
|
|
19
25
|
const READINGS = {
|
|
20
26
|
none: () => _jsx(Text, { color: "secondary", children: "\u2014" }),
|
|
21
27
|
boolean: ({ value }, _, t) => t(value ? '@theorem.data.yes' : '@theorem.data.no'),
|
|
@@ -23,18 +29,18 @@ const READINGS = {
|
|
|
23
29
|
link: ({ href }) => (_jsx(Link, { href: href, target: "_blank", rel: "noreferrer", children: href })),
|
|
24
30
|
date: ({ date }, formats) => formats.date.format(date),
|
|
25
31
|
'date-time': ({ date }, formats) => formats.dateTime.format(date),
|
|
26
|
-
// Prose (an answer, a summary) is usually Markdown; headings start small, under the section's own.
|
|
27
|
-
text: ({ text }) => isProse(text) ? (_jsx(
|
|
32
|
+
// why: Prose (an answer, a summary) is usually Markdown; headings start small, under the section's own.
|
|
33
|
+
text: ({ text }) => isProse(text) ? (_jsx(ProseReading, { text: text })) : (_jsx("span", { style: { whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }, children: text })),
|
|
28
34
|
};
|
|
29
35
|
function Plain({ value, unit }) {
|
|
30
36
|
const t = useLabels();
|
|
31
37
|
const locale = useLocale();
|
|
32
38
|
const formats = useMemo(() => ({
|
|
33
|
-
// No grouping: ids and years read as written. Six significant digits: a timing reads 1.40369.
|
|
39
|
+
// why: No grouping: ids and years read as written. Six significant digits: a timing reads 1.40369.
|
|
34
40
|
integer: new Intl.NumberFormat(locale, { useGrouping: false }),
|
|
35
41
|
decimal: new Intl.NumberFormat(locale, { useGrouping: false, maximumSignificantDigits: 6 }),
|
|
36
42
|
dateTime: new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeStyle: 'short' }),
|
|
37
|
-
// A bare date is a calendar day, parsed at UTC midnight: read it in UTC, or it slips a day west of Greenwich.
|
|
43
|
+
// why: A bare date is a calendar day, parsed at UTC midnight: read it in UTC, or it slips a day west of Greenwich.
|
|
38
44
|
date: new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeZone: 'UTC' }),
|
|
39
45
|
}), [locale]);
|
|
40
46
|
const reading = plainReading(value, unit);
|
|
@@ -53,7 +59,7 @@ function More({ total }) {
|
|
|
53
59
|
return null;
|
|
54
60
|
return (_jsx(Text, { type: "supporting", color: "secondary", children: t('@theorem.data.more', { count: String(total - MAX_ROWS) }) }));
|
|
55
61
|
}
|
|
56
|
-
function DataTable({ columns, rows, units }) {
|
|
62
|
+
function DataTable({ columns, rows, units, }) {
|
|
57
63
|
const tableColumns = columns.map((key, index) => {
|
|
58
64
|
const { label, unit } = fieldLabel(key, units);
|
|
59
65
|
return {
|
|
@@ -74,46 +80,80 @@ function ListRowEnd({ row }) {
|
|
|
74
80
|
return (_jsxs(Text, { type: "supporting", color: "secondary", children: [label, " ", _jsx(Plain, { value: value, unit: unit })] }, key));
|
|
75
81
|
}), row.tags.map((tag) => (_jsx(Token, { label: tag, size: "sm" }, tag)))] }));
|
|
76
82
|
}
|
|
77
|
-
function
|
|
78
|
-
return
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
function dataListItem(row) {
|
|
84
|
+
return {
|
|
85
|
+
label: row.title,
|
|
86
|
+
description: row.description,
|
|
87
|
+
href: row.href,
|
|
88
|
+
target: row.href ? '_blank' : undefined,
|
|
89
|
+
rel: row.href ? 'noreferrer' : undefined,
|
|
90
|
+
endContent: _jsx(ListRowEnd, { row: row }),
|
|
91
|
+
};
|
|
81
92
|
}
|
|
82
|
-
|
|
93
|
+
/** A top-level row eases in with the rest of a returned result. Nested rows stay put. */
|
|
94
|
+
function ArrivingDataRow({ row }) {
|
|
95
|
+
const arrive = useArrive();
|
|
96
|
+
return _jsx(ListItem, { className: arrive?.className, style: arrive?.style, ...dataListItem(row) });
|
|
97
|
+
}
|
|
98
|
+
function DataList({ rows, depth }) {
|
|
99
|
+
return (_jsxs(VStack, { gap: 1, children: [_jsx(List, { hasDividers: true, density: "compact", children: keyedByContent(rows.slice(0, MAX_ROWS), (row) => row.title).map(({ item: row, key }) => depth === 0 ? (_jsx(ArrivingDataRow, { row: row }, key)) : (_jsx(ListItem, { ...dataListItem(row) }, key))) }), _jsx(More, { total: rows.length })] }));
|
|
100
|
+
}
|
|
101
|
+
function ObjectRow({ item, index, depth, className, style, }) {
|
|
83
102
|
const t = useLabels();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
103
|
+
const heading = rowHeading(item);
|
|
104
|
+
return (_jsx(Collapsible, { className: className, style: style, value: String(index), trigger: _jsxs(HStack, { gap: 2, vAlign: "center", wrap: "wrap", children: [_jsx(Text, { type: "body", children: heading.title ?? t('@theorem.data.item', { index: String(index + 1) }) }), heading.tags.map((tag) => (_jsx(Token, { label: tag, size: "sm" }, tag)))] }), children: _jsx(Node, { value: heading.rest, depth: depth + 1 }) }));
|
|
105
|
+
}
|
|
106
|
+
/** A top-level object row eases in with the result. One nested under it does not. */
|
|
107
|
+
function ArrivingObjectRow({ item, index, depth, }) {
|
|
108
|
+
const arrive = useArrive();
|
|
109
|
+
return (_jsx(ObjectRow, { item: item, index: index, depth: depth, className: arrive?.className, style: arrive?.style }));
|
|
110
|
+
}
|
|
111
|
+
function Rows({ items, depth }) {
|
|
112
|
+
return (_jsxs(VStack, { gap: 1, children: [_jsx(CollapsibleGroup, { type: "multiple", hasDividers: true, density: "compact", children: keyedByContent(items.slice(0, MAX_ROWS), (item) => JSON.stringify(item) ?? '').map(({ item, index, key }) => depth === 0 ? (_jsx(ArrivingObjectRow, { item: item, index: index, depth: depth }, key)) : (_jsx(ObjectRow, { item: item, index: index, depth: depth }, key))) }), _jsx(More, { total: items.length })] }));
|
|
113
|
+
}
|
|
114
|
+
function FieldSection({ shown, depth, className, style, }) {
|
|
115
|
+
return (_jsx(Collapsible, { className: className, style: style, value: shown.key, trigger: _jsx(Text, { type: "label", children: shown.title }), children: _jsx(VStack, { paddingInlineStart: 3, children: _jsx(Node, { value: shown.value, depth: depth + 1, units: shown.units }) }) }));
|
|
116
|
+
}
|
|
117
|
+
function ArrivingFieldSection({ shown, depth }) {
|
|
118
|
+
const arrive = useArrive();
|
|
119
|
+
return (_jsx(FieldSection, { shown: shown, depth: depth, className: arrive?.className, style: arrive?.style }));
|
|
90
120
|
}
|
|
91
121
|
function Fields({ row, depth, units }) {
|
|
92
122
|
const { plain, sections } = splitFields(row);
|
|
93
123
|
const fields = plain.length > 0 && (_jsx(MetadataList, { label: { position: 'start', width: '40%' }, children: plain.map(([key, value]) => {
|
|
94
124
|
const { label, unit } = fieldReading(key, value, units);
|
|
95
|
-
return (_jsx(MetadataListItem, { label: label, children: Array.isArray(unpacked(value)) ? _jsx(Node, { value: value, depth: depth + 1 }) : _jsx(Plain, { value: value, unit: unit }) }, key));
|
|
125
|
+
return (_jsx(MetadataListItem, { label: label, children: Array.isArray(unpacked(value)) ? (_jsx(Node, { value: value, depth: depth + 1 })) : (_jsx(Plain, { value: value, unit: unit })) }, key));
|
|
96
126
|
}) }));
|
|
97
127
|
return (_jsxs(VStack, { gap: 2, children: [depth > 0 && fields, sections.length > 0 && (_jsx(CollapsibleGroup, { type: "multiple", hasDividers: true, density: "compact",
|
|
98
|
-
// The top level's first section starts open: it's usually the payload. The rest wait to be asked for.
|
|
99
|
-
defaultValue: depth === 0 ? sections.slice(0, 1).map((shown) => shown.key) : [], children: sections.map((shown) => (_jsx(
|
|
128
|
+
// why: The top level's first section starts open: it's usually the payload. The rest wait to be asked for.
|
|
129
|
+
defaultValue: depth === 0 ? sections.slice(0, 1).map((shown) => shown.key) : [], children: sections.map((shown) => depth === 0 ? (_jsx(ArrivingFieldSection, { shown: shown, depth: depth }, shown.key)) : (_jsx(FieldSection, { shown: shown, depth: depth }, shown.key))) })), depth === 0 && fields ? _jsx(Arrive, { children: fields }) : null] }));
|
|
100
130
|
}
|
|
101
131
|
/** Each shape a value takes, drawn. */
|
|
102
132
|
const SHAPES = {
|
|
103
133
|
json: (_, { value }) => _jsx(Json, { text: json(value) }),
|
|
104
134
|
none: (_, { t }) => _jsx(Text, { color: "secondary", children: t('@theorem.data.none') }),
|
|
105
135
|
plain: (_, { value }) => (_jsx(Text, { type: "body", children: _jsx(Plain, { value: value }) })),
|
|
106
|
-
tokens: ({ items }) => (_jsxs(VStack, { gap: 1, children: [_jsx(HStack, { gap: 1, wrap: "wrap", children: items.slice(0, MAX_ROWS).map((item,
|
|
107
|
-
list: ({ rows }) => _jsx(DataList, { rows: rows }),
|
|
108
|
-
table: ({ columns, rows }, { units }) => _jsx(DataTable, { columns: columns, rows: rows, units: units }),
|
|
136
|
+
tokens: ({ items }) => (_jsxs(VStack, { gap: 1, children: [_jsx(HStack, { gap: 1, wrap: "wrap", children: keyedByContent(items.slice(0, MAX_ROWS), (item) => String(item)).map(({ item, key }) => (_jsx(Token, { label: item === null ? '—' : String(item), size: "sm" }, key))) }), _jsx(More, { total: items.length })] })),
|
|
137
|
+
list: ({ rows }, { depth }) => _jsx(DataList, { rows: rows, depth: depth }),
|
|
138
|
+
table: ({ columns, rows }, { units }) => (_jsx(DataTable, { columns: columns, rows: rows, units: units })),
|
|
109
139
|
rows: ({ items }, { depth }) => _jsx(Rows, { items: items, depth: depth }),
|
|
110
140
|
fields: ({ row }, { depth, units }) => _jsx(Fields, { row: row, depth: depth, units: units }),
|
|
111
141
|
};
|
|
112
|
-
function Node({ value: raw, depth, units }) {
|
|
142
|
+
function Node({ value: raw, depth, units, }) {
|
|
113
143
|
const t = useLabels();
|
|
114
144
|
const value = unpacked(raw);
|
|
115
145
|
const shape = shapeOf(value, depth);
|
|
116
|
-
|
|
146
|
+
const body = SHAPES[shape.kind](shape, {
|
|
147
|
+
value,
|
|
148
|
+
depth,
|
|
149
|
+
units,
|
|
150
|
+
t,
|
|
151
|
+
});
|
|
152
|
+
// why: A list, a set of rows, or an object's sections stagger their own children. Anything else at the top of a result is one arrival.
|
|
153
|
+
if (depth !== 0 || shape.kind === 'list' || shape.kind === 'rows' || shape.kind === 'fields') {
|
|
154
|
+
return body;
|
|
155
|
+
}
|
|
156
|
+
return _jsx(Arrive, { children: body });
|
|
117
157
|
}
|
|
118
158
|
/** A render the rules didn't foresee throws into the JSON, never into the transcript. */
|
|
119
159
|
class ShapeBoundary extends Component {
|
|
@@ -121,6 +161,9 @@ class ShapeBoundary extends Component {
|
|
|
121
161
|
static getDerivedStateFromError() {
|
|
122
162
|
return { failed: true };
|
|
123
163
|
}
|
|
164
|
+
componentDidCatch() {
|
|
165
|
+
this.props.onFallback();
|
|
166
|
+
}
|
|
124
167
|
render() {
|
|
125
168
|
return this.state.failed ? this.props.fallback : this.props.children;
|
|
126
169
|
}
|
|
@@ -131,10 +174,8 @@ export function ShapedData({ value: raw, title, lead, ledKeys = [] }) {
|
|
|
131
174
|
const locale = useLocale();
|
|
132
175
|
const [view, setView] = useState('data');
|
|
133
176
|
const value = unpacked(raw);
|
|
134
|
-
// A plain value reads the same either way; only structure gets the switch.
|
|
135
177
|
const structured = !isPlain(value);
|
|
136
178
|
const text = useMemo(() => (structured ? json(value) : ''), [structured, value]);
|
|
137
|
-
// What the lead hasn't shown; nothing when it showed every field.
|
|
138
179
|
const rest = useMemo(() => {
|
|
139
180
|
if (!ledKeys.length || !isRow(value))
|
|
140
181
|
return value;
|
|
@@ -142,10 +183,14 @@ export function ShapedData({ value: raw, title, lead, ledKeys = [] }) {
|
|
|
142
183
|
return left.length ? Object.fromEntries(left) : undefined;
|
|
143
184
|
}, [value, ledKeys]);
|
|
144
185
|
if (text.length > LARGE_JSON_CHARS) {
|
|
145
|
-
const size = new Intl.NumberFormat(locale, {
|
|
186
|
+
const size = new Intl.NumberFormat(locale, {
|
|
187
|
+
style: 'unit',
|
|
188
|
+
unit: 'kilobyte',
|
|
189
|
+
maximumFractionDigits: 0,
|
|
190
|
+
}).format(LARGE_JSON_CHARS / 1000);
|
|
146
191
|
return (_jsxs(VStack, { gap: 2, children: [_jsx(Text, { type: "supporting", color: "secondary", children: title }), _jsx(Text, { type: "supporting", color: "secondary", children: t('@theorem.data.large', { size }) }), _jsx(Json, { text: text })] }));
|
|
147
192
|
}
|
|
148
193
|
return (_jsxs(VStack, { gap: 2, children: [_jsxs(HStack, { gap: 2, vAlign: "center", hAlign: "between", children: [_jsx(Text, { type: "supporting", color: "secondary", children: title }), structured && (_jsxs(SegmentedControl, { label: t('@theorem.data.view'), size: "sm", value: view, onChange: (next) => {
|
|
149
194
|
setView(next === 'json' ? 'json' : 'data');
|
|
150
|
-
}, children: [_jsx(SegmentedControlItem, { value: "data", label: t('@theorem.data.shaped') }), _jsx(SegmentedControlItem, { value: "json", label: t('@theorem.data.json') })] }))] }), view === 'json' && structured ? (_jsx(Json, { text: text })) : (_jsx(ShapeBoundary, { fallback: _jsx(Json, { text: text }), children: _jsxs(VStack, { gap: 3, children: [lead, rest === undefined ? null : _jsx(Node, { value: rest, depth: 0 })] }) }))] }));
|
|
195
|
+
}, children: [_jsx(SegmentedControlItem, { value: "data", label: t('@theorem.data.shaped') }), _jsx(SegmentedControlItem, { value: "json", label: t('@theorem.data.json') })] }))] }), view === 'json' && structured ? (_jsx(Json, { text: text })) : (_jsx(ShapeBoundary, { fallback: _jsx(Json, { text: text }), onFallback: () => setView('json'), children: _jsxs(VStack, { gap: 3, children: [lead, rest === undefined ? null : _jsx(Node, { value: rest, depth: 0 })] }) }))] }));
|
|
151
196
|
}
|
package/ui/SidePanel.d.ts
CHANGED
|
@@ -10,8 +10,9 @@ export declare function useSidePanel(containerRef: RefObject<HTMLDivElement | nu
|
|
|
10
10
|
resizable: ResizableRegion;
|
|
11
11
|
};
|
|
12
12
|
/** The Layout header row: side panel toggles, at the end. */
|
|
13
|
-
export declare function SidePanelHeader({ children }: {
|
|
13
|
+
export declare function SidePanelHeader({ children, floating, }: {
|
|
14
14
|
children?: ReactNode;
|
|
15
|
+
floating?: boolean;
|
|
15
16
|
}): import("react").JSX.Element;
|
|
16
17
|
/** A side panel's words: its name, and its toggle and drag handle actions. */
|
|
17
18
|
export type SidePanelLabels = {
|
|
@@ -41,17 +42,85 @@ export type SidePanelProps = {
|
|
|
41
42
|
open?: boolean;
|
|
42
43
|
/** Card padding; 0 for content that brings its own (e.g. ChatLayout). */
|
|
43
44
|
padding?: 0;
|
|
45
|
+
/**
|
|
46
|
+
* `end` docks on the trailing edge: the handle, then the panel. `start`
|
|
47
|
+
* docks on the leading edge: the panel, then the handle.
|
|
48
|
+
*/
|
|
49
|
+
side?: 'start' | 'end';
|
|
50
|
+
className?: string;
|
|
44
51
|
children?: ReactNode;
|
|
45
52
|
};
|
|
46
53
|
/** Page corners like the host's raised panels, no border or shadow; the card fill, so surface-filled cards read on it. */
|
|
47
54
|
export declare const RAISED: {
|
|
48
55
|
background: string;
|
|
49
56
|
borderRadius: string;
|
|
57
|
+
overflow: string;
|
|
50
58
|
};
|
|
59
|
+
/** A docked panel's inset: 12px off the outer edge, 16px off the bottom (the page inset). */
|
|
60
|
+
export declare const PANEL_EDGE = 3;
|
|
61
|
+
export declare const PANEL_BOTTOM = 4;
|
|
62
|
+
/** The raised card every docked panel and the content beside one share. */
|
|
63
|
+
export declare function PanelBody({ padding, inset, className, children, }: {
|
|
64
|
+
padding?: 0;
|
|
65
|
+
/**
|
|
66
|
+
* `end` insets the trailing edge (a panel docked there, or the content
|
|
67
|
+
* filling beside a start panel). `start` insets the leading edge. `beside`
|
|
68
|
+
* insets both.
|
|
69
|
+
*/
|
|
70
|
+
inset?: 'end' | 'start' | 'beside';
|
|
71
|
+
className?: string;
|
|
72
|
+
children?: ReactNode;
|
|
73
|
+
}): import("react").JSX.Element;
|
|
74
|
+
/** The scroll inside a panel card. The same area the trace uses, so a short body sizes to its rows. */
|
|
75
|
+
export declare function PanelScroll({ label, padding, children, }: {
|
|
76
|
+
label: string;
|
|
77
|
+
padding?: 4;
|
|
78
|
+
children?: ReactNode;
|
|
79
|
+
}): import("react").JSX.Element;
|
|
51
80
|
/**
|
|
52
|
-
* A Layout
|
|
53
|
-
*
|
|
54
|
-
*
|
|
81
|
+
* A Layout side panel as Astryx's IDE template builds one (a `ResizeHandle`
|
|
82
|
+
* beside a `LayoutPanel` sized by `useResizable`), holding a raised section
|
|
83
|
+
* inset from the layout's outer edge and bottom instead of a flat pane.
|
|
84
|
+
* `end` puts the handle first and reverses it; `start` puts the panel first.
|
|
55
85
|
* For more than one panel, nest Layouts, one panel each, as the template does.
|
|
56
86
|
*/
|
|
57
|
-
export declare function SidePanel({ id, labels, resizable, open, padding, children }: SidePanelProps): import("react").JSX.Element;
|
|
87
|
+
export declare function SidePanel({ id, labels, resizable, open, padding, side, className, children, }: SidePanelProps): import("react").JSX.Element;
|
|
88
|
+
/** The element's width, kept current. */
|
|
89
|
+
export declare function useWidth(): {
|
|
90
|
+
ref: (node: HTMLDivElement | null) => (() => void) | undefined;
|
|
91
|
+
width: number;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* A raised pane that fills its place, inset from the sides. Its content is
|
|
95
|
+
* told whether the pane is wide enough for two readable columns.
|
|
96
|
+
*/
|
|
97
|
+
export declare function RaisedPane({ id, header, children, }: {
|
|
98
|
+
id?: string;
|
|
99
|
+
header?: ReactNode;
|
|
100
|
+
children: (isWide: boolean) => ReactNode;
|
|
101
|
+
}): import("react").JSX.Element;
|
|
102
|
+
/** A raised pane's content. Narrow, the detail takes the overview's place; wide, it sits beside it. */
|
|
103
|
+
export declare function PaneLayout({ isWide, label, detailLabel, lead, overview, detail, }: {
|
|
104
|
+
isWide: boolean;
|
|
105
|
+
label: string;
|
|
106
|
+
detailLabel: string;
|
|
107
|
+
/** Rendered once, ahead of the content. */
|
|
108
|
+
lead?: ReactNode;
|
|
109
|
+
overview: ReactNode;
|
|
110
|
+
detail: ReactNode;
|
|
111
|
+
}): import("react").JSX.Element;
|
|
112
|
+
/** One titled block of a pane's detail; `doc` says what the title means, on hover. */
|
|
113
|
+
export declare function PanePanel({ title, doc, children, }: {
|
|
114
|
+
title: string;
|
|
115
|
+
doc?: string;
|
|
116
|
+
children: ReactNode;
|
|
117
|
+
}): import("react").JSX.Element;
|
|
118
|
+
/** Text as it was written: its line breaks kept, long words wrapped. */
|
|
119
|
+
export declare function Prose({ text }: {
|
|
120
|
+
text: string;
|
|
121
|
+
}): import("react").JSX.Element;
|
|
122
|
+
/** The surface, or the view in its place; the surface stays mounted underneath, so nothing it holds is lost. */
|
|
123
|
+
export declare function InPlace({ view, children }: {
|
|
124
|
+
view: ReactNode;
|
|
125
|
+
children: ReactNode;
|
|
126
|
+
}): import("react").JSX.Element;
|
package/ui/SidePanel.js
CHANGED
|
@@ -2,8 +2,12 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import { Card } from '@astryxdesign/core/Card';
|
|
3
3
|
import { HStack } from '@astryxdesign/core/HStack';
|
|
4
4
|
import { IconButton } from '@astryxdesign/core/IconButton';
|
|
5
|
-
import { LayoutHeader, LayoutPanel } from '@astryxdesign/core/Layout';
|
|
5
|
+
import { Layout, LayoutContent, LayoutHeader, LayoutPanel } from '@astryxdesign/core/Layout';
|
|
6
6
|
import { ResizeHandle, useResizable, } from '@astryxdesign/core/Resizable';
|
|
7
|
+
import { ScrollableArea } from '@astryxdesign/core/ScrollableArea';
|
|
8
|
+
import { StackItem } from '@astryxdesign/core/Stack';
|
|
9
|
+
import { Text } from '@astryxdesign/core/Text';
|
|
10
|
+
import { Tooltip as HoverTip } from '@astryxdesign/core/Tooltip';
|
|
7
11
|
import { VStack } from '@astryxdesign/core/VStack';
|
|
8
12
|
import { useCallback, useId, useState } from 'react';
|
|
9
13
|
/**
|
|
@@ -29,22 +33,91 @@ export function useSidePanel(containerRef, initiallyOpen) {
|
|
|
29
33
|
return { id, open, toggle, resizable };
|
|
30
34
|
}
|
|
31
35
|
/** The Layout header row: side panel toggles, at the end. */
|
|
32
|
-
export function SidePanelHeader({ children }) {
|
|
36
|
+
export function SidePanelHeader({ children, floating = false, }) {
|
|
37
|
+
if (floating)
|
|
38
|
+
return (_jsx("div", { style: {
|
|
39
|
+
position: 'absolute',
|
|
40
|
+
insetBlockStart: 'var(--spacing-4)',
|
|
41
|
+
insetInlineEnd: 'var(--spacing-4)',
|
|
42
|
+
zIndex: 3,
|
|
43
|
+
}, children: _jsx(HStack, { hAlign: "end", gap: 1, children: children }) }));
|
|
33
44
|
return (_jsx(LayoutHeader, { hasDivider: false, children: _jsx(HStack, { hAlign: "end", gap: 1, children: children }) }));
|
|
34
45
|
}
|
|
35
46
|
/** Shows or hides one side panel (`panelId` from useSidePanel). */
|
|
36
47
|
export function SidePanelToggle({ labels, icon, panelId, open, onToggle, }) {
|
|
37
48
|
const action = open ? labels.hide : labels.show;
|
|
38
|
-
return (_jsx(IconButton, { label: action, tooltip: action, variant:
|
|
49
|
+
return (_jsx(IconButton, { label: action, tooltip: action, variant: open ? 'secondary' : 'ghost', "aria-expanded": open, "aria-controls": panelId, icon: icon, onClick: onToggle }));
|
|
39
50
|
}
|
|
40
51
|
/** Page corners like the host's raised panels, no border or shadow; the card fill, so surface-filled cards read on it. */
|
|
41
|
-
export const RAISED = {
|
|
52
|
+
export const RAISED = {
|
|
53
|
+
background: 'var(--theorem-pane-background, var(--color-background-card))',
|
|
54
|
+
borderRadius: 'var(--radius-page)',
|
|
55
|
+
overflow: 'hidden',
|
|
56
|
+
};
|
|
57
|
+
/** A docked panel's inset: 12px off the outer edge, 16px off the bottom (the page inset). */
|
|
58
|
+
export const PANEL_EDGE = 3;
|
|
59
|
+
export const PANEL_BOTTOM = 4;
|
|
60
|
+
/** The raised card every docked panel and the content beside one share. */
|
|
61
|
+
export function PanelBody({ padding, inset = 'end', className, children, }) {
|
|
62
|
+
return (_jsx(VStack, { height: "100%", className: className, paddingInlineStart: inset === 'end' ? undefined : PANEL_EDGE, paddingInlineEnd: inset === 'start' ? undefined : PANEL_EDGE, paddingBlockEnd: PANEL_BOTTOM, children: _jsx(Card, { variant: "transparent", height: "100%", padding: padding, style: RAISED, children: children }) }));
|
|
63
|
+
}
|
|
64
|
+
/** The scroll inside a panel card. The same area the trace uses, so a short body sizes to its rows. */
|
|
65
|
+
export function PanelScroll({ label, padding, children, }) {
|
|
66
|
+
return (_jsx(ScrollableArea, { className: "theorem-panel-scroll", label: label, height: "100%", padding: padding, children: children }));
|
|
67
|
+
}
|
|
42
68
|
/**
|
|
43
|
-
* A Layout
|
|
44
|
-
*
|
|
45
|
-
*
|
|
69
|
+
* A Layout side panel as Astryx's IDE template builds one (a `ResizeHandle`
|
|
70
|
+
* beside a `LayoutPanel` sized by `useResizable`), holding a raised section
|
|
71
|
+
* inset from the layout's outer edge and bottom instead of a flat pane.
|
|
72
|
+
* `end` puts the handle first and reverses it; `start` puts the panel first.
|
|
46
73
|
* For more than one panel, nest Layouts, one panel each, as the template does.
|
|
47
74
|
*/
|
|
48
|
-
export function SidePanel({ id, labels, resizable, open = true, padding, children }) {
|
|
49
|
-
|
|
75
|
+
export function SidePanel({ id, labels, resizable, open = true, padding, side = 'end', className, children, }) {
|
|
76
|
+
const handle = open ? (_jsx(ResizeHandle, { direction: "horizontal", isReversed: side === 'end', hasDivider: false, isAlwaysVisible: false, resizable: resizable.props, label: labels.resize })) : null;
|
|
77
|
+
const panel = (_jsx(LayoutPanel, { id: id, className: className, label: labels.name, role: "complementary", width: open ? resizable.size : 0, hasDivider: false, padding: 0, isScrollable: false, inert: !open, children: _jsx(PanelBody, { padding: padding, inset: side, children: open ? children : null }) }));
|
|
78
|
+
return side === 'start' ? (_jsxs(_Fragment, { children: [panel, handle] })) : (_jsxs(_Fragment, { children: [handle, panel] }));
|
|
79
|
+
}
|
|
80
|
+
/** The element's width, kept current. */
|
|
81
|
+
export function useWidth() {
|
|
82
|
+
const [width, setWidth] = useState(0);
|
|
83
|
+
const ref = useCallback((node) => {
|
|
84
|
+
if (!node)
|
|
85
|
+
return;
|
|
86
|
+
const observer = new ResizeObserver(([entry]) => setWidth(entry?.contentRect.width ?? 0));
|
|
87
|
+
observer.observe(node);
|
|
88
|
+
return () => observer.disconnect();
|
|
89
|
+
}, []);
|
|
90
|
+
return { ref, width };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* A raised pane that fills its place, inset from the sides. Its content is
|
|
94
|
+
* told whether the pane is wide enough for two readable columns.
|
|
95
|
+
*/
|
|
96
|
+
export function RaisedPane({ id, header, children, }) {
|
|
97
|
+
const { ref, width } = useWidth();
|
|
98
|
+
return (_jsx(Layout, { height: "fill", style: { position: 'relative' }, header: header, content: _jsx(LayoutContent, { padding: 0, isScrollable: false, children: _jsx(VStack, { height: "100%", padding: 3, children: _jsx(Card, { variant: "transparent", height: "100%", padding: 0, style: RAISED, children: _jsx("div", { id: id, ref: ref, style: { height: '100%' }, children: children(isSidePanelWide(width)) }) }) }) }) }));
|
|
99
|
+
}
|
|
100
|
+
/** A raised pane's content. Narrow, the detail takes the overview's place; wide, it sits beside it. */
|
|
101
|
+
export function PaneLayout({ isWide, label, detailLabel, lead, overview, detail, }) {
|
|
102
|
+
if (!isWide) {
|
|
103
|
+
return (_jsxs(PanelScroll, { label: label, children: [lead, detail || overview] }));
|
|
104
|
+
}
|
|
105
|
+
return (_jsxs(HStack, { height: "100%", gap: 0, children: [_jsx(StackItem, { size: "fill", children: _jsxs(PanelScroll, { label: label, children: [lead, overview] }) }), detail ? (_jsx("div", { style: {
|
|
106
|
+
width: '48%',
|
|
107
|
+
flexShrink: 0,
|
|
108
|
+
height: '100%',
|
|
109
|
+
borderInlineStart: '1px solid var(--color-border)',
|
|
110
|
+
}, children: _jsx(PanelScroll, { label: detailLabel, children: detail }) })) : null] }));
|
|
111
|
+
}
|
|
112
|
+
/** One titled block of a pane's detail; `doc` says what the title means, on hover. */
|
|
113
|
+
export function PanePanel({ title, doc, children, }) {
|
|
114
|
+
return (_jsx(Card, { padding: 3, variant: "muted", style: { background: 'var(--color-background-surface)' }, children: _jsxs(VStack, { gap: 2, children: [_jsx(HoverTip, { content: doc ?? title, children: _jsx(Text, { type: "supporting", color: "secondary", children: title }) }), children] }) }));
|
|
115
|
+
}
|
|
116
|
+
/** Text as it was written: its line breaks kept, long words wrapped. */
|
|
117
|
+
export function Prose({ text }) {
|
|
118
|
+
return _jsx("span", { style: { whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }, children: text });
|
|
119
|
+
}
|
|
120
|
+
/** The surface, or the view in its place; the surface stays mounted underneath, so nothing it holds is lost. */
|
|
121
|
+
export function InPlace({ view, children }) {
|
|
122
|
+
return (_jsxs(_Fragment, { children: [view, _jsx("div", { style: { display: view ? 'none' : 'contents' }, children: children })] }));
|
|
50
123
|
}
|
package/ui/StateFields.d.ts
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import type { DecisionJson } from '@theoremjs/agents';
|
|
2
|
+
import { type DecisionImages } from '../client/decision-images.ts';
|
|
2
3
|
export type Edit<T = DecisionJson> = {
|
|
3
4
|
value: T;
|
|
4
5
|
onChange: (next: T) => void;
|
|
5
6
|
};
|
|
7
|
+
/** A text area's height: one row per line of its text, within `min` and `max`. Wrapped lines do not count. */
|
|
8
|
+
export declare function textRows(text: string, min: number, max: number): number;
|
|
9
|
+
/** The images the state names, and how to change them; a field whose whole text names one shows it. */
|
|
10
|
+
export declare const StateImages: import("react").Context<{
|
|
11
|
+
images: DecisionImages;
|
|
12
|
+
onImages: (next: DecisionImages) => void;
|
|
13
|
+
}>;
|
|
6
14
|
export type GroupLabel = {
|
|
7
15
|
label: string;
|
|
8
16
|
isRequired?: boolean;
|
|
9
|
-
description?:
|
|
17
|
+
description?: React.ReactNode;
|
|
10
18
|
};
|
|
11
19
|
/** Several fields under one name, in the same label a single field has. */
|
|
12
|
-
export declare function Labelled({ label, isRequired, description, children }: GroupLabel & {
|
|
20
|
+
export declare function Labelled({ label, isRequired, description, children, }: GroupLabel & {
|
|
13
21
|
children: React.ReactNode;
|
|
14
22
|
}): import("react").JSX.Element;
|
|
15
23
|
/** Nested fields sit under their name, set in by a rule. */
|