@parallelworks/ui 0.15.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{CodePreviewEditor-DuMZMA_k.js → CodePreviewEditor-UAtnpmOV.js} +1 -1
- package/dist/chunks/{CreateModal-B7smkHkv.js → CreateModal-BbV5cTj7.js} +3 -3
- package/dist/chunks/{Monaco-BXDH2jVj.js → Monaco-DVyIpyv4.js} +485 -172
- package/dist/chunks/{PageHeader-CoZWF0pJ.js → PageHeader-MxuOs9cH.js} +1 -1
- package/dist/chunks/{Provider-CbFVFB7O.js → Provider-DHmZ3v9i.js} +28 -2
- package/dist/chunks/{StatusBadge-zvEtK16a.js → StatusBadge-C1Blx_cW.js} +23 -6
- package/dist/chunks/{SwitchToggle-3V836Cn7.js → SwitchToggle-BRQoEwLZ.js} +7 -4
- package/dist/chunks/{Table-CutP1bRt.js → Table-C6JdFFiJ.js} +1 -1
- package/dist/chunks/{editor-Ko3zX22Y.js → editor-mNznodJN.js} +2 -2
- package/dist/chunks/{list-CduoUsMW.js → list-7k0eaI5N.js} +21 -19
- package/dist/components/Button.d.ts +2 -1
- package/dist/components/Provider.d.ts +57 -2
- package/dist/components/SectionHeader.d.ts +3 -1
- package/dist/components/StatusBadge.d.ts +7 -2
- package/dist/components/relativeTime.d.ts +15 -0
- package/dist/editor/index.js +1 -1
- package/dist/editor/usesGitlab.d.ts +97 -0
- package/dist/file-explorer/index.js +253 -253
- package/dist/file-explorer/lib/paging.d.ts +27 -28
- package/dist/file-explorer/lib/types.d.ts +0 -1
- package/dist/file-explorer/lib/utils.d.ts +16 -5
- package/dist/form/index.js +5 -5
- package/dist/form/types/workflow-schema.d.ts +38 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +42 -11
- package/dist/list/ListPage.d.ts +3 -1
- package/dist/list/ListPager.d.ts +3 -1
- package/dist/list/ListTable.d.ts +2 -1
- package/dist/list/index.js +2 -2
- package/dist/list/listView.d.ts +1 -1
- package/dist/logviewer/index.js +1 -1
- package/dist/styles/theme.css +14 -12
- package/dist/styles.css +1 -1
- package/dist/workflow/DependencyGraph.d.ts +2 -0
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.js +3 -2
- package/dist/workflow/types.d.ts +5 -0
- package/package.json +4 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AddIcon, CheckIcon, ChevronRightIcon, ClipboardIcon, CopyIcon, EditIcon, IdCardIcon, KeyIcon, LinkIcon, MailIcon, MoreIcon, UserIcon } from "../icons.js";
|
|
2
|
-
import {
|
|
2
|
+
import { i as useNotify, l as useSlots, n as useLink, u as useStrings } from "./Provider-DHmZ3v9i.js";
|
|
3
3
|
import { n as TOOLTIP_ID } from "./Tooltip-n9w7Tkt1.js";
|
|
4
4
|
import cx from "classnames";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useContext, useMemo } from "react";
|
|
3
|
+
import { useLocalStorage } from "usehooks-ts";
|
|
3
4
|
//#region src/components/Provider.tsx
|
|
4
5
|
var DefaultLink = ({ to, onClick, className, children, ...rest }) => /* @__PURE__ */ jsx("a", {
|
|
5
6
|
href: to,
|
|
@@ -17,6 +18,10 @@ var defaultUseRunFile = () => ({
|
|
|
17
18
|
isLoading: false,
|
|
18
19
|
error: void 0
|
|
19
20
|
});
|
|
21
|
+
function useLocalStoragePersistedState(key, defaultValue) {
|
|
22
|
+
const [value, setValue] = useLocalStorage(key, defaultValue);
|
|
23
|
+
return [value, setValue];
|
|
24
|
+
}
|
|
20
25
|
var DEFAULTS = {
|
|
21
26
|
notify: {
|
|
22
27
|
info: () => {},
|
|
@@ -319,6 +324,13 @@ var DEFAULTS = {
|
|
|
319
324
|
readOnlyDescription: "Read-only mounts can list and download objects, but cannot upload, modify, or delete them.",
|
|
320
325
|
readOnlyForced: "Your access to this bucket is read-only, so this mount cannot be writable."
|
|
321
326
|
},
|
|
327
|
+
time: {
|
|
328
|
+
now: "now",
|
|
329
|
+
minutes: (n) => `${n}m`,
|
|
330
|
+
hours: (n) => `${n}h`,
|
|
331
|
+
days: (n) => `${n}d`,
|
|
332
|
+
years: (n) => `${n}y`
|
|
333
|
+
},
|
|
322
334
|
list: {
|
|
323
335
|
copy: (label) => `Copy ${label}`,
|
|
324
336
|
copied: (label) => `Copied ${label}`,
|
|
@@ -393,7 +405,10 @@ var DEFAULTS = {
|
|
|
393
405
|
toMarketItem: () => {},
|
|
394
406
|
openExternal: (href) => window.open(href, "_blank")
|
|
395
407
|
},
|
|
396
|
-
data: {
|
|
408
|
+
data: {
|
|
409
|
+
useRunFile: defaultUseRunFile,
|
|
410
|
+
usePersistedState: useLocalStoragePersistedState
|
|
411
|
+
},
|
|
397
412
|
slots: {}
|
|
398
413
|
};
|
|
399
414
|
var UIContext = createContext(DEFAULTS);
|
|
@@ -415,10 +430,17 @@ function useWorkflowJsonResolver() {
|
|
|
415
430
|
function useProvisionCorsRules() {
|
|
416
431
|
return useContext(UIContext).data.provisionCorsRules;
|
|
417
432
|
}
|
|
433
|
+
function useRepoSuggestions() {
|
|
434
|
+
return useContext(UIContext).data.repoSuggestions;
|
|
435
|
+
}
|
|
418
436
|
function useRunFile(slug, path, options) {
|
|
419
437
|
const { useRunFile: boundUseRunFile } = useContext(UIContext).data;
|
|
420
438
|
return boundUseRunFile(slug, path, options);
|
|
421
439
|
}
|
|
440
|
+
function usePersistedState(key, defaultValue) {
|
|
441
|
+
const { usePersistedState: boundUsePersistedState } = useContext(UIContext).data;
|
|
442
|
+
return boundUsePersistedState(key, defaultValue);
|
|
443
|
+
}
|
|
422
444
|
function UIProvider({ notify, strings, navigation, data, slots, children }) {
|
|
423
445
|
const value = useMemo(() => ({
|
|
424
446
|
notify: {
|
|
@@ -459,6 +481,10 @@ function UIProvider({ notify, strings, navigation, data, slots, children }) {
|
|
|
459
481
|
...DEFAULTS.strings.form,
|
|
460
482
|
...strings?.form
|
|
461
483
|
},
|
|
484
|
+
time: {
|
|
485
|
+
...DEFAULTS.strings.time,
|
|
486
|
+
...strings?.time
|
|
487
|
+
},
|
|
462
488
|
list: {
|
|
463
489
|
...DEFAULTS.strings.list,
|
|
464
490
|
...strings?.list
|
|
@@ -486,4 +512,4 @@ function UIProvider({ notify, strings, navigation, data, slots, children }) {
|
|
|
486
512
|
});
|
|
487
513
|
}
|
|
488
514
|
//#endregion
|
|
489
|
-
export {
|
|
515
|
+
export { usePersistedState as a, useRunFile as c, useWorkflowJsonResolver as d, useNotify as i, useSlots as l, useLink as n, useProvisionCorsRules as o, useNavigation as r, useRepoSuggestions as s, UIProvider as t, useStrings as u };
|
|
@@ -115,13 +115,30 @@ var pulseColors = {
|
|
|
115
115
|
info: "bg-blue-500",
|
|
116
116
|
muted: "bg-(--theme-muted-text-color)"
|
|
117
117
|
};
|
|
118
|
-
|
|
118
|
+
var textColors = {
|
|
119
|
+
default: "text-(--theme-app)",
|
|
120
|
+
success: "text-emerald-600 dark:text-emerald-400",
|
|
121
|
+
warning: "text-amber-600 dark:text-amber-400",
|
|
122
|
+
error: "text-red-600 dark:text-red-400",
|
|
123
|
+
info: "text-blue-600 dark:text-blue-400",
|
|
124
|
+
muted: "text-(--theme-muted-text-color)"
|
|
125
|
+
};
|
|
126
|
+
/** A line of text in a variant's colour, for a status that stands beside a
|
|
127
|
+
* dot rather than inside a badge. */
|
|
128
|
+
function statusTextClass(variant) {
|
|
129
|
+
return textColors[variant];
|
|
130
|
+
}
|
|
131
|
+
function StatusBadge({ children, variant = "default", pulse = false, dot = false, size = "sm", className }) {
|
|
119
132
|
return /* @__PURE__ */ jsxs("span", {
|
|
120
133
|
className: cx("inline-flex items-center gap-1.5 rounded-full border font-medium", size === "sm" && "px-2 py-0.5 text-[10px]", size === "md" && "px-2.5 py-1 text-xs", variantStyles[variant], className),
|
|
121
|
-
children: [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
children: [
|
|
135
|
+
pulse && /* @__PURE__ */ jsxs("span", {
|
|
136
|
+
className: "relative flex h-2 w-2",
|
|
137
|
+
children: [/* @__PURE__ */ jsx("span", { className: cx("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75", pulseColors[variant]) }), /* @__PURE__ */ jsx("span", { className: cx("relative inline-flex h-2 w-2 rounded-full", pulseColors[variant]) })]
|
|
138
|
+
}),
|
|
139
|
+
dot && !pulse && /* @__PURE__ */ jsx("span", { className: cx("inline-flex h-1.5 w-1.5 rounded-full", pulseColors[variant]) }),
|
|
140
|
+
children
|
|
141
|
+
]
|
|
125
142
|
});
|
|
126
143
|
}
|
|
127
144
|
function StatusDot({ variant = "default", pulse = false, size = 8, className }) {
|
|
@@ -141,4 +158,4 @@ function StatusDot({ variant = "default", pulse = false, size = 8, className })
|
|
|
141
158
|
});
|
|
142
159
|
}
|
|
143
160
|
//#endregion
|
|
144
|
-
export {
|
|
161
|
+
export { filterOption as a, isCategory as c, DropdownCaret as i, isOption as l, StatusDot as n, flattenDisplayOptions as o, statusTextClass as r, flattenOptions as s, StatusBadge as t, toggleSetMember as u };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AngleDownIcon, AngleRightIcon, CaretDownIcon } from "../icons.js";
|
|
2
|
-
import {
|
|
2
|
+
import { u as useStrings } from "./Provider-DHmZ3v9i.js";
|
|
3
3
|
import { a as truncationTooltipProps, n as TOOLTIP_ID } from "./Tooltip-n9w7Tkt1.js";
|
|
4
|
-
import { a as
|
|
4
|
+
import { a as filterOption, c as isCategory, i as DropdownCaret, l as isOption, o as flattenDisplayOptions, s as flattenOptions, u as toggleSetMember } from "./StatusBadge-C1Blx_cW.js";
|
|
5
5
|
import cx from "classnames";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
@@ -254,6 +254,7 @@ function DropdownHelper({ wrapperClassName = "w-full", className, textBoxClassNa
|
|
|
254
254
|
customValueLabel
|
|
255
255
|
]);
|
|
256
256
|
const comboOptions = /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(ComboboxOptions, {
|
|
257
|
+
modal: false,
|
|
257
258
|
ref: setPopperElement,
|
|
258
259
|
style: styles["popper"],
|
|
259
260
|
className: "bg-(--theme-input-bg) border-(--theme-border) text-(--theme-app) max-h-60! empty:invisible border overflow-auto rounded-md shadow-lg focus:outline-hidden sm:text-sm z-9999 data-[popper-reference-hidden=true]:invisible data-[popper-reference-hidden=true]:pointer-events-none",
|
|
@@ -368,6 +369,7 @@ function CustomListbox({ options = [], handleOptionSelected, type, allowSearch =
|
|
|
368
369
|
const wrapperRef = useRef(null);
|
|
369
370
|
useOutsideAlerter(wrapperRef);
|
|
370
371
|
const optionsElement = /* @__PURE__ */ jsxs(ListboxOptions, {
|
|
372
|
+
modal: false,
|
|
371
373
|
static: true,
|
|
372
374
|
className: "absolute w-full py-1 z-50 theme-input rounded-md",
|
|
373
375
|
children: [allowSearch && /* @__PURE__ */ jsx("div", {
|
|
@@ -545,6 +547,7 @@ function MultiSelectDropdown({ options, value, onChange, id, "aria-labelledby":
|
|
|
545
547
|
}),
|
|
546
548
|
/* @__PURE__ */ jsx(DropdownCaret, {}),
|
|
547
549
|
open && /* @__PURE__ */ jsxs(ComboboxOptions, {
|
|
550
|
+
modal: false,
|
|
548
551
|
ref: setPopperEl,
|
|
549
552
|
static: true,
|
|
550
553
|
"data-testid": "msd-list",
|
|
@@ -689,9 +692,9 @@ function MaybeCollapsible({ className, initialState, children, title, customized
|
|
|
689
692
|
}
|
|
690
693
|
//#endregion
|
|
691
694
|
//#region src/components/SectionHeader.tsx
|
|
692
|
-
function SectionHeader({ children, className = "" }) {
|
|
695
|
+
function SectionHeader({ children, className = "", size = "md" }) {
|
|
693
696
|
return /* @__PURE__ */ jsx("div", {
|
|
694
|
-
className: cx("w-full text-
|
|
697
|
+
className: cx("w-full font-semibold", size === "sm" ? "mb-2 text-sm theme-text" : "text-lg", className),
|
|
695
698
|
children
|
|
696
699
|
});
|
|
697
700
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CheckIcon, CopyIcon, EyeIcon, EyeOffIcon } from "../icons.js";
|
|
2
|
-
import {
|
|
2
|
+
import { i as useNotify, u as useStrings } from "./Provider-DHmZ3v9i.js";
|
|
3
3
|
import { t as Loader } from "./Loader-BXoXTZGc.js";
|
|
4
4
|
import { t as SwitchToggleSmall } from "./SwitchToggleSmall-FcsaYSTs.js";
|
|
5
5
|
import { c as FieldWrapper, n as flattenGroups, r as impureSetValueFromPath } from "./lib-CUTRDpa6.js";
|
|
@@ -9,7 +9,7 @@ import { Suspense, lazy, useId, useMemo, useRef, useState } from "react";
|
|
|
9
9
|
import { convertToDynamicForm, parseStringsOfObj, parseStringsOfObjReady } from "@parallelworks/workflow-parser";
|
|
10
10
|
import { useField, useFormikContext } from "formik";
|
|
11
11
|
//#region src/editor/Editor.tsx
|
|
12
|
-
var MonacoEditor = lazy(() => import("./Monaco-
|
|
12
|
+
var MonacoEditor = lazy(() => import("./Monaco-DVyIpyv4.js"));
|
|
13
13
|
function Editor({ ...props }) {
|
|
14
14
|
return /* @__PURE__ */ jsx(Suspense, {
|
|
15
15
|
fallback: /* @__PURE__ */ jsx(Loader, { text: "Loading editor..." }),
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, DisplayIcon, ErrorIcon, FilterIcon, MenuIcon, SearchIcon, XIcon } from "../icons.js";
|
|
2
|
-
import {
|
|
2
|
+
import { a as usePersistedState, n as useLink, r as useNavigation, u as useStrings } from "./Provider-DHmZ3v9i.js";
|
|
3
3
|
import { n as TOOLTIP_ID, r as TooltipInfo } from "./Tooltip-n9w7Tkt1.js";
|
|
4
|
-
import { _ as useRowMenuActive, c as PageHeader, p as rowContextMenuProps, u as MoreButton } from "./PageHeader-
|
|
5
|
-
import { n as Table } from "./Table-
|
|
4
|
+
import { _ as useRowMenuActive, c as PageHeader, p as rowContextMenuProps, u as MoreButton } from "./PageHeader-MxuOs9cH.js";
|
|
5
|
+
import { n as Table } from "./Table-C6JdFFiJ.js";
|
|
6
6
|
import cx from "classnames";
|
|
7
7
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { Children, Fragment as Fragment$1, cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
9
|
+
import { useLocalStorage } from "usehooks-ts";
|
|
9
10
|
import { createPortal } from "react-dom";
|
|
10
11
|
import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
|
|
11
|
-
import { useLocalStorage } from "usehooks-ts";
|
|
12
12
|
//#region src/list/ActionToolbar.tsx
|
|
13
13
|
var OverflowMenuContext = createContext(false);
|
|
14
14
|
/** Omits data-tooltip-content when absent — react-tooltip treats an explicit undefined as an empty tooltip anchor. */
|
|
@@ -203,7 +203,7 @@ var listTableFixedProps = {
|
|
|
203
203
|
...listTableProps,
|
|
204
204
|
className: listTableProps.className.replace("table-auto", "table-fixed")
|
|
205
205
|
};
|
|
206
|
-
function NameCell({ icon, name, subtitle, href, badge }) {
|
|
206
|
+
function NameCell({ icon, name, subtitle, href, badge, title }) {
|
|
207
207
|
const Link = useLink();
|
|
208
208
|
const inner = /* @__PURE__ */ jsxs(Fragment, { children: [icon, /* @__PURE__ */ jsxs("div", {
|
|
209
209
|
className: "min-w-0",
|
|
@@ -211,6 +211,7 @@ function NameCell({ icon, name, subtitle, href, badge }) {
|
|
|
211
211
|
className: "flex min-w-0 items-center gap-1.5",
|
|
212
212
|
children: [/* @__PURE__ */ jsx("div", {
|
|
213
213
|
className: cx(NAME_TEXT, href && "group-hover/namecell:text-(--theme-link)"),
|
|
214
|
+
title,
|
|
214
215
|
children: name
|
|
215
216
|
}), badge]
|
|
216
217
|
}), subtitle && /* @__PURE__ */ jsx("div", {
|
|
@@ -401,7 +402,7 @@ function ListPage({ section = "main", shellPadded, embedded = false, breadcrumbs
|
|
|
401
402
|
})]
|
|
402
403
|
});
|
|
403
404
|
}
|
|
404
|
-
function ListRow({ href, items, goTo, openMenu, pinnedActions, selected, selectionActive, onRowSelect, leading, className, children }) {
|
|
405
|
+
function ListRow({ href, onActivate, items, goTo, openMenu, pinnedActions, selected, selectionActive, onRowSelect, leading, className, children }) {
|
|
405
406
|
const { active: menuActive, openMenu: openRowMenu } = useRowMenuActive(openMenu);
|
|
406
407
|
const { show: showActions, always: alwaysShowActions, columnClasses } = useContext(RowActionsContext);
|
|
407
408
|
const styledCells = Children.toArray(children).map((child, i) => {
|
|
@@ -417,11 +418,12 @@ function ListRow({ href, items, goTo, openMenu, pinnedActions, selected, selecti
|
|
|
417
418
|
return;
|
|
418
419
|
}
|
|
419
420
|
if (href) goTo(href);
|
|
421
|
+
else onActivate?.();
|
|
420
422
|
};
|
|
421
423
|
const pinnedLabels = new Set((pinnedActions ?? []).map((a) => a.label));
|
|
422
424
|
const hasOverflowItems = items.some((item) => item.kind !== "divider" && (item.kind === "submenu" || !pinnedLabels.has(item.label)));
|
|
423
425
|
return /* @__PURE__ */ jsxs("tr", {
|
|
424
|
-
className: cx("group", selected ? "bg-blue-500/5" : menuActive ? "theme-hover" : "hover:theme-hover", (href || selectionActive) && "cursor-pointer", className),
|
|
426
|
+
className: cx("group", selected ? "bg-blue-500/5" : menuActive ? "theme-hover" : "hover:theme-hover", (href || onActivate || selectionActive) && "cursor-pointer", className),
|
|
425
427
|
onClick: handleRowClick,
|
|
426
428
|
...rowContextMenuProps(openRowMenu, items),
|
|
427
429
|
children: [
|
|
@@ -518,16 +520,16 @@ function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], def
|
|
|
518
520
|
const defaultOrderByValue = isFavorite ? DEFAULT_ORDER : defaultOrderBy ?? orderBys[0]?.value ?? "";
|
|
519
521
|
const defaultHiddenKeys = useMemo(() => columns.filter((c) => c.defaultHidden && !c.alwaysVisible).map((c) => c.key), [columns]);
|
|
520
522
|
const defaultPinnedKeys = useMemo(() => actions.filter((a) => a.defaultPinned).map((a) => a.key), [actions]);
|
|
521
|
-
const [hiddenKeys, setHiddenKeys] =
|
|
522
|
-
const [pinnedKeys, setPinnedKeys] =
|
|
523
|
-
const [groupBy, setGroupBy] =
|
|
524
|
-
const [showColumnHeaders, setShowColumnHeaders] =
|
|
525
|
-
const [showActions, setShowActions] =
|
|
526
|
-
const [alwaysShowActions, setAlwaysShowActions] =
|
|
527
|
-
const [orderBy, setOrderBy] =
|
|
528
|
-
const [orderDir, setOrderDir] =
|
|
529
|
-
const [localFilters, setLocalFilters] =
|
|
530
|
-
const [sharedFilters, setSharedFilters] =
|
|
523
|
+
const [hiddenKeys, setHiddenKeys] = usePersistedState(`${storageKey}:hiddenColumns`, defaultHiddenKeys);
|
|
524
|
+
const [pinnedKeys, setPinnedKeys] = usePersistedState(`${storageKey}:pinnedActions`, defaultPinnedKeys);
|
|
525
|
+
const [groupBy, setGroupBy] = usePersistedState(`${storageKey}:groupBy`, defaultGroupByValue);
|
|
526
|
+
const [showColumnHeaders, setShowColumnHeaders] = usePersistedState(`${storageKey}:showColumnHeaders`, defaultShowColumnHeaders);
|
|
527
|
+
const [showActions, setShowActions] = usePersistedState(`${storageKey}:showActions`, defaultShowActions);
|
|
528
|
+
const [alwaysShowActions, setAlwaysShowActions] = usePersistedState(`${storageKey}:alwaysShowActions`, false);
|
|
529
|
+
const [orderBy, setOrderBy] = usePersistedState(`${storageKey}:orderBy`, defaultOrderByValue);
|
|
530
|
+
const [orderDir, setOrderDir] = usePersistedState(`${storageKey}:orderDir`, "asc");
|
|
531
|
+
const [localFilters, setLocalFilters] = usePersistedState(`${storageKey}:filters`, {});
|
|
532
|
+
const [sharedFilters, setSharedFilters] = usePersistedState(`${sharedFiltersKey ?? storageKey}:sharedFilters`, {});
|
|
531
533
|
const sharedKeys = useMemo(() => new Set(facets.filter((f) => f.shared).map((f) => f.key)), [facets]);
|
|
532
534
|
const filters = useMemo(() => {
|
|
533
535
|
if (sharedKeys.size === 0) return localFilters;
|
|
@@ -1184,7 +1186,7 @@ var LIST_PAGE_SIZES = [
|
|
|
1184
1186
|
200
|
|
1185
1187
|
];
|
|
1186
1188
|
/** Footer pager bar for paginated new-nav lists: "{start}–{end} of {total}" with prev/next. Renders nothing for a single page. */
|
|
1187
|
-
function ListPager({ page, pageSize, total, onPageChange, pageSizes = LIST_PAGE_SIZES, onPageSizeChange }) {
|
|
1189
|
+
function ListPager({ page, pageSize, total, onPageChange, pageSizes = LIST_PAGE_SIZES, onPageSizeChange, hasNext }) {
|
|
1188
1190
|
const t = useStrings().list;
|
|
1189
1191
|
const pageCount = Math.max(1, Math.ceil(total / pageSize));
|
|
1190
1192
|
const start = page * pageSize + 1;
|
|
@@ -1220,7 +1222,7 @@ function ListPager({ page, pageSize, total, onPageChange, pageSizes = LIST_PAGE_
|
|
|
1220
1222
|
}), /* @__PURE__ */ jsx("button", {
|
|
1221
1223
|
type: "button",
|
|
1222
1224
|
"aria-label": t.paginationNext,
|
|
1223
|
-
disabled: page >= pageCount - 1,
|
|
1225
|
+
disabled: hasNext === void 0 ? page >= pageCount - 1 : !hasNext,
|
|
1224
1226
|
onClick: () => onPageChange(page + 1),
|
|
1225
1227
|
className: cx(listControlButtonClasses, "disabled:opacity-40 disabled:pointer-events-none"),
|
|
1226
1228
|
children: /* @__PURE__ */ jsx(ChevronRightIcon, { className: "w-3.5 h-3.5" })
|
|
@@ -13,6 +13,7 @@ interface MethodButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
13
13
|
label: string;
|
|
14
14
|
description?: string;
|
|
15
15
|
href?: string;
|
|
16
|
+
loading?: boolean;
|
|
16
17
|
}
|
|
17
|
-
export declare function MethodButton({ icon, label, description, href, className, ...props }: MethodButtonProps): import("react").JSX.Element;
|
|
18
|
+
export declare function MethodButton({ icon, label, description, href, loading, disabled, className, ...props }: MethodButtonProps): import("react").JSX.Element;
|
|
18
19
|
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
1
|
+
import type { Dispatch, ReactNode, SetStateAction } from 'react';
|
|
2
|
+
import type { RunLink } from '../workflow/types.js';
|
|
2
3
|
export type UIToastId = string | number;
|
|
3
4
|
export interface UIToastOptions {
|
|
4
5
|
toastId?: UIToastId;
|
|
@@ -321,6 +322,13 @@ export interface UIStrings {
|
|
|
321
322
|
readOnlyDescription: string;
|
|
322
323
|
readOnlyForced: string;
|
|
323
324
|
};
|
|
325
|
+
time: {
|
|
326
|
+
now: string;
|
|
327
|
+
minutes: (n: number) => string;
|
|
328
|
+
hours: (n: number) => string;
|
|
329
|
+
days: (n: number) => string;
|
|
330
|
+
years: (n: number) => string;
|
|
331
|
+
};
|
|
324
332
|
list: {
|
|
325
333
|
copy: (label: string) => string;
|
|
326
334
|
copied: (label: string) => string;
|
|
@@ -380,6 +388,7 @@ export interface RunFileResult {
|
|
|
380
388
|
isLoading: boolean;
|
|
381
389
|
error: unknown;
|
|
382
390
|
}
|
|
391
|
+
export type PersistedStateHook = <T>(key: string, defaultValue: T) => [T, Dispatch<SetStateAction<T>>];
|
|
383
392
|
export interface UIData {
|
|
384
393
|
/**
|
|
385
394
|
* Host-bound hook resolving a file in a run's artifact tree (logs, scripts).
|
|
@@ -406,6 +415,49 @@ export interface UIData {
|
|
|
406
415
|
}) => Promise<{
|
|
407
416
|
error?: string;
|
|
408
417
|
}>;
|
|
418
|
+
usePersistedState: PersistedStateHook;
|
|
419
|
+
/**
|
|
420
|
+
* Repository completions for `uses:`, bound by the host because the credential
|
|
421
|
+
* lives on the platform. Absent, those completions no-op.
|
|
422
|
+
*/
|
|
423
|
+
repoSuggestions?: {
|
|
424
|
+
refs: (repo: string) => Promise<RepoRef[]>;
|
|
425
|
+
files: (repo: string, ref: string, path: string) => Promise<RepoEntry[]>;
|
|
426
|
+
file: (repo: string, ref: string, path: string) => Promise<string>;
|
|
427
|
+
repos: (owner: string) => Promise<RepoSummary[]>;
|
|
428
|
+
gitlabProjects: (search: string, host?: string) => Promise<GitlabProject[]>;
|
|
429
|
+
owners: (provider: 'github' | 'gitlab', search: string, host?: string) => Promise<RepoOwner[]>;
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
/** One branch, tag or commit a `uses:` reference can name. */
|
|
433
|
+
export interface RepoRef {
|
|
434
|
+
name: string;
|
|
435
|
+
kind: string;
|
|
436
|
+
detail: string;
|
|
437
|
+
}
|
|
438
|
+
/** One repository in an owner listing. */
|
|
439
|
+
export interface RepoSummary {
|
|
440
|
+
name: string;
|
|
441
|
+
description: string;
|
|
442
|
+
private: boolean;
|
|
443
|
+
}
|
|
444
|
+
/** One account, organization or group a repository can live under. */
|
|
445
|
+
export interface RepoOwner {
|
|
446
|
+
name: string;
|
|
447
|
+
kind: string;
|
|
448
|
+
host?: string | undefined;
|
|
449
|
+
}
|
|
450
|
+
/** One GitLab project, with the server a $host must name to reach it. */
|
|
451
|
+
export interface GitlabProject {
|
|
452
|
+
path: string;
|
|
453
|
+
host: string;
|
|
454
|
+
description: string;
|
|
455
|
+
private: boolean;
|
|
456
|
+
}
|
|
457
|
+
/** One entry in a repository directory listing. */
|
|
458
|
+
export interface RepoEntry {
|
|
459
|
+
path: string;
|
|
460
|
+
type: 'blob' | 'tree';
|
|
409
461
|
}
|
|
410
462
|
import type { FieldComponent } from '../form/fieldRegistry.js';
|
|
411
463
|
import type { PageHeaderCrumb } from './PageHeader.js';
|
|
@@ -431,11 +483,12 @@ export interface UISlots {
|
|
|
431
483
|
* renders fields inside a Suspense boundary.
|
|
432
484
|
*/
|
|
433
485
|
formFields?: Record<string, FieldComponent>;
|
|
434
|
-
/**
|
|
486
|
+
/** Links and sessions block for a run's sidebar; absent hides the section. */
|
|
435
487
|
runSessions?: (args: {
|
|
436
488
|
runId: string;
|
|
437
489
|
runNumber: number;
|
|
438
490
|
runActive: boolean;
|
|
491
|
+
links?: Record<string, RunLink> | undefined;
|
|
439
492
|
}) => ReactNode;
|
|
440
493
|
/** Rich read-only YAML viewer; absent falls back to a plain <pre>. */
|
|
441
494
|
yamlViewer?: (args: {
|
|
@@ -488,9 +541,11 @@ export declare function useNavigation(): UINavigation;
|
|
|
488
541
|
export declare function useSlots(): UISlots;
|
|
489
542
|
export declare function useWorkflowJsonResolver(): ((ref: WorkflowJsonRef) => Promise<unknown>) | undefined;
|
|
490
543
|
export declare function useProvisionCorsRules(): UIData['provisionCorsRules'];
|
|
544
|
+
export declare function useRepoSuggestions(): UIData['repoSuggestions'];
|
|
491
545
|
export declare function useRunFile(slug: string, path: string | null, options?: {
|
|
492
546
|
refreshInterval?: number;
|
|
493
547
|
}): RunFileResult;
|
|
548
|
+
export declare function usePersistedState<T>(key: string, defaultValue: T): [T, Dispatch<SetStateAction<T>>];
|
|
494
549
|
export declare function UIProvider({ notify, strings, navigation, data, slots, children, }: {
|
|
495
550
|
notify?: Partial<UINotify>;
|
|
496
551
|
strings?: {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
interface ISectionHeaderProps {
|
|
2
2
|
className?: string;
|
|
3
|
+
/** 'sm' heads a group inside a page rather than the page's own section. */
|
|
4
|
+
size?: 'md' | 'sm';
|
|
3
5
|
children: React.ReactNode;
|
|
4
6
|
}
|
|
5
|
-
export default function SectionHeader({ children, className, }: ISectionHeaderProps): import("react").JSX.Element;
|
|
7
|
+
export default function SectionHeader({ children, className, size, }: ISectionHeaderProps): import("react").JSX.Element;
|
|
6
8
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
type BadgeVariant = 'default' | 'success' | 'warning' | 'error' | 'info' | 'muted';
|
|
2
|
+
export type BadgeVariant = 'default' | 'success' | 'warning' | 'error' | 'info' | 'muted';
|
|
3
3
|
interface StatusBadgeProps {
|
|
4
4
|
/** Badge content */
|
|
5
5
|
children: ReactNode;
|
|
@@ -7,12 +7,17 @@ interface StatusBadgeProps {
|
|
|
7
7
|
variant?: BadgeVariant;
|
|
8
8
|
/** Show animated pulse indicator */
|
|
9
9
|
pulse?: boolean;
|
|
10
|
+
/** Show a still indicator in the variant's colour */
|
|
11
|
+
dot?: boolean;
|
|
10
12
|
/** Size */
|
|
11
13
|
size?: 'sm' | 'md';
|
|
12
14
|
/** Additional class */
|
|
13
15
|
className?: string;
|
|
14
16
|
}
|
|
15
|
-
|
|
17
|
+
/** A line of text in a variant's colour, for a status that stands beside a
|
|
18
|
+
* dot rather than inside a badge. */
|
|
19
|
+
export declare function statusTextClass(variant: BadgeVariant): string;
|
|
20
|
+
export declare function StatusBadge({ children, variant, pulse, dot, size, className, }: StatusBadgeProps): import("react").JSX.Element;
|
|
16
21
|
interface StatusDotProps {
|
|
17
22
|
variant?: BadgeVariant;
|
|
18
23
|
pulse?: boolean;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DateTime } from 'luxon';
|
|
2
|
+
export declare function compactRelativeMinutes(minutes: number, t: {
|
|
3
|
+
now: string;
|
|
4
|
+
minutes: (n: number) => string;
|
|
5
|
+
hours: (n: number) => string;
|
|
6
|
+
days: (n: number) => string;
|
|
7
|
+
years: (n: number) => string;
|
|
8
|
+
}): string;
|
|
9
|
+
/** A clock that re-reads itself, so "5m" ages while the page sits open, with
|
|
10
|
+
* the abbreviated form a row has room for. */
|
|
11
|
+
export declare function useRelativeTime(): {
|
|
12
|
+
now: DateTime;
|
|
13
|
+
compact: (iso: string | undefined) => string;
|
|
14
|
+
compactMinutes: (minutes: number) => string;
|
|
15
|
+
};
|
package/dist/editor/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as EditorField, i as FormCodePanel, n as defineEditorThemes, o as Editor, r as getThemeName, t as setupMonacoWorkers } from "../chunks/editor-
|
|
1
|
+
import { a as EditorField, i as FormCodePanel, n as defineEditorThemes, o as Editor, r as getThemeName, t as setupMonacoWorkers } from "../chunks/editor-mNznodJN.js";
|
|
2
2
|
export { Editor, EditorField, FormCodePanel, defineEditorThemes, getThemeName, setupMonacoWorkers };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/** Pure helpers behind the gitlab/ uses: completion, kept out of Monaco.tsx so they can
|
|
2
|
+
* be tested without loading the editor. */
|
|
3
|
+
export interface MonacoRange {
|
|
4
|
+
startLineNumber: number;
|
|
5
|
+
startColumn: number;
|
|
6
|
+
endLineNumber: number;
|
|
7
|
+
endColumn: number;
|
|
8
|
+
}
|
|
9
|
+
interface LineReader {
|
|
10
|
+
getLineContent: (line: number) => string;
|
|
11
|
+
}
|
|
12
|
+
/** Splits GROUP/PROJECT@REF; the ref is null when no @ has been typed yet. */
|
|
13
|
+
export declare function splitAtRef(typed: string): [string, string | null];
|
|
14
|
+
/** What to insert for a chosen project, given what is already typed. A typed prefix is
|
|
15
|
+
* completed like the github one; a mid-path match has to replace what is there. */
|
|
16
|
+
export declare function projectInsert(typed: string, path: string): {
|
|
17
|
+
text: string;
|
|
18
|
+
replace: boolean;
|
|
19
|
+
};
|
|
20
|
+
/** The $host the step pins, or undefined. Quotes and a trailing comment are stripped,
|
|
21
|
+
* so a pinned host is recognised however it was written. */
|
|
22
|
+
export declare function stepHostAbove(model: LineReader, usesLine: number, maxLines?: number): string | undefined;
|
|
23
|
+
/** A YAML scalar as written: quotes removed, a trailing comment dropped. */
|
|
24
|
+
export declare function unquoteScalar(raw: string): string;
|
|
25
|
+
/** A missing $host already means gitlab.com, so naming it adds nothing. */
|
|
26
|
+
export declare const DEFAULT_GITLAB_HOST = "gitlab.com";
|
|
27
|
+
/** How to add `$host` to a step. Always an edit of its own: monaco re-indents a
|
|
28
|
+
* multi-line insertText, and the cursor has to stay after the project. */
|
|
29
|
+
export declare function hostCompletion(model: LineReader & {
|
|
30
|
+
getLineCount: () => number;
|
|
31
|
+
}, usesLine: number, host: string): {
|
|
32
|
+
appendText: string;
|
|
33
|
+
edits: {
|
|
34
|
+
range: MonacoRange;
|
|
35
|
+
text: string;
|
|
36
|
+
}[];
|
|
37
|
+
};
|
|
38
|
+
/** A trailing slash or @ after a known project is the ref separator, the way a trailing
|
|
39
|
+
* slash is for github/OWNER/REPO. The slash is swapped for @ when a ref is accepted. */
|
|
40
|
+
export declare function refTrigger(typed: string, knownPaths: string[]): {
|
|
41
|
+
projectPath: string;
|
|
42
|
+
replaceSlash: boolean;
|
|
43
|
+
} | undefined;
|
|
44
|
+
/** What to ask the platform for when completing a project path: everything before the
|
|
45
|
+
* first slash. Typing deeper then filters that answer instead of asking again. */
|
|
46
|
+
export declare function projectSearchKey(typed: string): string;
|
|
47
|
+
/** Where a `$yaml:`/`$thumbnail:` value's files come from: the nearest `uses:` above,
|
|
48
|
+
* addressed the way the platform's file endpoint expects. A gitlab/ path carries no
|
|
49
|
+
* host, so the step's $host supplies it. */
|
|
50
|
+
export declare function usesFileSource(model: LineReader, fromLine: number, maxLines?: number): {
|
|
51
|
+
repo: string;
|
|
52
|
+
ref: string;
|
|
53
|
+
} | undefined;
|
|
54
|
+
/** One entry of a repository tree. */
|
|
55
|
+
export interface RepoTreeEntry {
|
|
56
|
+
path: string;
|
|
57
|
+
type: 'tree' | 'blob';
|
|
58
|
+
}
|
|
59
|
+
declare const FILE_VALUE_KINDS: {
|
|
60
|
+
$yaml: {
|
|
61
|
+
detail: string;
|
|
62
|
+
keep: (path: string) => boolean;
|
|
63
|
+
};
|
|
64
|
+
$thumbnail: {
|
|
65
|
+
detail: string;
|
|
66
|
+
keep: (path: string) => boolean;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
/** Which of the two file-valued keys the cursor sits on, if either. */
|
|
70
|
+
export declare function fileValueKey(textUntilCursor: string): keyof typeof FILE_VALUE_KINDS | undefined;
|
|
71
|
+
/** The files worth offering for a `$yaml:`/`$thumbnail:` value, from whichever
|
|
72
|
+
* repository the step's `uses:` names. Empty when the line is not one of those keys,
|
|
73
|
+
* no `uses:` is above it, or the tree cannot be read. */
|
|
74
|
+
export declare function fileValueSuggestions(model: LineReader, line: number, textUntilCursor: string, loadTree: (repo: string, ref: string) => Promise<RepoTreeEntry[]>): Promise<{
|
|
75
|
+
path: string;
|
|
76
|
+
detail: string;
|
|
77
|
+
}[]>;
|
|
78
|
+
/** Every $-prefixed key a step already sets, mapped to its value. Nothing already
|
|
79
|
+
* written is offered a second time, and an existing one must not suppress the rest. */
|
|
80
|
+
export declare function specialKeysNearUses(model: LineReader & {
|
|
81
|
+
getLineCount: () => number;
|
|
82
|
+
}, usesLineNumber: number): Map<string, string>;
|
|
83
|
+
/** The same, for a with: block located by its own indentation. */
|
|
84
|
+
export declare function specialKeysInWithBlock(model: LineReader & {
|
|
85
|
+
getLineCount: () => number;
|
|
86
|
+
}, withLineNumber: number): Map<string, string>;
|
|
87
|
+
/** Text to be inserted as a snippet, with the characters monaco's snippet parser would
|
|
88
|
+
* otherwise read as placeholders escaped. */
|
|
89
|
+
export declare function snippetLiteral(text: string): string;
|
|
90
|
+
/** How a completion inserts its text when a `$host` block has to travel with it. The
|
|
91
|
+
* cursor stop is what keeps the caret on the uses: line rather than after the block;
|
|
92
|
+
* without a block there is nothing to escape and it stays a plain insert. */
|
|
93
|
+
export declare function completionInsert(text: string, appendText: string): {
|
|
94
|
+
insertText: string;
|
|
95
|
+
isSnippet: boolean;
|
|
96
|
+
};
|
|
97
|
+
export {};
|