@colixsystems/widget-sdk 0.275.2 → 0.277.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/README.md +13 -3
- package/dist/bundled-assets.cjs +6 -2
- package/dist/bundled-assets.js +0 -1
- package/dist/contract.cjs +13 -9
- package/dist/contract.js +10 -7
- package/dist/define-widget.js +8 -1
- package/dist/index.d.ts +15 -16
- package/dist/linter.cjs +279 -210
- package/dist/lucideIconName.cjs +14 -5
- package/dist/manifest.cjs +7 -15
- package/dist/manifest.js +10 -69
- package/dist/markdown-doc.js +209 -0
- package/dist/markdown-editor-bridge.js +162 -0
- package/dist/markdown-editor-core.js +184 -0
- package/dist/markdown-editor-css.js +50 -0
- package/dist/markdown-editor-field.js +111 -0
- package/dist/markdown-editor-field.native.js +9 -0
- package/dist/markdown-editor-html.js +4 -0
- package/dist/markdown-editor-webview.js +70 -0
- package/dist/markdown-input-view.js +43 -101
- package/dist/markdown-input.js +4 -6
- package/dist/markdown-input.native.js +3 -5
- package/dist/markdown.js +1 -112
- package/dist/navigation.cjs +31 -11
- package/dist/richtext-tokens.js +1 -25
- package/dist/source-mask.cjs +169 -0
- package/dist/theme-appearance.cjs +7 -5
- package/dist/theme-appearance.js +1 -1
- package/dist/theme-background.cjs +3 -8
- package/dist/theme-components.cjs +13 -10
- package/package.json +63 -3
- package/src/contract.js +10 -7
- package/dist/markdown-edit.js +0 -164
- package/dist/markdown-live-field.js +0 -312
- package/dist/markdown-live-field.native.js +0 -36
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// sc-15080 — the page `<MarkdownInput>` runs its editor in on iOS and Android.
|
|
2
|
+
//
|
|
3
|
+
// React Native has no editable rich-text surface, so the native field hosts
|
|
4
|
+
// the SAME editor module the web field mounts, inside a WebView. This entry is
|
|
5
|
+
// bundled into ./markdown-editor-html.js by scripts/build-markdown-editor.mjs;
|
|
6
|
+
// nothing imports it at runtime.
|
|
7
|
+
//
|
|
8
|
+
// Messages are JSON strings. In: init, style, set, command, focus. Out:
|
|
9
|
+
// ready, change, height, focus, selection, state.
|
|
10
|
+
|
|
11
|
+
import { createMarkdownEditor } from "./markdown-editor-core.js";
|
|
12
|
+
import { markdownEditorCss } from "./markdown-editor-css.js";
|
|
13
|
+
|
|
14
|
+
const ROOT = "#root";
|
|
15
|
+
|
|
16
|
+
function post(message) {
|
|
17
|
+
if (window.ReactNativeWebView) window.ReactNativeWebView.postMessage(JSON.stringify(message));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let instance = null;
|
|
21
|
+
const style = document.createElement("style");
|
|
22
|
+
document.head.appendChild(style);
|
|
23
|
+
|
|
24
|
+
function paint({ tokens, minHeight }) {
|
|
25
|
+
if (tokens && typeof tokens === "object") style.textContent = markdownEditorCss(tokens, ROOT, { minHeight });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function init(message) {
|
|
29
|
+
if (instance) return;
|
|
30
|
+
const { markdown, placeholder, label } = message;
|
|
31
|
+
paint(message);
|
|
32
|
+
|
|
33
|
+
const root = document.querySelector(ROOT);
|
|
34
|
+
instance = createMarkdownEditor({
|
|
35
|
+
element: root,
|
|
36
|
+
markdown,
|
|
37
|
+
placeholder,
|
|
38
|
+
label,
|
|
39
|
+
onChange: (next) => post({ type: "change", markdown: next }),
|
|
40
|
+
onFocusChange: (focused) => post({ type: "focus", focused }),
|
|
41
|
+
onState: (state) => post({ type: "state", inTable: state.inTable === true }),
|
|
42
|
+
});
|
|
43
|
+
instance.editor.on("selectionUpdate", () => post({ type: "selection", text: instance.selectedText() }));
|
|
44
|
+
|
|
45
|
+
let lastHeight = 0;
|
|
46
|
+
new ResizeObserver(() => {
|
|
47
|
+
const height = Math.ceil(root.getBoundingClientRect().height);
|
|
48
|
+
if (height !== lastHeight) post({ type: "height", height: (lastHeight = height) });
|
|
49
|
+
}).observe(root);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
window.mdEditor = {
|
|
53
|
+
receive(raw) {
|
|
54
|
+
let message;
|
|
55
|
+
try {
|
|
56
|
+
message = JSON.parse(raw);
|
|
57
|
+
} catch {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (!message || typeof message !== "object") return;
|
|
61
|
+
if (message.type === "init") init(message);
|
|
62
|
+
else if (message.type === "style") paint(message);
|
|
63
|
+
else if (!instance) return;
|
|
64
|
+
else if (message.type === "set") instance.setMarkdown(message.markdown);
|
|
65
|
+
else if (message.type === "command") instance.command(message.name, message.args);
|
|
66
|
+
else if (message.type === "focus") instance.focus();
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
post({ type: "ready" });
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// sc-6970 — the ONE implementation behind the `<MarkdownInput>` SDK primitive.
|
|
2
2
|
//
|
|
3
|
-
// The authoring half of `<RichText
|
|
4
|
-
// author's SELECTION in markdown markers. Widgets previously had only a bare
|
|
3
|
+
// The authoring half of `<RichText>`. Widgets previously had only a bare
|
|
5
4
|
// `TextInput`, so a widget asked for a formatting toolbar had to splice literal
|
|
6
5
|
// `<strong>`/`<br>` tags into the text — markup the author then read as tag soup.
|
|
7
6
|
//
|
|
8
|
-
// sc-
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
7
|
+
// sc-15080: the author edits the formatted result directly — headings, lists,
|
|
8
|
+
// links and real tables — while the stored value stays markdown. Each host
|
|
9
|
+
// binding supplies its own `EditorField` around the one shared editor (mounted
|
|
10
|
+
// in the page on web, in a WebView natively), so `showPreview` has nothing
|
|
11
|
+
// left to show and is accepted as a no-op.
|
|
12
12
|
//
|
|
13
13
|
// The two platform bindings differ ONLY in where the primitives come from; the
|
|
14
14
|
// component is defined once here so the hosts cannot drift (CLAUDE.md §3, §8).
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
import React from "react";
|
|
17
17
|
import { useHostTheme } from "./hooks.js";
|
|
18
18
|
import { resolveRichTextTokens } from "./richtext-tokens.js";
|
|
19
|
-
import { markdownEditOps } from "./markdown-edit.js";
|
|
20
19
|
import { makeMarkdownLinkDialog } from "./markdown-link-dialog.js";
|
|
21
20
|
|
|
22
21
|
const TOOLS = Object.freeze([
|
|
@@ -28,8 +27,7 @@ const TOOLS = Object.freeze([
|
|
|
28
27
|
{ key: "bullet", label: "•", accessibilityLabel: "Bulleted list" },
|
|
29
28
|
{ key: "ordered", label: "1.", accessibilityLabel: "Numbered list" },
|
|
30
29
|
{ key: "table", label: "Table", accessibilityLabel: "Insert table" },
|
|
31
|
-
// sc-7349:
|
|
32
|
-
// so this button opens the form instead of editing the string directly.
|
|
30
|
+
// sc-7349: a link needs a target no selection supplies, so this opens a form.
|
|
33
31
|
{
|
|
34
32
|
key: "link",
|
|
35
33
|
label: "Link",
|
|
@@ -38,17 +36,26 @@ const TOOLS = Object.freeze([
|
|
|
38
36
|
},
|
|
39
37
|
]);
|
|
40
38
|
|
|
41
|
-
|
|
39
|
+
// Shown while the caret is in a table: a phone keyboard has no Tab key to
|
|
40
|
+
// grow one, and a table must be removable without selecting around it.
|
|
41
|
+
const TABLE_TOOLS = Object.freeze([
|
|
42
|
+
{ key: "addRow", label: "+ Row", accessibilityLabel: "Add row" },
|
|
43
|
+
{ key: "addColumn", label: "+ Column", accessibilityLabel: "Add column" },
|
|
44
|
+
{ key: "removeRow", label: "− Row", accessibilityLabel: "Remove row" },
|
|
45
|
+
{ key: "removeColumn", label: "− Column", accessibilityLabel: "Remove column" },
|
|
46
|
+
{ key: "removeTable", label: "Delete table", accessibilityLabel: "Delete table" },
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
export function makeMarkdownInput(rn, Modal, Button, EditorField) {
|
|
42
50
|
const { Text, View, Pressable } = rn;
|
|
43
51
|
const MarkdownLinkDialog = makeMarkdownLinkDialog(rn, Modal, Button);
|
|
44
52
|
|
|
53
|
+
// `previewLabel`, `showPreview` and `renderImage` are accepted for widgets
|
|
54
|
+
// written before the field showed the formatted result itself.
|
|
45
55
|
function MarkdownInput({
|
|
46
56
|
value,
|
|
47
57
|
onChange,
|
|
48
58
|
placeholder,
|
|
49
|
-
previewLabel = "Preview",
|
|
50
|
-
showPreview = false,
|
|
51
|
-
renderImage,
|
|
52
59
|
pages,
|
|
53
60
|
minHeight = 150,
|
|
54
61
|
maxHeight = 280,
|
|
@@ -58,72 +65,34 @@ export function makeMarkdownInput(rn, RichText, Modal, Button, LiveField) {
|
|
|
58
65
|
}) {
|
|
59
66
|
const theme = useHostTheme();
|
|
60
67
|
const tokens = React.useMemo(() => resolveRichTextTokens(theme), [theme]);
|
|
61
|
-
const [selection, setSelection] = React.useState({ start: 0, end: 0 });
|
|
62
68
|
const fieldRef = React.useRef(null);
|
|
63
|
-
const [
|
|
64
|
-
|
|
65
|
-
const
|
|
69
|
+
const [linkLabel, setLinkLabel] = React.useState(null);
|
|
70
|
+
const [inTable, setInTable] = React.useState(false);
|
|
71
|
+
const onStateChange = React.useCallback((state) => setInTable(state.inTable), []);
|
|
66
72
|
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
73
|
+
const run = React.useCallback((name, args) => {
|
|
74
|
+
const field = fieldRef.current;
|
|
75
|
+
if (field) field.command(name, args);
|
|
70
76
|
}, []);
|
|
71
77
|
|
|
72
|
-
const applyTool = React.useCallback(
|
|
73
|
-
(toolKey, options) => {
|
|
74
|
-
if (typeof onChange !== "function") return;
|
|
75
|
-
const edit = markdownEditOps[toolKey];
|
|
76
|
-
if (!edit) return;
|
|
77
|
-
const next = edit(text, selection, options);
|
|
78
|
-
const field = fieldRef.current;
|
|
79
|
-
if (field && typeof field.recordEdit === "function") field.recordEdit();
|
|
80
|
-
onChange(next.value);
|
|
81
|
-
setSelection(next.selection);
|
|
82
|
-
},
|
|
83
|
-
[onChange, text, selection],
|
|
84
|
-
);
|
|
85
|
-
|
|
86
78
|
const pressTool = React.useCallback(
|
|
87
79
|
(tool) => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
applyTool(tool.key);
|
|
80
|
+
// The selection seeds the form's Link text, so a highlighted phrase
|
|
81
|
+
// stays the label.
|
|
82
|
+
if (tool.opensDialog) setLinkLabel(fieldRef.current ? fieldRef.current.selectedText() : "");
|
|
83
|
+
else run(tool.key);
|
|
93
84
|
},
|
|
94
|
-
[
|
|
85
|
+
[run],
|
|
95
86
|
);
|
|
96
87
|
|
|
97
88
|
const insertLink = React.useCallback(
|
|
98
89
|
(result) => {
|
|
99
|
-
|
|
100
|
-
|
|
90
|
+
setLinkLabel(null);
|
|
91
|
+
run("link", result);
|
|
101
92
|
},
|
|
102
|
-
[
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
// The selection seeds the dialog's Link text, so highlighting a phrase and
|
|
106
|
-
// pressing Link keeps that phrase as the label.
|
|
107
|
-
const selected = text.slice(
|
|
108
|
-
Math.min(selection.start, selection.end),
|
|
109
|
-
Math.max(selection.start, selection.end),
|
|
93
|
+
[run],
|
|
110
94
|
);
|
|
111
95
|
|
|
112
|
-
const fieldStyle = {
|
|
113
|
-
fontFamily: tokens.bodyFont,
|
|
114
|
-
fontSize: tokens.bodySize,
|
|
115
|
-
lineHeight: tokens.lineHeight,
|
|
116
|
-
color: tokens.color,
|
|
117
|
-
backgroundColor: tokens.surface,
|
|
118
|
-
borderWidth: 1,
|
|
119
|
-
borderColor: tokens.border,
|
|
120
|
-
borderRadius: tokens.radius,
|
|
121
|
-
padding: tokens.padding,
|
|
122
|
-
minHeight,
|
|
123
|
-
maxHeight,
|
|
124
|
-
textAlignVertical: "top",
|
|
125
|
-
};
|
|
126
|
-
|
|
127
96
|
return React.createElement(
|
|
128
97
|
View,
|
|
129
98
|
{ style, testID },
|
|
@@ -137,7 +106,7 @@ export function makeMarkdownInput(rn, RichText, Modal, Button, LiveField) {
|
|
|
137
106
|
},
|
|
138
107
|
testID: testID ? `${testID}-toolbar` : undefined,
|
|
139
108
|
},
|
|
140
|
-
TOOLS.map((tool) =>
|
|
109
|
+
(inTable ? [...TOOLS, ...TABLE_TOOLS] : TOOLS).map((tool) =>
|
|
141
110
|
React.createElement(
|
|
142
111
|
Pressable,
|
|
143
112
|
{
|
|
@@ -174,51 +143,24 @@ export function makeMarkdownInput(rn, RichText, Modal, Button, LiveField) {
|
|
|
174
143
|
),
|
|
175
144
|
),
|
|
176
145
|
),
|
|
177
|
-
React.createElement(
|
|
146
|
+
React.createElement(EditorField, {
|
|
178
147
|
ref: fieldRef,
|
|
179
|
-
value:
|
|
148
|
+
value: typeof value === "string" ? value : "",
|
|
180
149
|
onChangeText: onChange,
|
|
181
|
-
|
|
182
|
-
selection,
|
|
150
|
+
onStateChange,
|
|
183
151
|
placeholder,
|
|
184
152
|
accessibilityLabel,
|
|
185
153
|
tokens,
|
|
186
|
-
|
|
154
|
+
minHeight,
|
|
155
|
+
maxHeight,
|
|
187
156
|
testID: testID ? `${testID}-field` : undefined,
|
|
188
157
|
}),
|
|
189
|
-
showPreview && text.trim()
|
|
190
|
-
? React.createElement(
|
|
191
|
-
View,
|
|
192
|
-
{ style: { marginTop: tokens.padding } },
|
|
193
|
-
React.createElement(
|
|
194
|
-
Text,
|
|
195
|
-
{
|
|
196
|
-
style: {
|
|
197
|
-
fontFamily: tokens.bodyFont,
|
|
198
|
-
fontSize: tokens.codeSize,
|
|
199
|
-
color: tokens.mutedColor,
|
|
200
|
-
marginBottom: tokens.blockGap,
|
|
201
|
-
},
|
|
202
|
-
},
|
|
203
|
-
previewLabel,
|
|
204
|
-
),
|
|
205
|
-
React.createElement(RichText, {
|
|
206
|
-
value: text,
|
|
207
|
-
renderImage,
|
|
208
|
-
// sc-7349: the preview shows the reader's document, but a tap
|
|
209
|
-
// must NOT leave the page — that would throw away the draft the
|
|
210
|
-
// author is still writing.
|
|
211
|
-
followLinks: false,
|
|
212
|
-
testID: testID ? `${testID}-preview` : undefined,
|
|
213
|
-
}),
|
|
214
|
-
)
|
|
215
|
-
: null,
|
|
216
158
|
React.createElement(MarkdownLinkDialog, {
|
|
217
|
-
visible:
|
|
218
|
-
onClose: () =>
|
|
159
|
+
visible: linkLabel !== null,
|
|
160
|
+
onClose: () => setLinkLabel(null),
|
|
219
161
|
onSubmit: insertLink,
|
|
220
162
|
pages,
|
|
221
|
-
initialLabel:
|
|
163
|
+
initialLabel: linkLabel || "",
|
|
222
164
|
testID,
|
|
223
165
|
}),
|
|
224
166
|
);
|
package/dist/markdown-input.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// sc-6970 — `<MarkdownInput>` (web). Binds the shared implementation in
|
|
2
|
-
// ./markdown-input-view.js to react-native-web's primitives
|
|
3
|
-
//
|
|
2
|
+
// ./markdown-input-view.js to react-native-web's primitives and the web
|
|
3
|
+
// editor field.
|
|
4
4
|
|
|
5
5
|
import { ReactNative } from "./text-scale.js";
|
|
6
6
|
import { makeMarkdownInput } from "./markdown-input-view.js";
|
|
7
|
-
import {
|
|
8
|
-
import { RichText } from "./richtext.js";
|
|
7
|
+
import { MarkdownEditorField } from "./markdown-editor-field.js";
|
|
9
8
|
import { Modal } from "./modal.js";
|
|
10
9
|
import { Button } from "./button.js";
|
|
11
10
|
import { TextInput } from "./primitives.js";
|
|
@@ -13,8 +12,7 @@ import { TextInput } from "./primitives.js";
|
|
|
13
12
|
// TextInput stays for the link form, which is built from the same `rn`.
|
|
14
13
|
export const MarkdownInput = makeMarkdownInput(
|
|
15
14
|
{ ...ReactNative, TextInput },
|
|
16
|
-
RichText,
|
|
17
15
|
Modal,
|
|
18
16
|
Button,
|
|
19
|
-
|
|
17
|
+
MarkdownEditorField,
|
|
20
18
|
);
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
// sc-6970 — `<MarkdownInput>` (native). Binds the shared implementation in
|
|
2
2
|
// ./markdown-input-view.js to React Native's own primitives and the native
|
|
3
|
-
//
|
|
3
|
+
// editor field. Only the imports differ from the web mirror.
|
|
4
4
|
|
|
5
5
|
import { View, Pressable, ScrollView } from "react-native";
|
|
6
6
|
import { Text, TextInput } from "./bounded-text.native.js";
|
|
7
7
|
import { makeMarkdownInput } from "./markdown-input-view.js";
|
|
8
|
-
import {
|
|
9
|
-
import { RichText } from "./richtext.native.js";
|
|
8
|
+
import { MarkdownEditorField } from "./markdown-editor-field.native.js";
|
|
10
9
|
import { Modal } from "./modal.native.js";
|
|
11
10
|
import { Button } from "./button.native.js";
|
|
12
11
|
|
|
@@ -14,8 +13,7 @@ import { Button } from "./button.native.js";
|
|
|
14
13
|
// scroll rather than grow an app's every page past the panel.
|
|
15
14
|
export const MarkdownInput = makeMarkdownInput(
|
|
16
15
|
{ Text, View, Pressable, TextInput, ScrollView },
|
|
17
|
-
RichText,
|
|
18
16
|
Modal,
|
|
19
17
|
Button,
|
|
20
|
-
|
|
18
|
+
MarkdownEditorField,
|
|
21
19
|
);
|
package/dist/markdown.js
CHANGED
|
@@ -196,8 +196,7 @@ const PLAIN_PATTERN =
|
|
|
196
196
|
* Splits one line into positioned tokens: `{ type: "text", start, end }`, or
|
|
197
197
|
* `{ type: "bold"|"italic"|"code", start, end, contentStart, contentEnd }`, or
|
|
198
198
|
* `{ type: "link", start, end, contentStart, contentEnd, href }` whose content
|
|
199
|
-
* is the RAW label.
|
|
200
|
-
* editor cannot style a construct the renderer would not draw.
|
|
199
|
+
* is the RAW label.
|
|
201
200
|
*/
|
|
202
201
|
function scanInline(source) {
|
|
203
202
|
const tokens = [];
|
|
@@ -532,116 +531,6 @@ export function parseMarkdown(text, options) {
|
|
|
532
531
|
return blocks;
|
|
533
532
|
}
|
|
534
533
|
|
|
535
|
-
const LIVE_STYLE_KEYS = ["marker", "bold", "italic", "code", "href", "heading"];
|
|
536
|
-
|
|
537
|
-
function sameLiveStyle(a, b) {
|
|
538
|
-
return LIVE_STYLE_KEYS.every((key) => (a[key] || false) === (b[key] || false));
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
/**
|
|
542
|
-
* sc-14974 — `text` cut into styled runs for the live editor. Joining every
|
|
543
|
-
* run's `text` gives back `text` byte for byte: nothing is trimmed or joined,
|
|
544
|
-
* and markdown markers stay in place flagged `marker` so the editor can dim
|
|
545
|
-
* them. Each run is `{ text, marker?, bold?, italic?, code?, href?, heading? }`
|
|
546
|
-
* with `heading` the level. Lines are classified in `parseMarkdown`'s order.
|
|
547
|
-
*/
|
|
548
|
-
export function markdownLiveSegments(text) {
|
|
549
|
-
const source = typeof text === "string" ? text : "";
|
|
550
|
-
const runs = [];
|
|
551
|
-
const push = (value, style) => {
|
|
552
|
-
if (!value) return;
|
|
553
|
-
const last = runs[runs.length - 1];
|
|
554
|
-
if (last && sameLiveStyle(last, style)) last.text += value;
|
|
555
|
-
else runs.push({ text: value, ...style });
|
|
556
|
-
};
|
|
557
|
-
const inline = (value, base) => {
|
|
558
|
-
for (const token of scanInline(value)) {
|
|
559
|
-
if (token.type === "text") {
|
|
560
|
-
push(value.slice(token.start, token.end), base);
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
const content = value.slice(token.contentStart, token.contentEnd);
|
|
564
|
-
push(value.slice(token.start, token.contentStart), { ...base, marker: true });
|
|
565
|
-
if (token.type === "link") inline(content, { ...base, href: token.href });
|
|
566
|
-
else push(content, { ...base, [token.type]: true });
|
|
567
|
-
push(value.slice(token.contentEnd, token.end), { ...base, marker: true });
|
|
568
|
-
}
|
|
569
|
-
};
|
|
570
|
-
const tableRow = (line, base) => {
|
|
571
|
-
let cellStart = 0;
|
|
572
|
-
for (let k = 0; k < line.length; k += 1) {
|
|
573
|
-
if (line.charAt(k) === "\\" && line.charAt(k + 1) === "|") k += 1;
|
|
574
|
-
else if (line.charAt(k) === "|") {
|
|
575
|
-
inline(line.slice(cellStart, k), base);
|
|
576
|
-
push("|", { marker: true });
|
|
577
|
-
cellStart = k + 1;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
inline(line.slice(cellStart), base);
|
|
581
|
-
};
|
|
582
|
-
|
|
583
|
-
const lines = source.split("\n");
|
|
584
|
-
let inFence = false;
|
|
585
|
-
let tableStart = -1;
|
|
586
|
-
let tableEnd = -1;
|
|
587
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
588
|
-
if (i > 0) push("\n", {});
|
|
589
|
-
const line = lines[i];
|
|
590
|
-
const trimmed = line.trimEnd();
|
|
591
|
-
const tail = line.slice(trimmed.length);
|
|
592
|
-
|
|
593
|
-
if (inFence) {
|
|
594
|
-
if (FENCE_CLOSE_RE.test(line)) inFence = false;
|
|
595
|
-
push(line, inFence ? { code: true } : { marker: true });
|
|
596
|
-
continue;
|
|
597
|
-
}
|
|
598
|
-
if (FENCE_OPEN_RE.test(trimmed)) {
|
|
599
|
-
inFence = true;
|
|
600
|
-
push(line, { marker: true });
|
|
601
|
-
continue;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
if (i > tableEnd && TABLE_ROW_RE.test(line) && TABLE_DIVIDER_RE.test(lines[i + 1] || "")) {
|
|
605
|
-
tableStart = i;
|
|
606
|
-
tableEnd = i + 1;
|
|
607
|
-
while (tableEnd + 1 < lines.length && TABLE_ROW_RE.test(lines[tableEnd + 1])) tableEnd += 1;
|
|
608
|
-
}
|
|
609
|
-
if (i <= tableEnd) {
|
|
610
|
-
if (i === tableStart + 1) push(line, { marker: true });
|
|
611
|
-
else tableRow(line, i === tableStart ? { bold: true } : {});
|
|
612
|
-
continue;
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
const lead = trimmed.length - trimmed.trimStart().length;
|
|
616
|
-
const image = IMAGE_RE.exec(trimmed.trimStart());
|
|
617
|
-
if (image) {
|
|
618
|
-
const altStart = lead + 2;
|
|
619
|
-
const altEnd = altStart + image[1].length;
|
|
620
|
-
push(line.slice(0, altStart), { marker: true });
|
|
621
|
-
push(line.slice(altStart, altEnd), {});
|
|
622
|
-
push(line.slice(altEnd, trimmed.length), { marker: true });
|
|
623
|
-
push(tail, {});
|
|
624
|
-
continue;
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
const heading = HEADING_RE.exec(trimmed);
|
|
628
|
-
const listItem = heading ? null : BULLET_RE.exec(trimmed) || ORDERED_RE.exec(trimmed);
|
|
629
|
-
if (heading || listItem) {
|
|
630
|
-
const content = heading ? heading[2] : listItem[listItem.length - 1];
|
|
631
|
-
const base = heading ? { heading: heading[1].length } : {};
|
|
632
|
-
const contentStart = trimmed.length - content.length;
|
|
633
|
-
push(trimmed.slice(0, contentStart), { ...base, marker: true });
|
|
634
|
-
inline(content, base);
|
|
635
|
-
push(tail, base);
|
|
636
|
-
continue;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
inline(trimmed, {});
|
|
640
|
-
push(tail, {});
|
|
641
|
-
}
|
|
642
|
-
return runs;
|
|
643
|
-
}
|
|
644
|
-
|
|
645
534
|
function spansToText(spans) {
|
|
646
535
|
return spans.map((span) => span.text).join("");
|
|
647
536
|
}
|
package/dist/navigation.cjs
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// The BODY below is copied VERBATIM from navigation.js; only the module syntax
|
|
7
|
-
// differs. navigation-parity.test.js pins both facts — identical bodies AND
|
|
8
|
-
// identical behaviour over a shared case table — so drift fails CI.
|
|
1
|
+
// GENERATED from navigation.js by scripts/generate-cjs.js — DO NOT EDIT.
|
|
2
|
+
// Change navigation.js, then run `npm run generate:cjs` in packages/widget-sdk.
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { CONTRACT, isHexColor, contrastRatio, mixHex, readableTextColor } = require("./contract.cjs");
|
|
9
6
|
|
|
10
7
|
// REQ-NAV-STRUCTURE — the host side of an app's navigation SHAPE and the
|
|
11
8
|
// tokens its footer strip is painted with.
|
|
@@ -25,7 +22,6 @@
|
|
|
25
22
|
// the SAME literal, so what a planner may persist and what a host draws cannot
|
|
26
23
|
// diverge.
|
|
27
24
|
|
|
28
|
-
const { CONTRACT, isHexColor, contrastRatio, mixHex, readableTextColor } = require("./contract.cjs");
|
|
29
25
|
|
|
30
26
|
// Absent or unknown resolves here, so every app authored before menu types
|
|
31
27
|
// existed renders and compiles byte-identically.
|
|
@@ -895,5 +891,29 @@ function borderWidthOr(...values) {
|
|
|
895
891
|
}
|
|
896
892
|
return BORDER_WIDTH_MIN;
|
|
897
893
|
}
|
|
898
|
-
|
|
899
|
-
|
|
894
|
+
module.exports = {
|
|
895
|
+
normaliseNavigation,
|
|
896
|
+
menuItemCap,
|
|
897
|
+
resolveFooterTokens,
|
|
898
|
+
FOOTER_SIZES,
|
|
899
|
+
clampFooterSize,
|
|
900
|
+
FOOTER_HOME_LINE_CLEARANCE_PX,
|
|
901
|
+
resolveSidebarTokens,
|
|
902
|
+
TOP_STRIPE_SLOTS,
|
|
903
|
+
TOP_STRIPE_ALWAYS_SHOWN_SLOTS,
|
|
904
|
+
MIN_TOP_STRIPE_HEIGHT_PX,
|
|
905
|
+
MAX_TOP_STRIPE_HEIGHT_PX,
|
|
906
|
+
MIN_TOP_STRIPE_PADDING_X_PX,
|
|
907
|
+
MAX_TOP_STRIPE_PADDING_X_PX,
|
|
908
|
+
MAX_TOP_STRIPE_OFFSET_Y_PX,
|
|
909
|
+
DEFAULT_TOP_STRIPE_HEIGHT_PX,
|
|
910
|
+
DEFAULT_TOP_STRIPE_PADDING_X_PX,
|
|
911
|
+
topStripeShowKey,
|
|
912
|
+
resolveTopStripeLayout,
|
|
913
|
+
isDarkSurface,
|
|
914
|
+
resolveAppLogo,
|
|
915
|
+
resolveTopBarTokens,
|
|
916
|
+
resolveSidebarUnfold,
|
|
917
|
+
resolveLocalNavModel,
|
|
918
|
+
drawsLocalNavAnywhere,
|
|
919
|
+
};
|
package/dist/richtext-tokens.js
CHANGED
|
@@ -58,7 +58,7 @@ export function resolveRichTextTokens(theme) {
|
|
|
58
58
|
|
|
59
59
|
export { IMAGE_WIDTH as RICH_TEXT_IMAGE_WIDTH };
|
|
60
60
|
|
|
61
|
-
/** The style one inline span is drawn with
|
|
61
|
+
/** The style one inline span is drawn with by `<RichText>`. */
|
|
62
62
|
export function richTextSpanStyle(span, tokens) {
|
|
63
63
|
return {
|
|
64
64
|
...(span.bold ? { fontWeight: "700" } : null),
|
|
@@ -71,27 +71,3 @@ export function richTextSpanStyle(span, tokens) {
|
|
|
71
71
|
...(span.href ? { color: tokens.accent, textDecorationLine: "underline" } : null),
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* sc-14974 — the style one `markdownLiveSegments` run is drawn with in
|
|
77
|
-
* `<MarkdownInput>`: the span's `<RichText>` look, sized as its heading, with
|
|
78
|
-
* markers dimmed so the text reads as the result while staying editable.
|
|
79
|
-
*/
|
|
80
|
-
export function markdownLiveRunStyle(run, tokens) {
|
|
81
|
-
const headingSize = run.heading
|
|
82
|
-
? tokens.headingSizes[run.heading] || tokens.headingSizes[3]
|
|
83
|
-
: null;
|
|
84
|
-
return {
|
|
85
|
-
...(headingSize
|
|
86
|
-
? {
|
|
87
|
-
fontFamily: tokens.headingFont,
|
|
88
|
-
fontWeight: "700",
|
|
89
|
-
fontSize: headingSize,
|
|
90
|
-
// The field's body leading would overlap a larger heading line.
|
|
91
|
-
lineHeight: Math.round(headingSize * 1.3),
|
|
92
|
-
}
|
|
93
|
-
: null),
|
|
94
|
-
...richTextSpanStyle(run, tokens),
|
|
95
|
-
...(run.marker ? { color: tokens.mutedColor, textDecorationLine: "none" } : null),
|
|
96
|
-
};
|
|
97
|
-
}
|