@parallelworks/ui 0.52.0 → 0.54.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 +97 -8
- package/dist/access/AccessDrawer.d.ts +55 -0
- package/dist/access/AccessDrawer.js +445 -0
- package/dist/access/AccessList.d.ts +58 -0
- package/dist/access/AccessList.js +382 -0
- package/dist/access/GrantView.d.ts +35 -0
- package/dist/access/GrantView.js +188 -0
- package/dist/access/GroupPicker.d.ts +35 -0
- package/dist/access/GroupPicker.js +336 -0
- package/dist/access/focus.d.ts +10 -0
- package/dist/access/focus.js +27 -0
- package/dist/access/index.d.ts +3 -0
- package/dist/access/index.js +3 -0
- package/dist/access/model.d.ts +49 -0
- package/dist/access/model.js +129 -0
- package/dist/access/strings.d.ts +122 -0
- package/dist/access/strings.js +151 -0
- package/dist/access/types.d.ts +56 -0
- package/dist/ai/ai.css +5 -3
- package/dist/ai/components/SlashMenu.js +1 -1
- package/dist/ai/components/agent/AgentMessageParts.js +1 -1
- package/dist/ai/strings.js +0 -1
- package/dist/ai/ui/sidebar.js +1 -1
- package/dist/components/Combobox.d.ts +19 -0
- package/dist/components/Combobox.js +101 -50
- package/dist/components/Drawer.d.ts +6 -1
- package/dist/components/Drawer.js +25 -16
- package/dist/components/Field.d.ts +5 -4
- package/dist/components/Field.js +27 -4
- package/dist/components/InfoTip.d.ts +12 -4
- package/dist/components/InfoTip.js +13 -3
- package/dist/components/MarkdownImpl.d.ts +1 -0
- package/dist/components/MarkdownImpl.js +1 -0
- package/dist/components/Menu.js +4 -4
- package/dist/components/MenuEntries.js +1 -1
- package/dist/components/OptionDescription.d.ts +7 -0
- package/dist/components/OptionDescription.js +19 -0
- package/dist/components/Popover.d.ts +4 -1
- package/dist/components/Popover.js +2 -1
- package/dist/components/Provider.d.ts +9 -0
- package/dist/components/Provider.js +4 -0
- package/dist/components/Select.js +13 -11
- package/dist/components/comboboxItems.d.ts +10 -0
- package/dist/components/comboboxItems.js +8 -1
- package/dist/components/floating.d.ts +5 -2
- package/dist/components/floating.js +2 -2
- package/dist/components/focus.d.ts +5 -0
- package/dist/components/focus.js +21 -1
- package/dist/editor/Editor.js +13 -3
- package/dist/editor/Monaco.d.ts +8 -1
- package/dist/editor/Monaco.js +16 -3
- package/dist/editor/editorModel.d.ts +1 -1
- package/dist/editor/editorModel.js +1 -1
- package/dist/editor/index.d.ts +1 -0
- package/dist/editor/index.js +2 -1
- package/dist/editor/languages.d.ts +24 -0
- package/dist/editor/languages.js +31 -0
- package/dist/editor/monacoFeatures.d.ts +7 -0
- package/dist/editor/monacoFeatures.js +19 -0
- package/dist/editor/themes.d.ts +5 -0
- package/dist/editor/themes.js +24 -4
- package/dist/editor/workers.d.ts +3 -3
- package/dist/editor/workers.js +8 -4
- package/dist/editor/yamlLanguage.js +55 -38
- package/dist/file-explorer/FileExplorer.d.ts +18 -4
- package/dist/file-explorer/FileExplorer.js +64 -20
- package/dist/file-explorer/FilePreview/Viewer.js +12 -8
- package/dist/file-explorer/FileRow.d.ts +6 -2
- package/dist/file-explorer/FileRow.js +35 -17
- package/dist/file-explorer/FileRowsWindow.d.ts +2 -1
- package/dist/file-explorer/FileRowsWindow.js +2 -1
- package/dist/file-explorer/FileTable.d.ts +3 -1
- package/dist/file-explorer/FileTable.js +3 -2
- package/dist/file-explorer/TreeView.d.ts +5 -1
- package/dist/file-explorer/TreeView.js +49 -25
- package/dist/file-explorer/helpers.js +3 -3
- package/dist/file-explorer/lib/paging.d.ts +1 -0
- package/dist/file-explorer/lib/paging.js +5 -1
- package/dist/file-explorer/lib/utils.d.ts +3 -0
- package/dist/file-explorer/lib/utils.js +6 -1
- package/dist/form/Wizard/utils.js +2 -1
- package/dist/list/ActionToolbar.js +3 -2
- package/dist/list/ListPage.js +1 -1
- package/dist/list/ListTable.d.ts +5 -0
- package/dist/list/ListTable.js +6 -2
- package/dist/styles/theme.css +82 -62
- package/dist/styles.css +1 -1
- package/dist/theme/derive.d.ts +5 -3
- package/dist/theme/derive.js +61 -3
- package/package.json +9 -2
- package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +0 -6
- package/dist/file-explorer/FilePreview/CodePreviewEditor.js +0 -64
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Dispatch, ReactNode, SetStateAction } from 'react';
|
|
2
|
+
import type { AccessText } from '../access/strings.js';
|
|
2
3
|
import type { WorkflowAction, WorkflowEditing } from '../editing.js';
|
|
3
4
|
import type { WorkflowEngine } from '../engine.js';
|
|
4
5
|
import type { GraphEditorText, InputsEditorText } from '../graph/editorStrings.js';
|
|
@@ -14,6 +15,8 @@ export interface UIStrings {
|
|
|
14
15
|
favoriteHintOff: string;
|
|
15
16
|
};
|
|
16
17
|
common: {
|
|
18
|
+
/** Returns to the view a drawer or panel showed before. */
|
|
19
|
+
back: string;
|
|
17
20
|
cancel: string;
|
|
18
21
|
close: string;
|
|
19
22
|
save: string;
|
|
@@ -110,6 +113,8 @@ export interface UIStrings {
|
|
|
110
113
|
dropdown: {
|
|
111
114
|
select: (type: string) => string;
|
|
112
115
|
noOptionsFound: string;
|
|
116
|
+
/** While a search for options is under way. */
|
|
117
|
+
searching: string;
|
|
113
118
|
showOptions: string;
|
|
114
119
|
/** The row that adds typed text as a new value. */
|
|
115
120
|
add: (text: string) => string;
|
|
@@ -259,6 +264,8 @@ export interface UIStrings {
|
|
|
259
264
|
};
|
|
260
265
|
/** The visual editor's text a host translates; the editor fills in the rest in English. */
|
|
261
266
|
graphEditor: Partial<GraphEditorText>;
|
|
267
|
+
/** The access drawer's text, over its English defaults. */
|
|
268
|
+
access: Partial<AccessText>;
|
|
262
269
|
inputsEditor: Partial<InputsEditorText>;
|
|
263
270
|
fileExplorer: {
|
|
264
271
|
preview: {
|
|
@@ -268,6 +275,8 @@ export interface UIStrings {
|
|
|
268
275
|
select: (name: string) => string;
|
|
269
276
|
viewDetails: string;
|
|
270
277
|
openFolder: string;
|
|
278
|
+
/** A path's page in the app, as in "Copy link". */
|
|
279
|
+
link: string;
|
|
271
280
|
detailsTab: string;
|
|
272
281
|
previewTab: string;
|
|
273
282
|
preview: string;
|
|
@@ -41,6 +41,7 @@ var DEFAULTS = {
|
|
|
41
41
|
favoriteHintOff: "Favorite your item when it's created"
|
|
42
42
|
},
|
|
43
43
|
common: {
|
|
44
|
+
back: "Back",
|
|
44
45
|
cancel: "Cancel",
|
|
45
46
|
close: "Close",
|
|
46
47
|
save: "Save",
|
|
@@ -125,6 +126,7 @@ var DEFAULTS = {
|
|
|
125
126
|
dropdown: {
|
|
126
127
|
select: (type) => `Select ${type}`,
|
|
127
128
|
noOptionsFound: "No options found",
|
|
129
|
+
searching: "Searching…",
|
|
128
130
|
showOptions: "Show options",
|
|
129
131
|
add: (text) => `Add “${text}”`,
|
|
130
132
|
limitReached: (max) => `Up to ${max}`,
|
|
@@ -276,6 +278,7 @@ var DEFAULTS = {
|
|
|
276
278
|
openInNewGraph: "Open in new graph"
|
|
277
279
|
},
|
|
278
280
|
graphEditor: {},
|
|
281
|
+
access: {},
|
|
279
282
|
inputsEditor: {},
|
|
280
283
|
fileExplorer: {
|
|
281
284
|
preview: {
|
|
@@ -285,6 +288,7 @@ var DEFAULTS = {
|
|
|
285
288
|
select: (name) => `Select ${name}`,
|
|
286
289
|
viewDetails: "View details",
|
|
287
290
|
openFolder: "Open folder",
|
|
291
|
+
link: "link",
|
|
288
292
|
detailsTab: "Details",
|
|
289
293
|
previewTab: "Preview",
|
|
290
294
|
preview: "Preview",
|
|
@@ -3,7 +3,8 @@ import { CheckIcon, ChevronDownIcon } from "../icons.js";
|
|
|
3
3
|
import { panelClassName, positionerClassName } from "./floating.js";
|
|
4
4
|
import { Tooltip } from "./Tooltip.js";
|
|
5
5
|
import { fieldBox } from "./fieldBox.js";
|
|
6
|
-
import { pickerItemClassName } from "./comboboxItems.js";
|
|
6
|
+
import { optionNaming, pickerItemClassName } from "./comboboxItems.js";
|
|
7
|
+
import { OptionDescription } from "./OptionDescription.js";
|
|
7
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
9
|
import { useId } from "react";
|
|
9
10
|
import { Select } from "@base-ui/react/select";
|
|
@@ -52,12 +53,11 @@ function Select$1({ items, placeholder, className, "aria-label": ariaLabel, "ari
|
|
|
52
53
|
children: /* @__PURE__ */ jsx(Select.Popup, {
|
|
53
54
|
className: cx("min-w-(--anchor-width) max-w-[min(20rem,var(--available-width))]", panelClassName),
|
|
54
55
|
children: /* @__PURE__ */ jsx(Select.List, {
|
|
55
|
-
className: "max-h-[min(15rem,var(--available-height))] overflow-y-auto
|
|
56
|
+
className: cx("max-h-[min(15rem,var(--available-height))] overflow-y-auto", "p-1"),
|
|
56
57
|
children: items.map((item, index) => /* @__PURE__ */ jsxs(Select.Item, {
|
|
57
58
|
value: item.value,
|
|
58
59
|
disabled: item.disabled,
|
|
59
|
-
|
|
60
|
-
"aria-describedby": item.description && `${uid}${index}description`,
|
|
60
|
+
...optionNaming(`${uid}${index}`, item),
|
|
61
61
|
onFocus: onItemHighlighted && (() => onItemHighlighted(item.value)),
|
|
62
62
|
className: pickerItemClassName,
|
|
63
63
|
children: [
|
|
@@ -67,14 +67,16 @@ function Select$1({ items, placeholder, className, "aria-label": ariaLabel, "ari
|
|
|
67
67
|
}),
|
|
68
68
|
/* @__PURE__ */ jsxs("span", {
|
|
69
69
|
className: "flex min-w-0 flex-col",
|
|
70
|
-
children: [/* @__PURE__ */
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
71
|
+
className: "flex min-w-0 items-center gap-2",
|
|
72
|
+
children: [/* @__PURE__ */ jsx(Select.ItemText, {
|
|
73
|
+
id: `${uid}${index}label`,
|
|
74
|
+
className: "truncate",
|
|
75
|
+
children: item.label
|
|
76
|
+
}), item.badge]
|
|
77
|
+
}), /* @__PURE__ */ jsx(OptionDescription, {
|
|
75
78
|
id: `${uid}${index}description`,
|
|
76
|
-
|
|
77
|
-
children: item.description
|
|
79
|
+
item
|
|
78
80
|
})]
|
|
79
81
|
}),
|
|
80
82
|
/* @__PURE__ */ jsx(Select.ItemIndicator, {
|
|
@@ -7,7 +7,12 @@ export interface ComboboxItem<Value = string> {
|
|
|
7
7
|
value: Value;
|
|
8
8
|
label: string;
|
|
9
9
|
description?: string | undefined;
|
|
10
|
+
/** Read after the label in place of `description`, for what the row shows in more than
|
|
11
|
+
* words, such as a count and a status: "24 members, synced". */
|
|
12
|
+
accessibleDescription?: string | undefined;
|
|
10
13
|
icon?: ReactNode;
|
|
14
|
+
/** Beside the label, such as a tag that marks the option out. */
|
|
15
|
+
badge?: ReactNode;
|
|
11
16
|
disabled?: boolean | undefined;
|
|
12
17
|
}
|
|
13
18
|
export interface ComboboxGroup<Value = string> {
|
|
@@ -23,6 +28,11 @@ export interface ComboboxGroup<Value = string> {
|
|
|
23
28
|
export type ComboboxLeaf<Value = string> = ComboboxItem<Value> | (Value & string);
|
|
24
29
|
/** Items, groups under headings, or both: items between groups list where they sit, without a heading. */
|
|
25
30
|
export type ComboboxItems<Value = string> = readonly (ComboboxLeaf<Value> | ComboboxGroup<Value>)[];
|
|
31
|
+
/** How an option is named and described: by its label, with its description read after it. */
|
|
32
|
+
export declare function optionNaming(id: string, item: Pick<ComboboxItem<unknown>, 'description' | 'accessibleDescription'>): {
|
|
33
|
+
'aria-labelledby': string;
|
|
34
|
+
'aria-describedby': string | undefined;
|
|
35
|
+
};
|
|
26
36
|
export declare function isComboboxGroup<Value>(item: ComboboxLeaf<Value> | ComboboxGroup<Value>): item is ComboboxGroup<Value>;
|
|
27
37
|
/** Every item, out of their groups. */
|
|
28
38
|
export declare function comboboxLeaves<Value>(items: ComboboxItems<Value>): ComboboxLeaf<Value>[];
|
|
@@ -4,6 +4,13 @@ import { panelItemClassName } from "./floating.js";
|
|
|
4
4
|
/** An option's row in a Select's or a Combobox's list, so both lists look alike: the icon, the
|
|
5
5
|
* label over its description, and the check at the end. */
|
|
6
6
|
var pickerItemClassName = cx(panelItemClassName, "data-selected:font-medium");
|
|
7
|
+
/** How an option is named and described: by its label, with its description read after it. */
|
|
8
|
+
function optionNaming(id, item) {
|
|
9
|
+
return {
|
|
10
|
+
"aria-labelledby": `${id}label`,
|
|
11
|
+
"aria-describedby": item.accessibleDescription || item.description ? `${id}description` : void 0
|
|
12
|
+
};
|
|
13
|
+
}
|
|
7
14
|
function isComboboxGroup(item) {
|
|
8
15
|
return typeof item === "object" && "items" in item;
|
|
9
16
|
}
|
|
@@ -12,4 +19,4 @@ function comboboxLeaves(items) {
|
|
|
12
19
|
return items.flatMap((item) => isComboboxGroup(item) ? item.items : [item]);
|
|
13
20
|
}
|
|
14
21
|
//#endregion
|
|
15
|
-
export { comboboxLeaves, isComboboxGroup, pickerItemClassName };
|
|
22
|
+
export { comboboxLeaves, isComboboxGroup, optionNaming, pickerItemClassName };
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
export declare const positionerClassName = "z-(--layer-popover) outline-none data-anchor-hidden:invisible";
|
|
3
3
|
/** A panel of choices: a menu, a select's list, a combobox's list. */
|
|
4
4
|
export declare const panelClassName = "rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-sm text-(--theme-app) shadow-(--theme-shadow-lg) outline-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
|
|
5
|
-
/** A
|
|
6
|
-
|
|
5
|
+
/** A panel's rows inset by the same gutter on every side, so a highlighted first or last row
|
|
6
|
+
* keeps the margin it has at its sides rather than leaving a strip against the border. */
|
|
7
|
+
export declare const panelListClassName = "p-1";
|
|
8
|
+
/** A row in a panel, inside its `panelListClassName` gutter; its text lines up with a field's. */
|
|
9
|
+
export declare const panelItemClassName = "flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-2 py-1.5 text-left outline-none data-highlighted:bg-(--theme-hover) data-highlighted:text-(--theme-app) data-disabled:cursor-not-allowed data-disabled:opacity-50";
|
|
7
10
|
/** A tooltip's or infotip's small inverted surface. */
|
|
8
11
|
export declare const tipClassName = "rounded-md bg-(--theme-app) px-2 py-1 text-xs text-(--theme-app-bg) shadow-(--theme-shadow) whitespace-pre-line break-words data-instant:transition-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
|
|
9
12
|
export declare const tipArrowClassName = "size-2 rotate-45 bg-(--theme-app) data-[side=top]:-bottom-1 data-[side=bottom]:-top-1 data-[side=left]:-right-1 data-[side=right]:-left-1";
|
|
@@ -4,8 +4,8 @@ var positionerClassName = "z-(--layer-popover) outline-none data-anchor-hidden:i
|
|
|
4
4
|
var enterExit = "origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
|
|
5
5
|
/** A panel of choices: a menu, a select's list, a combobox's list. */
|
|
6
6
|
var panelClassName = `rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-sm text-(--theme-app) shadow-(--theme-shadow-lg) outline-none ${enterExit}`;
|
|
7
|
-
/** A row in a panel. */
|
|
8
|
-
var panelItemClassName = "flex w-full cursor-pointer select-none items-center gap-2 px-
|
|
7
|
+
/** A row in a panel, inside its `panelListClassName` gutter; its text lines up with a field's. */
|
|
8
|
+
var panelItemClassName = "flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-2 py-1.5 text-left outline-none data-highlighted:bg-(--theme-hover) data-highlighted:text-(--theme-app) data-disabled:cursor-not-allowed data-disabled:opacity-50";
|
|
9
9
|
/** A tooltip's or infotip's small inverted surface. */
|
|
10
10
|
var tipClassName = `rounded-md bg-(--theme-app) px-2 py-1 text-xs text-(--theme-app-bg) shadow-(--theme-shadow) whitespace-pre-line break-words data-instant:transition-none ${enterExit}`;
|
|
11
11
|
var tipArrowClassName = "size-2 rotate-45 bg-(--theme-app) data-[side=top]:-bottom-1 data-[side=bottom]:-top-1 data-[side=left]:-right-1 data-[side=right]:-left-1";
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
/** A ref that focuses a field once, when it mounts; stable so re-renders never move focus back. */
|
|
2
2
|
export declare function focusOnMount(element: HTMLElement | null): void;
|
|
3
|
+
/** Starts telling keyboard focus from pointer focus, for `focusCameFromKeyboard`. Call it once
|
|
4
|
+
* something needs to know, such as when a component that asks mounts. */
|
|
5
|
+
export declare function trackFocusModality(): void;
|
|
6
|
+
/** Whether the focus that just landed came from the keyboard rather than a press. */
|
|
7
|
+
export declare function focusCameFromKeyboard(): boolean;
|
package/dist/components/focus.js
CHANGED
|
@@ -3,5 +3,25 @@
|
|
|
3
3
|
function focusOnMount(element) {
|
|
4
4
|
element?.focus();
|
|
5
5
|
}
|
|
6
|
+
var fromKeyboard = false;
|
|
7
|
+
var listening = false;
|
|
8
|
+
function onKeyDown(event) {
|
|
9
|
+
if (!event.metaKey && !event.ctrlKey && !event.altKey) fromKeyboard = true;
|
|
10
|
+
}
|
|
11
|
+
function onPointerDown() {
|
|
12
|
+
fromKeyboard = false;
|
|
13
|
+
}
|
|
14
|
+
/** Starts telling keyboard focus from pointer focus, for `focusCameFromKeyboard`. Call it once
|
|
15
|
+
* something needs to know, such as when a component that asks mounts. */
|
|
16
|
+
function trackFocusModality() {
|
|
17
|
+
if (listening || typeof document === "undefined") return;
|
|
18
|
+
listening = true;
|
|
19
|
+
document.addEventListener("keydown", onKeyDown, true);
|
|
20
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
21
|
+
}
|
|
22
|
+
/** Whether the focus that just landed came from the keyboard rather than a press. */
|
|
23
|
+
function focusCameFromKeyboard() {
|
|
24
|
+
return fromKeyboard;
|
|
25
|
+
}
|
|
6
26
|
//#endregion
|
|
7
|
-
export { focusOnMount };
|
|
27
|
+
export { focusCameFromKeyboard, focusOnMount, trackFocusModality };
|
package/dist/editor/Editor.js
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { Loader } from "../components/Loader.js";
|
|
2
|
+
import { editorLanguagesLoading } from "./languages.js";
|
|
2
3
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
-
import { Suspense, lazy } from "react";
|
|
4
|
+
import { Suspense, lazy, use } from "react";
|
|
4
5
|
//#region src/editor/Editor.tsx
|
|
5
|
-
var
|
|
6
|
+
var loadMonaco = () => import("./Monaco.js");
|
|
7
|
+
var MonacoEditor = lazy(loadMonaco);
|
|
8
|
+
function LoadedEditor(props) {
|
|
9
|
+
const languages = editorLanguagesLoading();
|
|
10
|
+
if (languages) {
|
|
11
|
+
loadMonaco();
|
|
12
|
+
use(languages);
|
|
13
|
+
}
|
|
14
|
+
return /* @__PURE__ */ jsx(MonacoEditor, { ...props });
|
|
15
|
+
}
|
|
6
16
|
/** A Monaco code editor, loaded on first render so Monaco stays out of the host's first bundle. */
|
|
7
17
|
function Editor(props) {
|
|
8
18
|
const { width = "100%", height = "100%" } = props;
|
|
@@ -15,7 +25,7 @@ function Editor(props) {
|
|
|
15
25
|
className: "flex items-center justify-center",
|
|
16
26
|
children: /* @__PURE__ */ jsx(Loader, {})
|
|
17
27
|
}),
|
|
18
|
-
children: /* @__PURE__ */ jsx(
|
|
28
|
+
children: /* @__PURE__ */ jsx(LoadedEditor, { ...props })
|
|
19
29
|
});
|
|
20
30
|
}
|
|
21
31
|
//#endregion
|
package/dist/editor/Monaco.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import * as monaco from 'monaco-editor';
|
|
1
|
+
import * as monaco from 'monaco-editor/editor/editor.api';
|
|
2
2
|
import { type NestedWorkflowText } from './nestedText.js';
|
|
3
|
+
import './monacoFeatures.js';
|
|
3
4
|
/** A problem Monaco or the YAML service found in the text, as `onValidate` reports it. */
|
|
4
5
|
export type ValidationMarker = monaco.editor.IMarker;
|
|
5
6
|
/** A problem to mark on one line of the editor. */
|
|
@@ -11,6 +12,12 @@ export interface EditorProps extends monaco.editor.IStandaloneEditorConstruction
|
|
|
11
12
|
className?: string;
|
|
12
13
|
/** Shows the text greyed out and read-only, as a disabled field does. */
|
|
13
14
|
disabled?: boolean | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Shows the text to read rather than edit: it stays selectable, copyable and reachable by
|
|
17
|
+
* keyboard, announces itself read-only, and shows no cursor, current line or quick fixes. The
|
|
18
|
+
* view ends at the last line, and the wheel scrolls the page once the text has none left.
|
|
19
|
+
*/
|
|
20
|
+
readOnly?: boolean;
|
|
14
21
|
/** The text as the person edits it, with carriage returns removed. */
|
|
15
22
|
onChange?: (value: string) => void;
|
|
16
23
|
/** Every marker on the text, each time they change: the schema's, the lint's and the host's. */
|
package/dist/editor/Monaco.js
CHANGED
|
@@ -8,10 +8,11 @@ import { registerRevealer } from "./reveal.js";
|
|
|
8
8
|
import { defineEditorThemes, followThemeTokens, getThemeName } from "./themes.js";
|
|
9
9
|
import { setupMonacoWorkers } from "./workers.js";
|
|
10
10
|
import { holdListeners, modelFor } from "./editorModel.js";
|
|
11
|
+
import "./monacoFeatures.js";
|
|
11
12
|
import { ensureYamlLanguage } from "./yamlLanguage.js";
|
|
12
13
|
import { jsx } from "react/jsx-runtime";
|
|
13
14
|
import { useEffect, useId, useLayoutEffect, useRef } from "react";
|
|
14
|
-
import * as monaco from "monaco-editor";
|
|
15
|
+
import * as monaco from "monaco-editor/editor/editor.api";
|
|
15
16
|
//#region src/editor/Monaco.tsx
|
|
16
17
|
setupMonacoWorkers();
|
|
17
18
|
var SCHEMA_UNREADABLE = "768";
|
|
@@ -86,9 +87,17 @@ function MonacoEditor({ className, disabled, onChange = noop, onValidate = noop,
|
|
|
86
87
|
const editorOptions = {
|
|
87
88
|
ariaLabel: t.codeEditor,
|
|
88
89
|
readOnlyMessage: { value: t.readOnlyText },
|
|
90
|
+
domReadOnly: readOnly,
|
|
91
|
+
hideCursorInOverviewRuler: readOnly,
|
|
92
|
+
lightbulb: { enabled: readOnly ? monaco.editor.ShowLightbulbIconMode.Off : monaco.editor.ShowLightbulbIconMode.OnCode },
|
|
93
|
+
scrollBeyondLastLine: !readOnly,
|
|
89
94
|
...options,
|
|
95
|
+
scrollbar: {
|
|
96
|
+
alwaysConsumeMouseWheel: !readOnly,
|
|
97
|
+
...options.scrollbar
|
|
98
|
+
},
|
|
90
99
|
readOnly,
|
|
91
|
-
renderLineHighlight: disabled ? "none" : options.renderLineHighlight ?? "line"
|
|
100
|
+
renderLineHighlight: disabled ? "none" : options.renderLineHighlight ?? (readOnly ? "none" : "line")
|
|
92
101
|
};
|
|
93
102
|
const optionsKey = JSON.stringify(editorOptions);
|
|
94
103
|
const optionsRef = useRef(editorOptions);
|
|
@@ -192,6 +201,9 @@ function MonacoEditor({ className, disabled, onChange = noop, onValidate = noop,
|
|
|
192
201
|
for (const name of DISABLED_BACKGROUNDS) if (disabled) node?.style.setProperty(name, "var(--theme-input-disabled-bg)");
|
|
193
202
|
else node?.style.removeProperty(name);
|
|
194
203
|
}, [disabled]);
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
monacoRef.current?.getDomNode()?.querySelector("textarea.inputarea, .native-edit-context")?.setAttribute("aria-readonly", String(readOnly));
|
|
206
|
+
}, [readOnly, path]);
|
|
195
207
|
useEffect(() => {
|
|
196
208
|
monacoRef.current?.updateOptions(optionsRef.current);
|
|
197
209
|
}, [optionsKey]);
|
|
@@ -240,7 +252,8 @@ function MonacoEditor({ className, disabled, onChange = noop, onValidate = noop,
|
|
|
240
252
|
height
|
|
241
253
|
},
|
|
242
254
|
"data-disabled": disabled ? "" : void 0,
|
|
243
|
-
|
|
255
|
+
"data-read-only": readOnly ? "" : void 0,
|
|
256
|
+
className: cx("relative rounded border border-(--theme-border)", "focus-within:border-(--theme-accent) focus-within:ring-1 focus-within:ring-(--theme-accent)", "data-read-only:[&_.cursors-layer]:hidden data-disabled:[&_.view-lines]:opacity-60", className)
|
|
244
257
|
});
|
|
245
258
|
}
|
|
246
259
|
//#endregion
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as monaco from 'monaco-editor';
|
|
1
|
+
import * as monaco from 'monaco-editor/editor/editor.api';
|
|
2
2
|
/** The model a new editor on `uri` edits, holding `value`. */
|
|
3
3
|
export declare function modelFor(uri: monaco.Uri, value: string, language: string | undefined): monaco.editor.ITextModel;
|
|
4
4
|
/** An editor's listeners on its model, dropped when the next editor takes the model over. */
|
package/dist/editor/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { Editor } from './Editor.js';
|
|
2
2
|
export { EditorField, type IEditorField } from './EditorField.js';
|
|
3
3
|
export { FormCodePanel, type FormCodePanelProps } from './FormCodePanel.js';
|
|
4
|
+
export { addEditorLanguages, type EditorLanguages } from './languages.js';
|
|
4
5
|
export { HOST_MARKER_OWNER, LINT_OWNER } from './lintOwner.js';
|
|
5
6
|
export type { EditorMarker, EditorProps, ValidationMarker } from './Monaco.js';
|
|
6
7
|
export { type NestedWorkflowText, nestedWorkflowText } from './nestedText.js';
|
package/dist/editor/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HOST_MARKER_OWNER, LINT_OWNER } from "./lintOwner.js";
|
|
2
2
|
import { INPUT_YAML_PATH, JOB_YAML_PATH, SETTINGS_YAML_PATH, STEP_YAML_PATH, settingsSchemas } from "./settingsYaml.js";
|
|
3
|
+
import { addEditorLanguages } from "./languages.js";
|
|
3
4
|
import { Editor } from "./Editor.js";
|
|
4
5
|
import { EditorField } from "./EditorField.js";
|
|
5
6
|
import { FormCodePanel } from "./FormCodePanel.js";
|
|
@@ -8,4 +9,4 @@ import { revealLines } from "./reveal.js";
|
|
|
8
9
|
import { defineEditorThemes, followThemeTokens, getThemeName } from "./themes.js";
|
|
9
10
|
import { setupMonacoWorkers } from "./workers.js";
|
|
10
11
|
import { configureEditorYaml } from "./yaml.js";
|
|
11
|
-
export { Editor, EditorField, FormCodePanel, HOST_MARKER_OWNER, INPUT_YAML_PATH, JOB_YAML_PATH, LINT_OWNER, SETTINGS_YAML_PATH, STEP_YAML_PATH, configureEditorYaml, defineEditorThemes, followThemeTokens, getThemeName, nestedWorkflowText, revealLines, settingsSchemas, setupMonacoWorkers };
|
|
12
|
+
export { Editor, EditorField, FormCodePanel, HOST_MARKER_OWNER, INPUT_YAML_PATH, JOB_YAML_PATH, LINT_OWNER, SETTINGS_YAML_PATH, STEP_YAML_PATH, addEditorLanguages, configureEditorYaml, defineEditorThemes, followThemeTokens, getThemeName, nestedWorkflowText, revealLines, settingsSchemas, setupMonacoWorkers };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Languages a host adds to the ones the editor carries. */
|
|
2
|
+
export interface EditorLanguages {
|
|
3
|
+
/**
|
|
4
|
+
* Imports Monaco's registration for the languages, such as
|
|
5
|
+
* `() => import('monaco-editor/languages/definitions/ruby/register')`. It runs when the next
|
|
6
|
+
* editor mounts, so Monaco still loads only once an editor needs it.
|
|
7
|
+
*/
|
|
8
|
+
load: () => Promise<unknown>;
|
|
9
|
+
/**
|
|
10
|
+
* A worker for each language service the registration brings, by the label Monaco asks for,
|
|
11
|
+
* such as `typescript` and `javascript`. Basic languages color text on the page and need none.
|
|
12
|
+
*/
|
|
13
|
+
workers?: Record<string, () => Worker>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Adds language services with their workers, such as TypeScript's, or languages of the host's
|
|
17
|
+
* own, to the coloring the editor has for every Monaco language. Call it once at startup, before
|
|
18
|
+
* the first editor mounts; an editor that mounts while they load waits for them.
|
|
19
|
+
*/
|
|
20
|
+
export declare function addEditorLanguages({ load, workers: added }: EditorLanguages): void;
|
|
21
|
+
/** The languages still loading, starting any added since, or undefined once all have. */
|
|
22
|
+
export declare function editorLanguagesLoading(): Promise<unknown> | undefined;
|
|
23
|
+
/** The host's worker for a Monaco worker label, if it added one. */
|
|
24
|
+
export declare function hostEditorWorker(label: string): (() => Worker) | undefined;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//#region src/editor/languages.ts
|
|
2
|
+
var workers = /* @__PURE__ */ new Map();
|
|
3
|
+
var waiting = [];
|
|
4
|
+
var loading;
|
|
5
|
+
/**
|
|
6
|
+
* Adds language services with their workers, such as TypeScript's, or languages of the host's
|
|
7
|
+
* own, to the coloring the editor has for every Monaco language. Call it once at startup, before
|
|
8
|
+
* the first editor mounts; an editor that mounts while they load waits for them.
|
|
9
|
+
*/
|
|
10
|
+
function addEditorLanguages({ load, workers: added = {} }) {
|
|
11
|
+
for (const [label, create] of Object.entries(added)) workers.set(label, create);
|
|
12
|
+
waiting.push(load);
|
|
13
|
+
}
|
|
14
|
+
/** The languages still loading, starting any added since, or undefined once all have. */
|
|
15
|
+
function editorLanguagesLoading() {
|
|
16
|
+
if (waiting.length > 0) {
|
|
17
|
+
const loads = waiting;
|
|
18
|
+
waiting = [];
|
|
19
|
+
const started = Promise.all([loading, ...loads.map((load) => load().catch((error) => console.error("An editor language failed to load", error)))]).finally(() => {
|
|
20
|
+
if (loading === started) loading = void 0;
|
|
21
|
+
});
|
|
22
|
+
loading = started;
|
|
23
|
+
}
|
|
24
|
+
return loading;
|
|
25
|
+
}
|
|
26
|
+
/** The host's worker for a Monaco worker label, if it added one. */
|
|
27
|
+
function hostEditorWorker(label) {
|
|
28
|
+
return workers.get(label);
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { addEditorLanguages, editorLanguagesLoading, hostEditorWorker };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import 'monaco-editor/features/register.all';
|
|
2
|
+
import 'monaco-editor/editor/contrib/caretOperations/browser/caretOperations';
|
|
3
|
+
import 'monaco-editor/editor/contrib/dropOrPasteInto/browser/copyPasteContribution';
|
|
4
|
+
import 'monaco-editor/editor/contrib/gotoError/browser/markerSelectionStatus';
|
|
5
|
+
import 'monaco-editor/editor/contrib/semanticTokens/browser/documentSemanticTokens';
|
|
6
|
+
import 'monaco-editor/languages/definitions/register.all';
|
|
7
|
+
import 'monaco-editor/languages/features/json/register';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { languages } from "monaco-editor/editor/editor.api";
|
|
2
|
+
import "monaco-editor/features/register.all";
|
|
3
|
+
import "monaco-editor/editor/contrib/caretOperations/browser/caretOperations";
|
|
4
|
+
import "monaco-editor/editor/contrib/dropOrPasteInto/browser/copyPasteContribution";
|
|
5
|
+
import "monaco-editor/editor/contrib/gotoError/browser/markerSelectionStatus";
|
|
6
|
+
import "monaco-editor/editor/contrib/semanticTokens/browser/documentSemanticTokens";
|
|
7
|
+
import "monaco-editor/languages/definitions/register.all";
|
|
8
|
+
import "monaco-editor/languages/features/json/register";
|
|
9
|
+
//#region src/editor/monacoFeatures.ts
|
|
10
|
+
var shell = () => import("monaco-editor/languages/definitions/shell/shell");
|
|
11
|
+
languages.register({
|
|
12
|
+
id: "bash",
|
|
13
|
+
aliases: ["Bash", "bash"]
|
|
14
|
+
});
|
|
15
|
+
languages.registerTokensProviderFactory("bash", { create: async () => (await shell()).language });
|
|
16
|
+
languages.onLanguageEncountered("bash", async () => {
|
|
17
|
+
languages.setLanguageConfiguration("bash", (await shell()).conf);
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
package/dist/editor/themes.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type * as monaco from 'monaco-editor';
|
|
2
|
+
import { type Mode } from '../theme/index.js';
|
|
2
3
|
export declare function expandShorthandHex(color: string): string;
|
|
4
|
+
/** The arm of a `light-dark()` value for a mode, or the value itself. A token the host leaves to
|
|
5
|
+
* theme.css reads as `light-dark(light, dark)`, which CSS resolves by the color-scheme of where it
|
|
6
|
+
* is used; Monaco takes one color per theme, so each theme takes its own mode's arm. */
|
|
7
|
+
export declare function forMode(value: string, mode: Mode): string;
|
|
3
8
|
/**
|
|
4
9
|
* Defines the light and dark Monaco themes from the document's tokens. Call it again after the
|
|
5
10
|
* host applies other tokens; it returns whether anything changed.
|
package/dist/editor/themes.js
CHANGED
|
@@ -6,11 +6,26 @@ function expandShorthandHex(color) {
|
|
|
6
6
|
return /^#([0-9a-fA-F]{3,4})$/.test(color) ? "#" + color.slice(1).split("").map((c) => c + c).join("") : color;
|
|
7
7
|
}
|
|
8
8
|
var fallbacks = {};
|
|
9
|
+
/** The arm of a `light-dark()` value for a mode, or the value itself. A token the host leaves to
|
|
10
|
+
* theme.css reads as `light-dark(light, dark)`, which CSS resolves by the color-scheme of where it
|
|
11
|
+
* is used; Monaco takes one color per theme, so each theme takes its own mode's arm. */
|
|
12
|
+
function forMode(value, mode) {
|
|
13
|
+
const inner = /^light-dark\((.*)\)$/s.exec(value.trim())?.[1];
|
|
14
|
+
if (inner === void 0) return value;
|
|
15
|
+
let depth = 0;
|
|
16
|
+
for (let i = 0; i < inner.length; i++) {
|
|
17
|
+
const ch = inner[i];
|
|
18
|
+
if (ch === "(") depth++;
|
|
19
|
+
else if (ch === ")") depth--;
|
|
20
|
+
else if (ch === "," && depth === 0) return (mode === "light" ? inner.slice(0, i) : inner.slice(i + 1)).trim();
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
9
24
|
/** A token as Monaco takes it: a 6-digit hex. Monaco can't read display-p3, so
|
|
10
25
|
* a wide-gamut value is mapped back into sRGB; the default theme fills in for
|
|
11
26
|
* a host that sets no tokens. */
|
|
12
27
|
function token(style, name, mode) {
|
|
13
|
-
const color = parseColor(expandShorthandHex(style?.getPropertyValue(name)
|
|
28
|
+
const color = parseColor(expandShorthandHex(forMode(style?.getPropertyValue(name) ?? "", mode).trim()));
|
|
14
29
|
if (color) return toHex(color);
|
|
15
30
|
fallbacks[mode] ??= deriveTheme(schemeFor(DEFAULT_THEME, mode));
|
|
16
31
|
return fallbacks[mode][name];
|
|
@@ -54,8 +69,13 @@ function editorTheme(style, mode) {
|
|
|
54
69
|
"editor.lineHighlightBackground": c("--theme-hover"),
|
|
55
70
|
"editorLineNumber.foreground": c("--theme-placeholder"),
|
|
56
71
|
"editorLineNumber.activeForeground": c("--theme-muted-text-color"),
|
|
57
|
-
"
|
|
58
|
-
|
|
72
|
+
...mode === "dark" ? {
|
|
73
|
+
"editor.selectionBackground": `${c("--theme-accent")}66`,
|
|
74
|
+
"editor.inactiveSelectionBackground": `${c("--theme-accent")}40`
|
|
75
|
+
} : {
|
|
76
|
+
"editor.selectionBackground": c("--theme-accent-subtle"),
|
|
77
|
+
"editor.inactiveSelectionBackground": c("--theme-hover")
|
|
78
|
+
}
|
|
59
79
|
}
|
|
60
80
|
};
|
|
61
81
|
}
|
|
@@ -115,4 +135,4 @@ function getThemeName(isDark) {
|
|
|
115
135
|
return `app-${isDark ? "dark" : "light"}`;
|
|
116
136
|
}
|
|
117
137
|
//#endregion
|
|
118
|
-
export { defineEditorThemes, expandShorthandHex, followThemeTokens, getThemeName };
|
|
138
|
+
export { defineEditorThemes, expandShorthandHex, followThemeTokens, forMode, getThemeName };
|
package/dist/editor/workers.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Points window.MonacoEnvironment at the bundled workers
|
|
3
|
-
* host that already configured its own
|
|
4
|
-
* calls this on first load only when nothing is configured.
|
|
2
|
+
* Points window.MonacoEnvironment at the bundled workers and those a host added with
|
|
3
|
+
* `addEditorLanguages`. Idempotent, and a host that already configured its own
|
|
4
|
+
* MonacoEnvironment wins — the editor calls this on first load only when nothing is configured.
|
|
5
5
|
*/
|
|
6
6
|
export declare function setupMonacoWorkers(): void;
|
|
7
7
|
/**
|
package/dist/editor/workers.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
|
+
import { hostEditorWorker } from "./languages.js";
|
|
1
2
|
//#region src/editor/workers.ts
|
|
2
3
|
var monacoEnvironment = { getWorker(_moduleId, label) {
|
|
4
|
+
const host = hostEditorWorker(label);
|
|
5
|
+
if (host) return host();
|
|
3
6
|
if (label === "json") return new Worker(new URL(
|
|
4
7
|
/* @vite-ignore */
|
|
5
8
|
new URL("../assets/json.worker-BKkHJbNB.js", import.meta.url).href,
|
|
6
9
|
"" + import.meta.url
|
|
7
10
|
), { type: "module" });
|
|
8
|
-
return new Worker(new URL(
|
|
11
|
+
if (label === "editorWorkerService") return new Worker(new URL(
|
|
9
12
|
/* @vite-ignore */
|
|
10
13
|
new URL("../assets/editor.worker-Cc3TfJ_-.js", import.meta.url).href,
|
|
11
14
|
"" + import.meta.url
|
|
12
15
|
), { type: "module" });
|
|
16
|
+
throw new Error(`The editor has no worker for Monaco's "${label}" service: pass one to addEditorLanguages.`);
|
|
13
17
|
} };
|
|
14
18
|
/**
|
|
15
|
-
* Points window.MonacoEnvironment at the bundled workers
|
|
16
|
-
* host that already configured its own
|
|
17
|
-
* calls this on first load only when nothing is configured.
|
|
19
|
+
* Points window.MonacoEnvironment at the bundled workers and those a host added with
|
|
20
|
+
* `addEditorLanguages`. Idempotent, and a host that already configured its own
|
|
21
|
+
* MonacoEnvironment wins — the editor calls this on first load only when nothing is configured.
|
|
18
22
|
*/
|
|
19
23
|
function setupMonacoWorkers() {
|
|
20
24
|
if (window.MonacoEnvironment) return;
|