@parallelworks/ui 0.16.0 → 0.18.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.
Files changed (59) hide show
  1. package/LICENSE +202 -21
  2. package/dist/chunks/{CodePreviewEditor-DuMZMA_k.js → CodePreviewEditor-4lx3kE-w.js} +2 -2
  3. package/dist/chunks/{CreateModal-B7smkHkv.js → CreateModal-BY6DLmur.js} +11 -11
  4. package/dist/chunks/{lib-CUTRDpa6.js → FieldWrapper-Bef01EWO.js} +49 -48
  5. package/dist/chunks/{Monaco-BXDH2jVj.js → Monaco-C1POOdVv.js} +485 -172
  6. package/dist/chunks/{PageHeader-CoZWF0pJ.js → PageHeader-l49p1nM9.js} +145 -82
  7. package/dist/chunks/{Provider-CbFVFB7O.js → Provider-Ghua9PkN.js} +34 -6
  8. package/dist/chunks/{StatusBadge-zvEtK16a.js → StatusBadge-C1Blx_cW.js} +23 -6
  9. package/dist/chunks/{SwitchToggle-3V836Cn7.js → SwitchToggle-CHPP8uZQ.js} +17 -14
  10. package/dist/chunks/{Table-CutP1bRt.js → Table-4OHjdKdO.js} +2 -2
  11. package/dist/chunks/{Tooltip-n9w7Tkt1.js → Tooltip-Crxo_eU_.js} +3 -3
  12. package/dist/chunks/{editor-Ko3zX22Y.js → editor-9_uVtUyy.js} +4 -4
  13. package/dist/chunks/{list-CduoUsMW.js → list-qrojZZNv.js} +146 -33
  14. package/dist/components/AddMenu.d.ts +1 -0
  15. package/dist/components/Avatar.d.ts +13 -0
  16. package/dist/components/Button.d.ts +2 -1
  17. package/dist/components/Dropdown.d.ts +2 -0
  18. package/dist/components/Provider.d.ts +59 -2
  19. package/dist/components/SectionHeader.d.ts +3 -1
  20. package/dist/components/StatusBadge.d.ts +7 -2
  21. package/dist/components/relativeTime.d.ts +15 -0
  22. package/dist/editor/index.js +1 -1
  23. package/dist/editor/usesGitlab.d.ts +97 -0
  24. package/dist/file-explorer/index.js +275 -274
  25. package/dist/file-explorer/lib/paging.d.ts +27 -28
  26. package/dist/file-explorer/lib/types.d.ts +0 -1
  27. package/dist/file-explorer/lib/utils.d.ts +16 -5
  28. package/dist/form/FieldWrapper.d.ts +2 -1
  29. package/dist/form/Form.d.ts +3 -1
  30. package/dist/form/index.js +23 -21
  31. package/dist/form/types/fieldComponentTypes.d.ts +1 -0
  32. package/dist/form/types/fieldTypes.d.ts +4 -4
  33. package/dist/form/types/workflow-schema.d.ts +38 -0
  34. package/dist/form/useParsedOpts.d.ts +1 -1
  35. package/dist/icons.d.ts +1 -1
  36. package/dist/icons.js +3 -3
  37. package/dist/index.d.ts +5 -2
  38. package/dist/index.js +49 -18
  39. package/dist/list/ListPage.d.ts +3 -1
  40. package/dist/list/ListPager.d.ts +3 -1
  41. package/dist/list/ListTable.d.ts +2 -1
  42. package/dist/list/RowContextMenu.d.ts +9 -9
  43. package/dist/list/UserHoverCard.d.ts +45 -0
  44. package/dist/list/index.d.ts +2 -1
  45. package/dist/list/index.js +3 -3
  46. package/dist/list/listView.d.ts +1 -1
  47. package/dist/logviewer/index.js +7 -7
  48. package/dist/styles/Geist-Variable.woff2 +0 -0
  49. package/dist/styles/GeistMono-Variable.woff2 +0 -0
  50. package/dist/styles/base.css +57 -0
  51. package/dist/styles/fonts.css +15 -0
  52. package/dist/styles/theme.css +6 -40
  53. package/dist/styles.css +1 -1
  54. package/dist/workflow/DependencyGraph.d.ts +2 -0
  55. package/dist/workflow/index.d.ts +1 -0
  56. package/dist/workflow/index.js +10 -9
  57. package/dist/workflow/types.d.ts +5 -0
  58. package/package.json +12 -11
  59. package/dist/chunks/{BareModal-BqroLbZ5.js → BareModal-BtRiQezg.js} +1 -1
@@ -1,14 +1,14 @@
1
+ import { a as usePersistedState, n as useLink, r as useNavigation, u as useStrings } from "./Provider-Ghua9PkN.js";
2
+ import { T as Avatar, _ as useRowMenuActive, c as PageHeader, f as rowContextMenuProps, u as MoreButton } from "./PageHeader-l49p1nM9.js";
1
3
  import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, DisplayIcon, ErrorIcon, FilterIcon, MenuIcon, SearchIcon, XIcon } from "../icons.js";
2
- import { c as useStrings, n as useLink, r as useNavigation } from "./Provider-CbFVFB7O.js";
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-CoZWF0pJ.js";
5
- import { n as Table } from "./Table-CutP1bRt.js";
4
+ import { n as TOOLTIP_ID, r as TooltipInfo } from "./Tooltip-Crxo_eU_.js";
5
+ import { n as Table } from "./Table-4OHjdKdO.js";
6
6
  import cx from "classnames";
7
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
- import { Children, Fragment as Fragment$1, cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
7
+ import { Children, Fragment, cloneElement, createContext, isValidElement, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
8
+ import { useLocalStorage } from "usehooks-ts";
9
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
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. */
@@ -20,7 +20,7 @@ function tipProps(content, place) {
20
20
  };
21
21
  }
22
22
  function ToolbarItemView({ item }) {
23
- if ("element" in item) return /* @__PURE__ */ jsx(Fragment, { children: item.element });
23
+ if ("element" in item) return /* @__PURE__ */ jsx(Fragment$1, { children: item.element });
24
24
  return /* @__PURE__ */ jsx(ToolbarButton, { action: item });
25
25
  }
26
26
  function ToolbarButton({ action }) {
@@ -30,7 +30,7 @@ function ToolbarButton({ action }) {
30
30
  const className = cx("inline-flex items-center gap-x-0.5 h-9 px-2 rounded-md text-[13.5px] uppercase font-semibold transition-colors whitespace-nowrap", "bg-(--theme-element) text-(--theme-element-text)", action.disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer hover:bg-(--theme-element-hover)");
31
31
  const tooltipProps = tipProps(action.disabled ? action.disabledTooltip : action.enabledTooltip, "bottom");
32
32
  const Icon = action.icon;
33
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
33
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
34
34
  className: "h-6 flex items-center",
35
35
  children: /* @__PURE__ */ jsx(Icon, { className: cx("w-auto", action.iconClassName) })
36
36
  }), action.label] });
@@ -64,7 +64,7 @@ function OverflowRow({ item }) {
64
64
  const Icon = item.icon;
65
65
  const className = cx("flex w-full items-center gap-x-1 px-4 py-2 text-[13px] uppercase font-semibold link hover:theme-hover", item.disabled && "opacity-50 cursor-not-allowed");
66
66
  const tooltipProps = tipProps(item.disabled ? item.disabledTooltip : item.enabledTooltip, "left");
67
- const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
67
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
68
68
  className: "w-5 h-6 flex items-center",
69
69
  children: /* @__PURE__ */ jsx(Icon, { className: cx(item.iconClassName) })
70
70
  }), item.label] });
@@ -106,7 +106,7 @@ function OverflowMenu({ items }) {
106
106
  className: "z-50 min-w-56 rounded-md border theme-border bg-(--theme-app-bg) shadow-lg py-1 focus:outline-none",
107
107
  children: /* @__PURE__ */ jsx(OverflowMenuContext.Provider, {
108
108
  value: true,
109
- children: items.map((item) => "element" in item ? /* @__PURE__ */ jsx(Fragment$1, { children: item.element }, item.key) : /* @__PURE__ */ jsx(OverflowRow, { item }, item.key))
109
+ children: items.map((item) => "element" in item ? /* @__PURE__ */ jsx(Fragment, { children: item.element }, item.key) : /* @__PURE__ */ jsx(OverflowRow, { item }, item.key))
110
110
  })
111
111
  })]
112
112
  });
@@ -177,7 +177,7 @@ function ActionToolbar({ groups, right = [], sticky = false }) {
177
177
  className: cx("flex items-center gap-2.5", sticky && cx("sticky top-10 z-10 bg-(--theme-app-bg)", "py-1.5")),
178
178
  children: [visibleGroups.length > 0 && /* @__PURE__ */ jsx("div", {
179
179
  className: "flex items-center gap-2.5 shrink-0",
180
- children: visibleGroups.map((group, i) => /* @__PURE__ */ jsxs(Fragment$1, { children: [i > 0 && /* @__PURE__ */ jsx("div", { className: "w-px h-5 bg-(--theme-border)" }), /* @__PURE__ */ jsx("div", {
180
+ children: visibleGroups.map((group, i) => /* @__PURE__ */ jsxs(Fragment, { children: [i > 0 && /* @__PURE__ */ jsx("div", { className: "w-px h-5 bg-(--theme-border)" }), /* @__PURE__ */ jsx("div", {
181
181
  className: "flex items-center gap-1",
182
182
  children: group.map((item) => /* @__PURE__ */ jsx(ToolbarItemView, { item }, item.key))
183
183
  })] }, group[0]?.key ?? i))
@@ -203,14 +203,15 @@ 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
- const inner = /* @__PURE__ */ jsxs(Fragment, { children: [icon, /* @__PURE__ */ jsxs("div", {
208
+ const inner = /* @__PURE__ */ jsxs(Fragment$1, { children: [icon, /* @__PURE__ */ jsxs("div", {
209
209
  className: "min-w-0",
210
210
  children: [/* @__PURE__ */ jsxs("div", {
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", {
@@ -331,7 +332,7 @@ function ListActionsHeader({ view, actionCount = 0, className }) {
331
332
  width,
332
333
  minWidth: width
333
334
  },
334
- className: cx("py-2 text-right @max-[43rem]:w-[52px]! @max-[43rem]:min-w-[52px]!", className),
335
+ className: cx("relative py-2 text-right @max-[43rem]:w-[52px]! @max-[43rem]:min-w-[52px]!", className),
335
336
  children: /* @__PURE__ */ jsx("span", {
336
337
  className: "sr-only",
337
338
  children: "Actions"
@@ -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: [
@@ -460,7 +462,7 @@ function ListRow({ href, items, goTo, openMenu, pinnedActions, selected, selecti
460
462
  }
461
463
  /** Name column min-width is by role, not position, so a leading checkbox doesn't shift it. */
462
464
  function ListColumns({ view }) {
463
- return /* @__PURE__ */ jsxs(Fragment, { children: [view.visibleColumns.map((col, i) => /* @__PURE__ */ jsx(Table.Header, {
465
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [view.visibleColumns.map((col, i) => /* @__PURE__ */ jsx(Table.Header, {
464
466
  caps: false,
465
467
  className: cx(col.headerClassName, view.columnClasses[i]),
466
468
  style: col.alwaysVisible ? { minWidth: 200 } : void 0,
@@ -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] = useLocalStorage(`${storageKey}:hiddenColumns`, defaultHiddenKeys);
522
- const [pinnedKeys, setPinnedKeys] = useLocalStorage(`${storageKey}:pinnedActions`, defaultPinnedKeys);
523
- const [groupBy, setGroupBy] = useLocalStorage(`${storageKey}:groupBy`, defaultGroupByValue);
524
- const [showColumnHeaders, setShowColumnHeaders] = useLocalStorage(`${storageKey}:showColumnHeaders`, defaultShowColumnHeaders);
525
- const [showActions, setShowActions] = useLocalStorage(`${storageKey}:showActions`, defaultShowActions);
526
- const [alwaysShowActions, setAlwaysShowActions] = useLocalStorage(`${storageKey}:alwaysShowActions`, false);
527
- const [orderBy, setOrderBy] = useLocalStorage(`${storageKey}:orderBy`, defaultOrderByValue);
528
- const [orderDir, setOrderDir] = useLocalStorage(`${storageKey}:orderDir`, "asc");
529
- const [localFilters, setLocalFilters] = useLocalStorage(`${storageKey}:filters`, {});
530
- const [sharedFilters, setSharedFilters] = useLocalStorage(`${sharedFiltersKey ?? storageKey}:sharedFilters`, {});
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;
@@ -741,7 +743,7 @@ function AnchoredPopover({ icon, label, active, shortcut, panelClassName, childr
741
743
  window.addEventListener("keydown", onKey);
742
744
  return () => window.removeEventListener("keydown", onKey);
743
745
  }, [shortcut, open]);
744
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("button", {
746
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("button", {
745
747
  ref: btnRef,
746
748
  type: "button",
747
749
  onClick: () => pos ? close() : open(),
@@ -984,7 +986,7 @@ function FilterMenuBody({ view }) {
984
986
  const scheduleClose = () => {
985
987
  closeTimer.current = setTimeout(() => setOpenKey(null), 160);
986
988
  };
987
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("div", {
989
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
988
990
  className: "flex items-center justify-between px-3 py-1",
989
991
  children: [/* @__PURE__ */ jsx("span", {
990
992
  className: "text-[11px] font-medium uppercase tracking-wide text-(--theme-muted-text-color)",
@@ -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" })
@@ -1295,4 +1297,115 @@ function HoverCardSurface({ x, y, widthClassName, padded = true, relayoutKey, on
1295
1297
  }), document.querySelector(".ds-root") ?? document.body);
1296
1298
  }
1297
1299
  //#endregion
1298
- export { ActionToolbar as A, ListSkeleton as C, actionsColumnWidth as D, RowSelectCheckbox as E, stickyTabsClasses as M, stickyTabsUnderHeaderClasses as N, listTableFixedProps as O, stickyToolbarClasses as P, ListError as S, NameCell as T, ListRow as _, ListCount as a, GroupRow as b, ListDisplayMenu as c, isTextEntryContext as d, DEFAULT_ORDER as f, ListPage as g, ListColumns as h, ListPager as i, ToolbarButton as j, listTableProps as k, ListFilterMenu as l, useListView as m, useHoverCard as n, ListSearchControl as o, FAVORITE_ORDER as p, LIST_PAGE_SIZES as r, useListSearch as s, HoverCardSurface as t, listControlButtonClasses as u, ListRowActionsProvider as v, NAME_MIN_WIDTH_PX as w, ListActionsHeader as x, useListNavigate as y };
1300
+ //#region src/list/UserHoverCard.tsx
1301
+ /** Opens `card` after a short hover or on focus of `children`. */
1302
+ function HoverCardTrigger({ className, children, card }) {
1303
+ const { triggerRef, coords, scheduleOpen, scheduleClose, keepOpen } = useHoverCard();
1304
+ return /* @__PURE__ */ jsxs("span", {
1305
+ ref: triggerRef,
1306
+ role: "none",
1307
+ className,
1308
+ onMouseEnter: scheduleOpen,
1309
+ onMouseLeave: scheduleClose,
1310
+ onFocus: scheduleOpen,
1311
+ onBlur: scheduleClose,
1312
+ children: [children, coords && card({
1313
+ x: coords.x,
1314
+ y: coords.y,
1315
+ onKeepOpen: keepOpen,
1316
+ onLeave: scheduleClose
1317
+ })]
1318
+ });
1319
+ }
1320
+ function UserHoverCard({ x, y, onKeepOpen, onLeave, username, name, avatarSrc, badge, href, linkComponent, loading = false, relayoutKey, children }) {
1321
+ const strings = useStrings();
1322
+ const SlotLink = useLink();
1323
+ const Link = linkComponent ?? SlotLink;
1324
+ const displayName = name || username;
1325
+ const header = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Avatar, {
1326
+ src: avatarSrc,
1327
+ name: displayName,
1328
+ size: "lg",
1329
+ className: "shrink-0"
1330
+ }), /* @__PURE__ */ jsxs("div", {
1331
+ className: "min-w-0",
1332
+ children: [/* @__PURE__ */ jsxs("div", {
1333
+ className: "flex items-center gap-1.5",
1334
+ children: [/* @__PURE__ */ jsx("span", {
1335
+ className: "truncate text-sm font-semibold text-(--theme-app)",
1336
+ children: displayName
1337
+ }), badge && /* @__PURE__ */ jsx("span", {
1338
+ className: "shrink-0 rounded-full bg-(--theme-muted-panel-bg) px-1.5 py-0.5 text-[10px] font-medium leading-none text-(--theme-muted-text-color)",
1339
+ children: badge
1340
+ })]
1341
+ }), /* @__PURE__ */ jsx("div", {
1342
+ className: "truncate text-xs text-(--theme-muted-text-color)",
1343
+ children: username
1344
+ })]
1345
+ })] });
1346
+ return /* @__PURE__ */ jsxs(HoverCardSurface, {
1347
+ x,
1348
+ y,
1349
+ widthClassName: "w-64",
1350
+ relayoutKey,
1351
+ onKeepOpen,
1352
+ onLeave,
1353
+ children: [href ? /* @__PURE__ */ jsx(Link, {
1354
+ to: href,
1355
+ className: "flex items-center gap-3 transition-opacity hover:opacity-80",
1356
+ children: header
1357
+ }) : /* @__PURE__ */ jsx("div", {
1358
+ className: "flex items-center gap-3",
1359
+ children: header
1360
+ }), loading ? /* @__PURE__ */ jsxs("div", {
1361
+ role: "status",
1362
+ "aria-label": strings.loading,
1363
+ className: "mt-3 space-y-2 border-t border-(--theme-border) pt-3",
1364
+ children: [
1365
+ /* @__PURE__ */ jsx("div", {
1366
+ "aria-hidden": "true",
1367
+ className: "h-3 w-24 animate-pulse rounded theme-muted-panel"
1368
+ }),
1369
+ /* @__PURE__ */ jsx("div", {
1370
+ "aria-hidden": "true",
1371
+ className: "h-3 w-36 animate-pulse rounded theme-muted-panel"
1372
+ }),
1373
+ /* @__PURE__ */ jsx("span", {
1374
+ className: "sr-only",
1375
+ children: strings.loading
1376
+ })
1377
+ ]
1378
+ }) : children ? /* @__PURE__ */ jsx("div", {
1379
+ className: "mt-3 space-y-2 border-t border-(--theme-border) pt-3 text-[13px]",
1380
+ children
1381
+ }) : null]
1382
+ });
1383
+ }
1384
+ var rowClasses = "flex gap-2 text-(--theme-muted-text-color)";
1385
+ /** One detail line of a hover card: a leading icon and its content. With
1386
+ * `onClick` the line is a button. */
1387
+ function HoverCardRow({ icon, children, title, align = "center", truncate = false, onClick }) {
1388
+ const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
1389
+ "aria-hidden": "true",
1390
+ className: cx("flex shrink-0 items-center [&>svg]:h-3.5 [&>svg]:w-3.5", align === "start" && "mt-0.5"),
1391
+ children: icon
1392
+ }), /* @__PURE__ */ jsx("span", {
1393
+ className: cx("min-w-0", truncate && "truncate"),
1394
+ children
1395
+ })] });
1396
+ const alignClass = align === "start" ? "items-start" : "items-center";
1397
+ if (onClick) return /* @__PURE__ */ jsx("button", {
1398
+ type: "button",
1399
+ title,
1400
+ onClick,
1401
+ className: cx(rowClasses, alignClass, "-mx-1 max-w-full rounded px-1 py-0.5 text-left transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app)"),
1402
+ children: content
1403
+ });
1404
+ return /* @__PURE__ */ jsx("div", {
1405
+ title,
1406
+ className: cx(rowClasses, alignClass),
1407
+ children: content
1408
+ });
1409
+ }
1410
+ //#endregion
1411
+ export { actionsColumnWidth as A, GroupRow as C, NAME_MIN_WIDTH_PX as D, ListSkeleton as E, stickyTabsClasses as F, stickyTabsUnderHeaderClasses as I, stickyToolbarClasses as L, listTableProps as M, ActionToolbar as N, NameCell as O, ToolbarButton as P, useListNavigate as S, ListError as T, useListView as _, useHoverCard as a, ListRow as b, ListCount as c, ListDisplayMenu as d, ListFilterMenu as f, FAVORITE_ORDER as g, DEFAULT_ORDER as h, HoverCardSurface as i, listTableFixedProps as j, RowSelectCheckbox as k, ListSearchControl as l, isTextEntryContext as m, HoverCardTrigger as n, LIST_PAGE_SIZES as o, listControlButtonClasses as p, UserHoverCard as r, ListPager as s, HoverCardRow as t, useListSearch as u, ListColumns as v, ListActionsHeader as w, ListRowActionsProvider as x, ListPage as y };
@@ -5,6 +5,7 @@ export interface HeaderAddMenuItem {
5
5
  to?: string;
6
6
  onSelect?: () => void;
7
7
  disabled?: boolean;
8
+ hint?: string | undefined;
8
9
  separatorAbove?: boolean;
9
10
  }
10
11
  export declare function AddMenu({ items, label, buttonClassName, zClassName, children, }: {
@@ -0,0 +1,13 @@
1
+ export type AvatarSize = 'sm' | 'md' | 'lg' | 'xl' | '2xl';
2
+ export type AvatarStatus = 'online' | 'offline' | 'busy' | 'away';
3
+ export interface AvatarProps {
4
+ /** Image URL; absent or failing to load shows the initials of `name`. */
5
+ src?: string | null | undefined;
6
+ name?: string | undefined;
7
+ size?: AvatarSize | undefined;
8
+ status?: AvatarStatus | undefined;
9
+ className?: string | undefined;
10
+ }
11
+ /** Up to two initials: the first and last word of a name split on spaces and dots. */
12
+ export declare function avatarInitials(name: string | undefined): string;
13
+ export declare function Avatar({ src, name, size, status, className, }: AvatarProps): import("react").JSX.Element;
@@ -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 {};
@@ -24,5 +24,7 @@ export interface IProps {
24
24
  /** Label for the toggle row hiding a category's collapsible options. */
25
25
  collapsedLabel?: (count: number) => string;
26
26
  portalEl?: HTMLElement;
27
+ /** Smaller option rows and text, for dense toolbars. */
28
+ compact?: boolean | undefined;
27
29
  }
28
30
  export default function Dropdown(props: IProps): import("react").JSX.Element;
@@ -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;
@@ -54,6 +55,7 @@ export interface UIStrings {
54
55
  create: string;
55
56
  remove: string;
56
57
  sort: string;
58
+ userAvatar: string;
57
59
  };
58
60
  dropdown: {
59
61
  select: (type: string) => string;
@@ -321,7 +323,15 @@ export interface UIStrings {
321
323
  readOnlyDescription: string;
322
324
  readOnlyForced: string;
323
325
  };
326
+ time: {
327
+ now: string;
328
+ minutes: (n: number) => string;
329
+ hours: (n: number) => string;
330
+ days: (n: number) => string;
331
+ years: (n: number) => string;
332
+ };
324
333
  list: {
334
+ copyMenu: string;
325
335
  copy: (label: string) => string;
326
336
  copied: (label: string) => string;
327
337
  couldntCopy: (label: string) => string;
@@ -380,6 +390,7 @@ export interface RunFileResult {
380
390
  isLoading: boolean;
381
391
  error: unknown;
382
392
  }
393
+ export type PersistedStateHook = <T>(key: string, defaultValue: T) => [T, Dispatch<SetStateAction<T>>];
383
394
  export interface UIData {
384
395
  /**
385
396
  * Host-bound hook resolving a file in a run's artifact tree (logs, scripts).
@@ -406,6 +417,49 @@ export interface UIData {
406
417
  }) => Promise<{
407
418
  error?: string;
408
419
  }>;
420
+ usePersistedState: PersistedStateHook;
421
+ /**
422
+ * Repository completions for `uses:`, bound by the host because the credential
423
+ * lives on the platform. Absent, those completions no-op.
424
+ */
425
+ repoSuggestions?: {
426
+ refs: (repo: string) => Promise<RepoRef[]>;
427
+ files: (repo: string, ref: string, path: string) => Promise<RepoEntry[]>;
428
+ file: (repo: string, ref: string, path: string) => Promise<string>;
429
+ repos: (owner: string) => Promise<RepoSummary[]>;
430
+ gitlabProjects: (search: string, host?: string) => Promise<GitlabProject[]>;
431
+ owners: (provider: 'github' | 'gitlab', search: string, host?: string) => Promise<RepoOwner[]>;
432
+ };
433
+ }
434
+ /** One branch, tag or commit a `uses:` reference can name. */
435
+ export interface RepoRef {
436
+ name: string;
437
+ kind: string;
438
+ detail: string;
439
+ }
440
+ /** One repository in an owner listing. */
441
+ export interface RepoSummary {
442
+ name: string;
443
+ description: string;
444
+ private: boolean;
445
+ }
446
+ /** One account, organization or group a repository can live under. */
447
+ export interface RepoOwner {
448
+ name: string;
449
+ kind: string;
450
+ host?: string | undefined;
451
+ }
452
+ /** One GitLab project, with the server a $host must name to reach it. */
453
+ export interface GitlabProject {
454
+ path: string;
455
+ host: string;
456
+ description: string;
457
+ private: boolean;
458
+ }
459
+ /** One entry in a repository directory listing. */
460
+ export interface RepoEntry {
461
+ path: string;
462
+ type: 'blob' | 'tree';
409
463
  }
410
464
  import type { FieldComponent } from '../form/fieldRegistry.js';
411
465
  import type { PageHeaderCrumb } from './PageHeader.js';
@@ -431,11 +485,12 @@ export interface UISlots {
431
485
  * renders fields inside a Suspense boundary.
432
486
  */
433
487
  formFields?: Record<string, FieldComponent>;
434
- /** Sessions block for a run's sidebar; absent hides the section. */
488
+ /** Links and sessions block for a run's sidebar; absent hides the section. */
435
489
  runSessions?: (args: {
436
490
  runId: string;
437
491
  runNumber: number;
438
492
  runActive: boolean;
493
+ links?: Record<string, RunLink> | undefined;
439
494
  }) => ReactNode;
440
495
  /** Rich read-only YAML viewer; absent falls back to a plain <pre>. */
441
496
  yamlViewer?: (args: {
@@ -488,9 +543,11 @@ export declare function useNavigation(): UINavigation;
488
543
  export declare function useSlots(): UISlots;
489
544
  export declare function useWorkflowJsonResolver(): ((ref: WorkflowJsonRef) => Promise<unknown>) | undefined;
490
545
  export declare function useProvisionCorsRules(): UIData['provisionCorsRules'];
546
+ export declare function useRepoSuggestions(): UIData['repoSuggestions'];
491
547
  export declare function useRunFile(slug: string, path: string | null, options?: {
492
548
  refreshInterval?: number;
493
549
  }): RunFileResult;
550
+ export declare function usePersistedState<T>(key: string, defaultValue: T): [T, Dispatch<SetStateAction<T>>];
494
551
  export declare function UIProvider({ notify, strings, navigation, data, slots, children, }: {
495
552
  notify?: Partial<UINotify>;
496
553
  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
- export declare function StatusBadge({ children, variant, pulse, size, className, }: StatusBadgeProps): import("react").JSX.Element;
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
+ };
@@ -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-Ko3zX22Y.js";
1
+ import { a as EditorField, i as FormCodePanel, n as defineEditorThemes, o as Editor, r as getThemeName, t as setupMonacoWorkers } from "../chunks/editor-9_uVtUyy.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 {};