@parallelworks/ui 0.51.0 → 0.53.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 (250) hide show
  1. package/README.md +197 -17
  2. package/dist/ai/agent/toolStrings.d.ts +2 -0
  3. package/dist/ai/agent/toolStrings.js +20 -18
  4. package/dist/ai/ai.css +17 -3
  5. package/dist/ai/components/AllocationSelector.js +5 -1
  6. package/dist/ai/components/AttachmentManager.js +51 -42
  7. package/dist/ai/components/AttachmentUpload.d.ts +4 -2
  8. package/dist/ai/components/AttachmentUpload.js +9 -9
  9. package/dist/ai/components/BlockedGroupBanner.js +8 -14
  10. package/dist/ai/components/BranchNavigator.js +11 -12
  11. package/dist/ai/components/ChatEmptyState.js +12 -19
  12. package/dist/ai/components/ChatInput.d.ts +4 -3
  13. package/dist/ai/components/ChatInput.js +27 -13
  14. package/dist/ai/components/ChatMessage.js +45 -121
  15. package/dist/ai/components/ChatMessageList.d.ts +5 -2
  16. package/dist/ai/components/ChatMessageList.js +89 -51
  17. package/dist/ai/components/ChatSidebar.js +11 -7
  18. package/dist/ai/components/ChatThread.js +1 -13
  19. package/dist/ai/components/DragOverlay.js +3 -3
  20. package/dist/ai/components/MessageAttachments.d.ts +10 -0
  21. package/dist/ai/components/MessageAttachments.js +78 -0
  22. package/dist/ai/components/ModelSelector.js +3 -3
  23. package/dist/ai/components/PastedText.js +2 -2
  24. package/dist/ai/components/ProviderIssueBanner.js +19 -23
  25. package/dist/ai/components/ShareDialog.js +2 -2
  26. package/dist/ai/components/agent/ActivityLine.d.ts +0 -2
  27. package/dist/ai/components/agent/ActivityLine.js +8 -51
  28. package/dist/ai/components/agent/AgentMessageParts.js +114 -53
  29. package/dist/ai/core/ChatProvider.d.ts +16 -5
  30. package/dist/ai/core/ChatProvider.js +41 -8
  31. package/dist/ai/core/config.js +4 -3
  32. package/dist/ai/index.d.ts +1 -1
  33. package/dist/ai/index.js +3 -3
  34. package/dist/ai/strings.d.ts +29 -3
  35. package/dist/ai/strings.js +80 -26
  36. package/dist/ai/types.d.ts +4 -0
  37. package/dist/ai/ui/Notice.d.ts +2 -2
  38. package/dist/ai/ui/Notice.js +29 -62
  39. package/dist/ai/ui/sidebar.d.ts +5 -3
  40. package/dist/ai/ui/sidebar.js +22 -10
  41. package/dist/components/ActionMenu.d.ts +1 -0
  42. package/dist/components/ActionMenu.js +6 -1
  43. package/dist/components/AppLink.d.ts +26 -0
  44. package/dist/components/AppLink.js +51 -0
  45. package/dist/components/Avatar.d.ts +2 -1
  46. package/dist/components/Avatar.js +4 -2
  47. package/dist/components/Button.d.ts +15 -5
  48. package/dist/components/Button.js +47 -40
  49. package/dist/components/Callout.d.ts +31 -7
  50. package/dist/components/Callout.js +50 -19
  51. package/dist/components/Card.d.ts +11 -6
  52. package/dist/components/Card.js +14 -8
  53. package/dist/components/Checkbox.js +2 -1
  54. package/dist/components/ColorInput.js +5 -3
  55. package/dist/components/Combobox.js +49 -19
  56. package/dist/components/ConfirmModal.d.ts +19 -8
  57. package/dist/components/ConfirmModal.js +31 -53
  58. package/dist/components/CopyButton.d.ts +38 -0
  59. package/dist/components/CopyButton.js +68 -0
  60. package/dist/components/CreateModal.js +118 -111
  61. package/dist/components/DatePicker.js +12 -8
  62. package/dist/components/DescriptionList.d.ts +25 -5
  63. package/dist/components/DescriptionList.js +56 -22
  64. package/dist/components/Disclosure.js +1 -1
  65. package/dist/components/Drawer.js +10 -16
  66. package/dist/components/Field.d.ts +16 -5
  67. package/dist/components/Field.js +63 -8
  68. package/dist/components/IconButton.d.ts +14 -5
  69. package/dist/components/IconButton.js +25 -14
  70. package/dist/components/InfoTip.d.ts +12 -1
  71. package/dist/components/InfoTip.js +13 -2
  72. package/dist/components/Input.d.ts +14 -8
  73. package/dist/components/Input.js +46 -31
  74. package/dist/components/Loader.js +1 -1
  75. package/dist/components/MarkdownImpl.d.ts +1 -0
  76. package/dist/components/MarkdownImpl.js +1 -0
  77. package/dist/components/MenuEntries.d.ts +4 -0
  78. package/dist/components/MenuEntries.js +11 -12
  79. package/dist/components/Meter.js +5 -4
  80. package/dist/components/Modal.d.ts +28 -4
  81. package/dist/components/Modal.js +37 -14
  82. package/dist/components/Overview.d.ts +20 -12
  83. package/dist/components/Overview.js +14 -9
  84. package/dist/components/PageHeader.d.ts +47 -65
  85. package/dist/components/PageHeader.js +275 -153
  86. package/dist/components/PageHeaderAction.d.ts +67 -0
  87. package/dist/components/PageHeaderAction.js +107 -0
  88. package/dist/components/Pagination.js +6 -16
  89. package/dist/components/Popover.d.ts +4 -1
  90. package/dist/components/Popover.js +5 -3
  91. package/dist/components/Provider.d.ts +79 -19
  92. package/dist/components/Provider.js +140 -130
  93. package/dist/components/Radio.js +2 -1
  94. package/dist/components/RefreshButton.js +7 -8
  95. package/dist/components/RequiredMark.js +1 -1
  96. package/dist/components/Section.js +1 -1
  97. package/dist/components/Select.d.ts +4 -9
  98. package/dist/components/Select.js +24 -11
  99. package/dist/components/SettingModeOption.d.ts +1 -1
  100. package/dist/components/SettingModeOption.js +7 -1
  101. package/dist/components/SettingsCard.d.ts +5 -3
  102. package/dist/components/SettingsCard.js +27 -20
  103. package/dist/components/Slider.js +1 -1
  104. package/dist/components/StatusBadge.js +2 -2
  105. package/dist/components/Switch.js +5 -4
  106. package/dist/components/Table.d.ts +30 -20
  107. package/dist/components/Table.js +50 -73
  108. package/dist/components/Tabs.d.ts +3 -7
  109. package/dist/components/Tabs.js +6 -10
  110. package/dist/components/ThemeEditor.js +18 -8
  111. package/dist/components/Toast.d.ts +11 -3
  112. package/dist/components/Toast.js +45 -20
  113. package/dist/components/buttonClasses.js +1 -1
  114. package/dist/components/comboboxItems.d.ts +4 -0
  115. package/dist/components/comboboxItems.js +6 -1
  116. package/dist/components/dismiss.d.ts +12 -0
  117. package/dist/components/dismiss.js +19 -0
  118. package/dist/components/fieldBox.d.ts +4 -0
  119. package/dist/components/fieldBox.js +7 -3
  120. package/dist/components/floating.d.ts +3 -3
  121. package/dist/components/floating.js +1 -1
  122. package/dist/components/focus.d.ts +5 -0
  123. package/dist/components/focus.js +21 -1
  124. package/dist/components/markdownCodeBreaks.js +3 -4
  125. package/dist/components/pageHeaderStore.d.ts +41 -0
  126. package/dist/components/pageHeaderStore.js +67 -0
  127. package/dist/components/pathBreaks.d.ts +8 -0
  128. package/dist/components/pathBreaks.js +28 -0
  129. package/dist/components/segmented.js +1 -1
  130. package/dist/components/shortcutKey.d.ts +2 -1
  131. package/dist/components/shortcutKey.js +19 -2
  132. package/dist/components/splitter.js +1 -1
  133. package/dist/components/useCssIsDark.js +40 -18
  134. package/dist/components/useTheme.js +9 -10
  135. package/dist/dev.d.ts +9 -0
  136. package/dist/dev.js +14 -0
  137. package/dist/editor/Editor.d.ts +3 -2
  138. package/dist/editor/Editor.js +24 -5
  139. package/dist/editor/EditorField.d.ts +2 -1
  140. package/dist/editor/EditorField.js +11 -29
  141. package/dist/editor/FormCodePanel.d.ts +7 -3
  142. package/dist/editor/FormCodePanel.js +14 -7
  143. package/dist/editor/Monaco.d.ts +22 -6
  144. package/dist/editor/Monaco.js +177 -113
  145. package/dist/editor/editorModel.d.ts +1 -1
  146. package/dist/editor/editorModel.js +1 -1
  147. package/dist/editor/index.d.ts +6 -5
  148. package/dist/editor/index.js +5 -4
  149. package/dist/editor/languages.d.ts +24 -0
  150. package/dist/editor/languages.js +31 -0
  151. package/dist/editor/monacoFeatures.d.ts +7 -0
  152. package/dist/editor/monacoFeatures.js +19 -0
  153. package/dist/editor/themes.d.ts +8 -5
  154. package/dist/editor/themes.js +65 -32
  155. package/dist/editor/workers.d.ts +3 -3
  156. package/dist/editor/workers.js +8 -4
  157. package/dist/editor/yaml.d.ts +6 -2
  158. package/dist/editor/yaml.js +11 -2
  159. package/dist/editor/yamlLanguage.js +55 -38
  160. package/dist/file-explorer/FileExplorer.d.ts +18 -4
  161. package/dist/file-explorer/FileExplorer.js +204 -142
  162. package/dist/file-explorer/FileExplorerContext.js +1 -1
  163. package/dist/file-explorer/FilePreview/StreamedBadge.js +3 -1
  164. package/dist/file-explorer/FilePreview/Viewer.js +14 -10
  165. package/dist/file-explorer/FilePreview/index.js +4 -4
  166. package/dist/file-explorer/FileRow.d.ts +6 -2
  167. package/dist/file-explorer/FileRow.js +35 -17
  168. package/dist/file-explorer/FileRowsWindow.d.ts +2 -1
  169. package/dist/file-explorer/FileRowsWindow.js +36 -4
  170. package/dist/file-explorer/FileTable.d.ts +3 -1
  171. package/dist/file-explorer/FileTable.js +6 -3
  172. package/dist/file-explorer/TreeView.d.ts +5 -1
  173. package/dist/file-explorer/TreeView.js +86 -39
  174. package/dist/file-explorer/helpers.d.ts +2 -1
  175. package/dist/file-explorer/helpers.js +24 -23
  176. package/dist/file-explorer/lib/utils.d.ts +3 -0
  177. package/dist/file-explorer/lib/utils.js +6 -1
  178. package/dist/file-explorer/toasts.js +27 -19
  179. package/dist/file-explorer/treeRows.d.ts +6 -0
  180. package/dist/file-explorer/treeRows.js +19 -1
  181. package/dist/file-explorer/useNearEnd.js +3 -6
  182. package/dist/form/Fields/CheckboxGroupField.js +12 -5
  183. package/dist/form/Fields/DurationField.js +1 -1
  184. package/dist/form/Fields/LabelField.js +1 -1
  185. package/dist/form/Fields/ObjectField.d.ts +1 -1
  186. package/dist/form/Fields/ObjectField.js +3 -2
  187. package/dist/form/Fields/StringField.js +16 -29
  188. package/dist/form/Fields/TextAreaField.js +3 -4
  189. package/dist/form/Form.js +3 -8
  190. package/dist/form/Wizard/WizardNavigation.js +0 -3
  191. package/dist/form/Wizard/utils.js +2 -1
  192. package/dist/form/fieldFrame.d.ts +1 -1
  193. package/dist/form/fieldFrame.js +2 -2
  194. package/dist/form/types/fieldComponentTypes.d.ts +2 -1
  195. package/dist/graph/AnnotationBanner.d.ts +1 -2
  196. package/dist/graph/AnnotationBanner.js +30 -32
  197. package/dist/graph/Connectors.js +4 -1
  198. package/dist/graph/DependencyGraph.js +2 -2
  199. package/dist/graph/GraphEditor.js +11 -9
  200. package/dist/graph/GraphEditorDialogs.js +1 -1
  201. package/dist/graph/GraphNode.js +7 -4
  202. package/dist/graph/InputDialog.js +1 -1
  203. package/dist/graph/InputsEditor.js +14 -11
  204. package/dist/graph/MatrixGroup.js +9 -3
  205. package/dist/graph/Reveal.js +18 -10
  206. package/dist/graph/ShortcutsButton.d.ts +11 -2
  207. package/dist/graph/ShortcutsButton.js +14 -3
  208. package/dist/graph/TreeView.js +35 -18
  209. package/dist/graph/WorkflowSettingsDialog.js +1 -1
  210. package/dist/graph/editorFields.js +1 -1
  211. package/dist/graph/editorRows.js +16 -10
  212. package/dist/graph/editorStrings.d.ts +7 -0
  213. package/dist/graph/editorStrings.js +8 -1
  214. package/dist/graph/inputRefs.js +1 -1
  215. package/dist/graph/settingsViews.js +11 -18
  216. package/dist/graph/stepWith.js +1 -1
  217. package/dist/icons.d.ts +2 -0
  218. package/dist/icons.js +4 -2
  219. package/dist/index.d.ts +12 -12
  220. package/dist/index.js +16 -16
  221. package/dist/list/ActionToolbar.d.ts +42 -6
  222. package/dist/list/ActionToolbar.js +298 -101
  223. package/dist/list/ListPage.d.ts +15 -23
  224. package/dist/list/ListPage.js +42 -43
  225. package/dist/list/ListTable.d.ts +37 -7
  226. package/dist/list/ListTable.js +52 -12
  227. package/dist/list/ListViewControls.js +1 -1
  228. package/dist/list/UserHoverCard.js +7 -15
  229. package/dist/list/index.d.ts +2 -2
  230. package/dist/list/index.js +2 -2
  231. package/dist/list/listSearch.js +12 -10
  232. package/dist/list/listView.d.ts +10 -1
  233. package/dist/list/listView.js +15 -1
  234. package/dist/logviewer/LogViewer.d.ts +4 -2
  235. package/dist/logviewer/LogViewer.js +87 -69
  236. package/dist/logviewer/logviewer.css +3 -2
  237. package/dist/reducedMotion.d.ts +5 -0
  238. package/dist/reducedMotion.js +20 -0
  239. package/dist/safeUrl.d.ts +1 -1
  240. package/dist/safeUrl.js +4 -2
  241. package/dist/styles.css +1 -1
  242. package/package.json +5 -2
  243. package/dist/components/Breadcrumbs.d.ts +0 -37
  244. package/dist/components/Breadcrumbs.js +0 -90
  245. package/dist/components/CopyToClipboard.d.ts +0 -17
  246. package/dist/components/CopyToClipboard.js +0 -66
  247. package/dist/components/Sort.d.ts +0 -9
  248. package/dist/components/Sort.js +0 -42
  249. package/dist/file-explorer/FilePreview/CodePreviewEditor.d.ts +0 -6
  250. package/dist/file-explorer/FilePreview/CodePreviewEditor.js +0 -64
@@ -1,28 +1,14 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { Tooltip } from "../components/Tooltip.js";
3
- import { useLink, useNavigation } from "../components/Provider.js";
3
+ import { useNavigation } from "../components/Provider.js";
4
+ import { AppLink, isPlainClick } from "../components/AppLink.js";
4
5
  import { isMenuDivider, isMenuSubmenu, useRunAction, warnIfInert } from "../components/MenuEntries.js";
5
- import { PageHeader } from "../components/PageHeader.js";
6
6
  import { Table } from "../components/Table.js";
7
- import { ListActionsHeader } from "./ListTable.js";
7
+ import { ListActionsHeader, StretchRowLinkContext } from "./ListTable.js";
8
8
  import { MoreButton, rowContextMenuProps, useRowMenuActive } from "./rowMenu.js";
9
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
10
  import { Children, cloneElement, createContext, isValidElement, useContext, useMemo } from "react";
11
11
  //#region src/list/ListPage.tsx
12
- /** Negative offsets (admin/org) cancel the shell's pt-4 so rows don't peek through the gap above the pinned header. */
13
- var SECTION_STICKY_TOP = {
14
- main: "top-0",
15
- kubernetes: "top-0",
16
- admin: "-top-4",
17
- org: "-top-4"
18
- };
19
- /** Whether the shell adds pt-4 padding that the edge-to-edge list must cancel with a negative margin. */
20
- var SECTION_SHELL_PADDED = {
21
- main: false,
22
- kubernetes: false,
23
- admin: true,
24
- org: true
25
- };
26
12
  /** Defaults mean rows outside a ListView fully show actions (no view = show everything). */
27
13
  var RowActionsContext = createContext({
28
14
  show: true,
@@ -50,20 +36,13 @@ function ListRowActionsProvider({ view, children }) {
50
36
  children
51
37
  });
52
38
  }
53
- function ListPage({ section = "main", shellPadded, embedded = false, breadcrumbs, title, titleAction, actions, toolbar, view, children }) {
54
- const cancelShellPadding = shellPadded ?? SECTION_SHELL_PADDED[section];
39
+ /** A list filling the page: its toolbar stays put while only the rows scroll. */
40
+ function ListPage({ bleed = false, toolbar, view, children }) {
55
41
  return /* @__PURE__ */ jsxs("div", {
56
- className: cx("flex h-full min-h-0 flex-col", cancelShellPadding && "-m-4"),
57
- children: [embedded ? toolbar && /* @__PURE__ */ jsx("div", {
58
- className: "px-4 py-1.5",
42
+ className: cx("flex h-full min-h-0 flex-col", bleed && "-m-4"),
43
+ children: [toolbar && /* @__PURE__ */ jsx("div", {
44
+ className: "shrink-0 px-4",
59
45
  children: toolbar
60
- }) : /* @__PURE__ */ jsx(PageHeader, {
61
- stickyTopClassName: SECTION_STICKY_TOP[section],
62
- breadcrumbs,
63
- title,
64
- titleAction,
65
- actions,
66
- toolbar
67
46
  }), /* @__PURE__ */ jsx("div", {
68
47
  className: "relative @container min-h-0 flex-1 overflow-auto",
69
48
  children: /* @__PURE__ */ jsx(ListRowActionsProvider, {
@@ -96,24 +75,29 @@ function ListRow({ href, onActivate, items, goTo, openMenu, pinnedActions, selec
96
75
  });
97
76
  return cloneElement(el, { className: cx(el.props.className, cls) });
98
77
  });
78
+ const stretch = !!href && !selectionActive;
99
79
  const handleRowClick = (e) => {
100
80
  if (e.target.closest("a,button,input,label")) return;
101
81
  if (selectionActive && onRowSelect) {
102
82
  onRowSelect(e);
103
83
  return;
104
84
  }
105
- if (href) goTo(href);
106
- else onActivate?.();
85
+ if (href) {
86
+ if (isPlainClick(e)) goTo(href);
87
+ } else onActivate?.();
107
88
  };
108
89
  const pinnedLabels = new Set((pinnedActions ?? []).map((a) => a.label));
109
90
  const hasOverflowItems = items.some((item) => !isMenuDivider(item) && (isMenuSubmenu(item) || !pinnedLabels.has(item.label)));
110
91
  return /* @__PURE__ */ jsxs("tr", {
111
- className: cx("group", selected ? "bg-blue-500/5" : menuActive ? "theme-hover" : "hover:theme-hover", (href || onActivate || selectionActive) && "cursor-pointer", className),
92
+ className: cx("group", selected ? "bg-(--theme-accent-subtle)/60" : menuActive ? "theme-hover" : "hover:theme-hover", (href || onActivate || selectionActive) && "cursor-pointer", stretch && "relative [&_:is(a,button,input,label,[role=checkbox]):not([data-row-link])]:relative [&_:is(a,button,input,label,[role=checkbox]):not([data-row-link])]:z-[1]", className),
112
93
  onClick: handleRowClick,
113
94
  ...rowContextMenuProps(openRowMenu, items),
114
95
  children: [
115
96
  leading,
116
- styledCells,
97
+ /* @__PURE__ */ jsx(StretchRowLinkContext.Provider, {
98
+ value: stretch,
99
+ children: styledCells
100
+ }),
117
101
  showActions && /* @__PURE__ */ jsx(Table.Item, {
118
102
  className: "relative py-2.5",
119
103
  children: /* @__PURE__ */ jsxs("div", {
@@ -137,18 +121,20 @@ function ListRow({ href, onActivate, items, goTo, openMenu, pinnedActions, selec
137
121
  }
138
122
  function PinnedActionButton({ action, visible }) {
139
123
  warnIfInert(action, "ListPage pinned action");
140
- const Link = useLink();
141
124
  const runAction = useRunAction();
142
125
  const reason = action.disabled ? action.disabledReason : void 0;
143
126
  const props = {
144
127
  "aria-label": reason ? `${action.label}. ${reason}` : action.label,
145
- className: cx("rounded p-1 cursor-pointer text-(--theme-muted-text-color) transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) aria-disabled:cursor-not-allowed aria-disabled:opacity-20 aria-disabled:hover:bg-transparent aria-disabled:hover:text-(--theme-muted-text-color) [&>svg]:h-4 [&>svg]:w-4", visible ? "opacity-100" : "opacity-0 group-hover:opacity-100 focus-visible:opacity-100")
128
+ className: cx("rounded p-1 cursor-pointer text-(--theme-muted-text-color) transition-colors hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) focus-visible:outline-2 focus-visible:outline-(--theme-link) aria-disabled:cursor-not-allowed aria-disabled:opacity-20 aria-disabled:hover:bg-transparent aria-disabled:hover:text-(--theme-muted-text-color) [&>svg]:h-4 [&>svg]:w-4", visible ? "opacity-100" : "opacity-0 group-hover:opacity-100 focus-visible:opacity-100")
146
129
  };
147
130
  return /* @__PURE__ */ jsx(Tooltip, {
148
131
  content: reason ?? action.label,
149
132
  side: "bottom",
150
- children: action.href && !action.disabled ? /* @__PURE__ */ jsx(Link, {
133
+ children: action.href ? /* @__PURE__ */ jsx(AppLink, {
151
134
  href: action.href,
135
+ reloadDocument: action.reloadDocument,
136
+ target: action.target,
137
+ disabled: action.disabled,
152
138
  ...props,
153
139
  children: action.icon
154
140
  }) : /* @__PURE__ */ jsx("button", {
@@ -163,14 +149,27 @@ function PinnedActionButton({ action, visible }) {
163
149
  })
164
150
  });
165
151
  }
166
- /** Name column min-width is by role, not position, so a leading checkbox doesn't shift it. */
152
+ function columnSort(view, orderBy) {
153
+ if (view.orderBy !== orderBy) return "none";
154
+ return view.orderDir === "asc" ? "ascending" : "descending";
155
+ }
156
+ /** Name column min-width is by role, not position, so a leading checkbox
157
+ * doesn't shift it. A column with an `orderBy` the view offers sorts by it.
158
+ * While the view's column headers are off, the header row collapses: its
159
+ * cells still size the columns, but nobody sees or hears them, as with any
160
+ * other display option that is off. */
167
161
  function ListColumns({ view }) {
168
- return /* @__PURE__ */ jsxs(Fragment, { children: [view.visibleColumns.map((col, i) => /* @__PURE__ */ jsx(Table.Header, {
169
- caps: false,
170
- className: cx(col.headerClassName, view.columnClasses[i]),
171
- style: col.alwaysVisible ? { minWidth: 200 } : void 0,
172
- children: col.label
173
- }, col.key)), /* @__PURE__ */ jsx(ListActionsHeader, { view })] });
162
+ return /* @__PURE__ */ jsxs(Fragment, { children: [view.visibleColumns.map((col, i) => {
163
+ const orderBy = view.orderBys.some((o) => o.value === col.orderBy) ? col.orderBy : void 0;
164
+ return /* @__PURE__ */ jsx(Table.Header, {
165
+ caps: false,
166
+ className: cx(col.headerClassName, view.columnClasses[i], !view.showColumnHeaders && "list-headers-off"),
167
+ style: col.alwaysVisible ? { minWidth: 200 } : void 0,
168
+ sort: orderBy === void 0 ? void 0 : columnSort(view, orderBy),
169
+ onSort: orderBy === void 0 ? void 0 : () => view.sortBy(orderBy),
170
+ children: col.label
171
+ }, col.key);
172
+ }), /* @__PURE__ */ jsx(ListActionsHeader, { view })] });
174
173
  }
175
174
  ListColumns.isTableHeader = true;
176
175
  /** Stable `goTo` for whole-row navigation (`rowNavProps`, `ListRow`). */
@@ -1,27 +1,44 @@
1
- import type { ReactNode } from 'react';
2
- /** table-auto + horizontal scroll; the name column has a min-width floor so it can't be crushed. */
1
+ import { type ReactNode } from 'react';
2
+ /** Marks a header cell of a list whose column headers are off; see `listTableProps`. */
3
+ export declare const HEADERS_OFF = "list-headers-off";
4
+ /** table-auto; the name column has a min-width floor so it can't be crushed.
5
+ * The header row sticks as the rows scroll, so the list's scrolling box
6
+ * (`ListPage`'s body) scrolls it sideways too. */
3
7
  export declare const listTableProps: {
4
8
  readonly borderless: true;
9
+ readonly stickyHeader: true;
5
10
  readonly panel: false;
6
11
  readonly rounded: false;
7
- readonly theadClassName: 'text-(--theme-muted-text-color)';
12
+ readonly theadClassName: 'bg-(--theme-app-bg) text-(--theme-muted-text-color) has-[.list-headers-off]:collapse';
8
13
  readonly tbodyClassName: 'divide-y border-b border-(--theme-border) divide-(--theme-border)';
9
14
  readonly className: 'table-auto w-full [&_td:first-child]:pl-4 [&_th:first-child]:pl-4 [&_td:last-child]:pr-4 [&_th:last-child]:pr-4';
10
15
  };
11
16
  /** Widths come only from the header row, so a windowed body cannot shift the
12
- * columns as different content scrolls into view. */
17
+ * columns as different content scrolls into view. The windowed table scrolls
18
+ * in its own box, so its header doesn't stick. */
13
19
  export declare const listTableFixedProps: {
14
20
  readonly borderless: true;
15
21
  readonly panel: false;
16
22
  readonly rounded: false;
17
- readonly theadClassName: 'text-(--theme-muted-text-color)';
23
+ readonly theadClassName: 'bg-(--theme-app-bg) text-(--theme-muted-text-color) has-[.list-headers-off]:collapse';
18
24
  readonly tbodyClassName: 'divide-y border-b border-(--theme-border) divide-(--theme-border)';
25
+ readonly stickyHeader: false;
19
26
  readonly className: string;
20
27
  };
28
+ /** True inside a `ListRow` whose name link should take the whole row's
29
+ * clicks: it navigates, and no selection claims plain clicks. */
30
+ export declare const StretchRowLinkContext: import("react").Context<boolean>;
31
+ export declare const STRETCHED_LINK = "after:absolute after:inset-0 after:content-[''] focus-visible:outline-none focus-visible:after:outline-2 focus-visible:after:-outline-offset-2 focus-visible:after:outline-(--theme-link)";
32
+ /** The row a `STRETCHED_LINK` covers: its box, with the row's other controls
33
+ * raised above the link so they keep their own clicks. A checkbox is a
34
+ * `role="checkbox"` span, not an input, so the role names it. */
35
+ export declare const STRETCHED_LINK_ROW = "relative [&_:is(a,button,input,label,[role=checkbox]):not([data-row-link])]:relative [&_:is(a,button,input,label,[role=checkbox]):not([data-row-link])]:z-[1]";
21
36
  export declare function NameCell({ icon, name, subtitle, href, badge, title, children, }: {
22
37
  icon: ReactNode;
23
38
  name: string;
24
39
  subtitle?: string | undefined;
40
+ /** The name's link. Inside a `ListRow` with an `href`, the whole row is
41
+ * this link, so give both the same URL. */
25
42
  href: string | null;
26
43
  badge?: ReactNode;
27
44
  title?: string | undefined;
@@ -34,9 +51,10 @@ export declare function GroupRow({ label, count, colSpan, }: {
34
51
  count: number;
35
52
  colSpan: number;
36
53
  }): import("react").JSX.Element;
37
- export declare function ListError({ error, noun }: {
54
+ export declare function ListError({ error, noun, }: {
38
55
  error: unknown;
39
- noun?: string;
56
+ /** What failed to load, as the heading names it: "Couldn't load clusters". */
57
+ noun?: string | undefined;
40
58
  }): import("react").JSX.Element;
41
59
  export declare function ListSkeleton({ rows }: {
42
60
  rows?: number;
@@ -54,6 +72,18 @@ export declare function RowSelectCheckbox({ checked, label, onToggle, }: {
54
72
  /** Gets the press, a click or a key, so a host can extend a range on Shift. */
55
73
  onToggle: (event: Event) => void;
56
74
  }): import("react").JSX.Element;
75
+ /** The selection column's header: a checkbox that ticks every row, mixed while
76
+ * only some are. `onToggle` selects them all, or clears them once all are. */
77
+ export declare function ListSelectAllHeader({ selected, total, onToggle, }: {
78
+ /** How many rows are selected. */
79
+ selected: number;
80
+ /** How many rows there are to select. */
81
+ total: number;
82
+ onToggle: () => void;
83
+ }): import("react").JSX.Element;
84
+ export declare namespace ListSelectAllHeader {
85
+ var isTableHeader: boolean;
86
+ }
57
87
  /** Sizes to fit pinned buttons + ⋯; below ~44rem only ⋯ remains. Pass a view's
58
88
  * chrome (structurally: showActions + actionsWidth) or actionCount. */
59
89
  export declare function ListActionsHeader({ view, actionCount, className, }: {
@@ -1,31 +1,48 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { ErrorIcon } from "../icons.js";
3
3
  import { positionKeys } from "../components/keys.js";
4
- import { useLink, useStrings } from "../components/Provider.js";
4
+ import { useStrings } from "../components/Provider.js";
5
+ import { AppLink } from "../components/AppLink.js";
5
6
  import { Checkbox } from "../components/Checkbox.js";
6
7
  import { LoadingRegion, Skeleton } from "../components/Skeleton.js";
7
8
  import { Table } from "../components/Table.js";
8
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
10
+ import { createContext, useContext } from "react";
9
11
  //#region src/list/ListTable.tsx
10
12
  var NAME_TEXT = "font-medium text-[13px] leading-tight text-(--theme-app) truncate";
11
13
  var SUBTITLE_TEXT = "text-xs leading-tight text-(--theme-muted-text-color) truncate";
12
- /** table-auto + horizontal scroll; the name column has a min-width floor so it can't be crushed. */
14
+ /** Marks a header cell of a list whose column headers are off; see `listTableProps`. */
15
+ var HEADERS_OFF = "list-headers-off";
16
+ /** table-auto; the name column has a min-width floor so it can't be crushed.
17
+ * The header row sticks as the rows scroll, so the list's scrolling box
18
+ * (`ListPage`'s body) scrolls it sideways too. */
13
19
  var listTableProps = {
14
20
  borderless: true,
21
+ stickyHeader: true,
15
22
  panel: false,
16
23
  rounded: false,
17
- theadClassName: "text-(--theme-muted-text-color)",
24
+ theadClassName: "bg-(--theme-app-bg) text-(--theme-muted-text-color) has-[.list-headers-off]:collapse",
18
25
  tbodyClassName: "divide-y border-b border-(--theme-border) divide-(--theme-border)",
19
26
  className: "table-auto w-full [&_td:first-child]:pl-4 [&_th:first-child]:pl-4 [&_td:last-child]:pr-4 [&_th:last-child]:pr-4"
20
27
  };
21
28
  /** Widths come only from the header row, so a windowed body cannot shift the
22
- * columns as different content scrolls into view. */
29
+ * columns as different content scrolls into view. The windowed table scrolls
30
+ * in its own box, so its header doesn't stick. */
23
31
  var listTableFixedProps = {
24
32
  ...listTableProps,
33
+ stickyHeader: false,
25
34
  className: listTableProps.className.replace("table-auto", "table-fixed")
26
35
  };
36
+ /** True inside a `ListRow` whose name link should take the whole row's
37
+ * clicks: it navigates, and no selection claims plain clicks. */
38
+ var StretchRowLinkContext = createContext(false);
39
+ var STRETCHED_LINK = "after:absolute after:inset-0 after:content-[''] focus-visible:outline-none focus-visible:after:outline-2 focus-visible:after:-outline-offset-2 focus-visible:after:outline-(--theme-link)";
40
+ /** The row a `STRETCHED_LINK` covers: its box, with the row's other controls
41
+ * raised above the link so they keep their own clicks. A checkbox is a
42
+ * `role="checkbox"` span, not an input, so the role names it. */
43
+ var STRETCHED_LINK_ROW = "relative [&_:is(a,button,input,label,[role=checkbox]):not([data-row-link])]:relative [&_:is(a,button,input,label,[role=checkbox]):not([data-row-link])]:z-[1]";
27
44
  function NameCell({ icon, name, subtitle, href, badge, title, children }) {
28
- const Link = useLink();
45
+ const stretch = useContext(StretchRowLinkContext);
29
46
  const inner = /* @__PURE__ */ jsxs(Fragment, { children: [icon, /* @__PURE__ */ jsxs("div", {
30
47
  className: "min-w-0",
31
48
  children: [/* @__PURE__ */ jsxs("div", {
@@ -42,9 +59,10 @@ function NameCell({ icon, name, subtitle, href, badge, title, children }) {
42
59
  })] });
43
60
  return /* @__PURE__ */ jsxs(Table.Item, {
44
61
  className: "py-2.5",
45
- children: [href ? /* @__PURE__ */ jsx(Link, {
62
+ children: [href ? /* @__PURE__ */ jsx(AppLink, {
46
63
  href,
47
- className: "group/namecell flex items-center gap-2.5 min-w-0 cursor-pointer",
64
+ "data-row-link": stretch || void 0,
65
+ className: cx("group/namecell flex items-center gap-2.5 min-w-0 cursor-pointer", stretch && "after:absolute after:inset-0 after:content-[''] focus-visible:outline-none focus-visible:after:outline-2 focus-visible:after:-outline-offset-2 focus-visible:after:outline-(--theme-link)"),
48
66
  children: inner
49
67
  }) : /* @__PURE__ */ jsx("div", {
50
68
  className: "flex items-center gap-2.5 min-w-0",
@@ -75,16 +93,20 @@ function GroupRow({ label, count, colSpan }) {
75
93
  ]
76
94
  }) });
77
95
  }
78
- function ListError({ error, noun = "data" }) {
96
+ function ListError({ error, noun }) {
79
97
  const { list: t } = useStrings();
80
98
  const message = error && typeof error === "object" && "message" in error ? String(error.message) : void 0;
81
99
  return /* @__PURE__ */ jsxs("div", {
100
+ role: "alert",
82
101
  className: "flex flex-col items-center justify-center gap-3 py-20 text-center",
83
- children: [/* @__PURE__ */ jsx(ErrorIcon, { className: "h-10 w-10 text-red-400 opacity-80" }), /* @__PURE__ */ jsxs("div", {
102
+ children: [/* @__PURE__ */ jsx(ErrorIcon, {
103
+ "aria-hidden": "true",
104
+ className: "h-10 w-10 text-(--theme-error)"
105
+ }), /* @__PURE__ */ jsxs("div", {
84
106
  className: "space-y-1",
85
107
  children: [/* @__PURE__ */ jsx("p", {
86
108
  className: "text-sm font-medium text-(--theme-app)",
87
- children: t.couldNotLoad(noun)
109
+ children: t.couldNotLoad(noun ?? t.data)
88
110
  }), message && /* @__PURE__ */ jsx("p", {
89
111
  className: "max-w-md text-xs text-(--theme-muted-text-color)",
90
112
  children: message
@@ -146,6 +168,24 @@ function RowSelectCheckbox({ checked, label, onToggle }) {
146
168
  }
147
169
  });
148
170
  }
171
+ /** The selection column's header: a checkbox that ticks every row, mixed while
172
+ * only some are. `onToggle` selects them all, or clears them once all are. */
173
+ function ListSelectAllHeader({ selected, total, onToggle }) {
174
+ const t = useStrings().list;
175
+ const all = total > 0 && selected >= total;
176
+ return /* @__PURE__ */ jsx(Table.Header, {
177
+ caps: false,
178
+ className: "w-10",
179
+ children: /* @__PURE__ */ jsx(Checkbox, {
180
+ "aria-label": t.selectAll,
181
+ checked: all,
182
+ indeterminate: selected > 0 && !all,
183
+ disabled: total === 0,
184
+ onCheckedChange: () => onToggle()
185
+ })
186
+ });
187
+ }
188
+ ListSelectAllHeader.isTableHeader = true;
149
189
  /** Sizes to fit pinned buttons + ⋯; below ~44rem only ⋯ remains. Pass a view's
150
190
  * chrome (structurally: showActions + actionsWidth) or actionCount. */
151
191
  function ListActionsHeader({ view, actionCount = 0, className }) {
@@ -158,7 +198,7 @@ function ListActionsHeader({ view, actionCount = 0, className }) {
158
198
  width,
159
199
  minWidth: width
160
200
  },
161
- className: cx("relative py-2 text-right @max-[43rem]:w-[52px]! @max-[43rem]:min-w-[52px]!", className),
201
+ className: cx("relative py-2 text-right @max-[44rem]:w-[52px]! @max-[44rem]:min-w-[52px]!", className),
162
202
  children: /* @__PURE__ */ jsx("span", {
163
203
  className: "sr-only",
164
204
  children: common.actions
@@ -167,4 +207,4 @@ function ListActionsHeader({ view, actionCount = 0, className }) {
167
207
  }
168
208
  ListActionsHeader.isTableHeader = true;
169
209
  //#endregion
170
- export { GroupRow, ListActionsHeader, ListError, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, actionsColumnWidth, listTableFixedProps, listTableProps };
210
+ export { GroupRow, HEADERS_OFF, ListActionsHeader, ListError, ListSelectAllHeader, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, STRETCHED_LINK, STRETCHED_LINK_ROW, StretchRowLinkContext, actionsColumnWidth, listTableFixedProps, listTableProps };
@@ -4,10 +4,10 @@ import { MenuCheckboxItem, MenuPopup, MenuSubmenuTrigger } from "../components/M
4
4
  import { Tooltip } from "../components/Tooltip.js";
5
5
  import { useStrings } from "../components/Provider.js";
6
6
  import { useQueryMatch } from "../components/search.js";
7
- import { Switch } from "../components/Switch.js";
8
7
  import { IconButton } from "../components/IconButton.js";
9
8
  import { Popover } from "../components/Popover.js";
10
9
  import { InfoTip } from "../components/InfoTip.js";
10
+ import { Switch } from "../components/Switch.js";
11
11
  import { Select } from "../components/Select.js";
12
12
  import { Menu } from "@base-ui/react/menu";
13
13
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -1,15 +1,12 @@
1
1
  import { cx } from "../cx.js";
2
- import { CopyIcon } from "../icons.js";
3
- import { Tooltip } from "../components/Tooltip.js";
4
2
  import { useLink, useStrings } from "../components/Provider.js";
5
- import { useCopy } from "../components/useCopy.js";
6
3
  import { Avatar } from "../components/Avatar.js";
4
+ import { CopyButton } from "../components/CopyButton.js";
7
5
  import { LoadingRegion, Skeleton } from "../components/Skeleton.js";
8
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
7
  //#region src/list/UserHoverCard.tsx
10
8
  function UserHoverCard({ username, name, avatarSrc, badge, href, linkComponent, loading = false, children }) {
11
- const { common, list: t } = useStrings();
12
- const { copy } = useCopy();
9
+ const { list: t } = useStrings();
13
10
  const SlotLink = useLink();
14
11
  const Link = linkComponent ?? SlotLink;
15
12
  const displayName = name || username;
@@ -36,7 +33,6 @@ function UserHoverCard({ username, name, avatarSrc, badge, href, linkComponent,
36
33
  })]
37
34
  })] });
38
35
  const copyLabel = EMAIL.test(username) ? t.labelEmail : t.labelUsername;
39
- const onCopy = () => void copy(username, { label: copyLabel });
40
36
  return /* @__PURE__ */ jsxs("div", {
41
37
  className: "w-64 p-3",
42
38
  children: [/* @__PURE__ */ jsxs("div", {
@@ -48,15 +44,11 @@ function UserHoverCard({ username, name, avatarSrc, badge, href, linkComponent,
48
44
  }) : /* @__PURE__ */ jsx("div", {
49
45
  className: "flex min-w-0 flex-1 items-start gap-3",
50
46
  children: header
51
- }), username && /* @__PURE__ */ jsx(Tooltip, {
52
- content: common.copyThing(copyLabel),
53
- children: /* @__PURE__ */ jsx("button", {
54
- type: "button",
55
- "aria-label": common.copyThing(copyLabel),
56
- onClick: onCopy,
57
- className: cx("shrink-0 cursor-pointer rounded p-0.5 text-(--theme-muted-text-color) opacity-0 transition hover:bg-(--theme-muted-panel-bg) hover:text-(--theme-app) [&>svg]:h-3.5 [&>svg]:w-3.5", "hover:opacity-100 group-has-[[data-copy-value]:hover]/copy:opacity-100 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-(--theme-link) pointer-coarse:opacity-100"),
58
- children: /* @__PURE__ */ jsx(CopyIcon, { "aria-hidden": "true" })
59
- })
47
+ }), username && /* @__PURE__ */ jsx(CopyButton, {
48
+ text: username,
49
+ label: copyLabel,
50
+ size: "xs",
51
+ className: cx("-mr-1 opacity-0", "hover:opacity-100 group-has-[[data-copy-value]:hover]/copy:opacity-100 focus-visible:opacity-100 pointer-coarse:opacity-100")
60
52
  })]
61
53
  }), loading ? /* @__PURE__ */ jsxs(LoadingRegion, {
62
54
  className: "mt-3 space-y-2 border-t border-(--theme-border) pt-3",
@@ -1,6 +1,6 @@
1
1
  export { ActionToolbar, stickyToolbarClasses, type ToolbarAction, ToolbarButton, type ToolbarItem, } from './ActionToolbar.js';
2
- export { ListColumns, ListPage, type ListPageProps, ListRow, ListRowActionsProvider, type ListSection, useListNavigate, } from './ListPage.js';
3
- export { actionsColumnWidth, GroupRow, ListActionsHeader, ListError, ListSkeleton, listTableFixedProps, listTableProps, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, } from './ListTable.js';
2
+ export { ListColumns, ListPage, type ListPageProps, ListRow, ListRowActionsProvider, useListNavigate, } from './ListPage.js';
3
+ export { actionsColumnWidth, GroupRow, ListActionsHeader, ListError, ListSelectAllHeader, ListSkeleton, listTableFixedProps, listTableProps, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, } from './ListTable.js';
4
4
  export { ListDisplayMenu, ListFilterMenu } from './ListViewControls.js';
5
5
  export { ListCount, type ListSearch, ListSearchControl, useListSearch, } from './listSearch.js';
6
6
  export { type ActionDef, type ColumnDef, DEFAULT_ORDER, FAVORITE_ORDER, type FilterFacet, type ListView, type ListViewChrome, type ListViewConfig, type OrderByOption, useListView, } from './listView.js';
@@ -1,9 +1,9 @@
1
1
  import { ActionToolbar, ToolbarButton, stickyToolbarClasses } from "./ActionToolbar.js";
2
- import { GroupRow, ListActionsHeader, ListError, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, actionsColumnWidth, listTableFixedProps, listTableProps } from "./ListTable.js";
2
+ import { GroupRow, ListActionsHeader, ListError, ListSelectAllHeader, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, actionsColumnWidth, listTableFixedProps, listTableProps } from "./ListTable.js";
3
3
  import { MoreButton, assembleRowMenu, rowContextMenuProps, useCopySubmenu, useRowMenuActive } from "./rowMenu.js";
4
4
  import { ListColumns, ListPage, ListRow, ListRowActionsProvider, useListNavigate } from "./ListPage.js";
5
5
  import { ListDisplayMenu, ListFilterMenu } from "./ListViewControls.js";
6
6
  import { ListCount, ListSearchControl, useListSearch } from "./listSearch.js";
7
7
  import { DEFAULT_ORDER, FAVORITE_ORDER, useListView } from "./listView.js";
8
8
  import { HoverCardRow, UserHoverCard } from "./UserHoverCard.js";
9
- export { ActionToolbar, DEFAULT_ORDER, FAVORITE_ORDER, GroupRow, HoverCardRow, ListActionsHeader, ListColumns, ListCount, ListDisplayMenu, ListError, ListFilterMenu, ListPage, ListRow, ListRowActionsProvider, ListSearchControl, ListSkeleton, MoreButton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, ToolbarButton, UserHoverCard, actionsColumnWidth, assembleRowMenu, listTableFixedProps, listTableProps, rowContextMenuProps, stickyToolbarClasses, useCopySubmenu, useListNavigate, useListSearch, useListView, useRowMenuActive };
9
+ export { ActionToolbar, DEFAULT_ORDER, FAVORITE_ORDER, GroupRow, HoverCardRow, ListActionsHeader, ListColumns, ListCount, ListDisplayMenu, ListError, ListFilterMenu, ListPage, ListRow, ListRowActionsProvider, ListSearchControl, ListSelectAllHeader, ListSkeleton, MoreButton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, ToolbarButton, UserHoverCard, actionsColumnWidth, assembleRowMenu, listTableFixedProps, listTableProps, rowContextMenuProps, stickyToolbarClasses, useCopySubmenu, useListNavigate, useListSearch, useListView, useRowMenuActive };
@@ -1,5 +1,5 @@
1
- import { CloseIcon, RemoveIcon, SearchIcon } from "../icons.js";
2
1
  import { buttonClasses } from "../components/buttonClasses.js";
2
+ import { CloseIcon, RemoveIcon, SearchIcon } from "../icons.js";
3
3
  import { Tooltip } from "../components/Tooltip.js";
4
4
  import { usePersistedState, useStrings } from "../components/Provider.js";
5
5
  import { isTextEntryContext } from "../components/shortcutKey.js";
@@ -7,6 +7,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import { useCallback, useEffect, useRef, useState } from "react";
8
8
  //#region src/list/listSearch.tsx
9
9
  var NO_TERMS = [];
10
+ var COUNT = new Intl.NumberFormat();
10
11
  /** Pass `enabled: false` to suspend the global ⌘F/`/` hijack (e.g. when search isn't shown and browser Find should stay active). */
11
12
  function useListSearch(enabled = true, storageKey) {
12
13
  const [filter, setFilter] = useState("");
@@ -20,11 +21,6 @@ function useListSearch(enabled = true, storageKey) {
20
21
  useEffect(() => {
21
22
  if (!enabled) return;
22
23
  const onKey = (e) => {
23
- if (e.key === "Escape" && searchOpen) {
24
- setSearchOpen(false);
25
- setFilter("");
26
- return;
27
- }
28
24
  if (isTextEntryContext(e.target)) return;
29
25
  if ((e.key === "f" || e.key === "F") && (e.metaKey || e.ctrlKey)) {
30
26
  e.preventDefault();
@@ -36,7 +32,7 @@ function useListSearch(enabled = true, storageKey) {
36
32
  };
37
33
  window.addEventListener("keydown", onKey);
38
34
  return () => window.removeEventListener("keydown", onKey);
39
- }, [searchOpen, enabled]);
35
+ }, [enabled]);
40
36
  const openSearch = useCallback(() => setSearchOpen(true), []);
41
37
  const closeSearch = useCallback(() => {
42
38
  setSearchOpen(false);
@@ -94,8 +90,12 @@ function ListSearchControl({ search, placeholder, label: labelProp }) {
94
90
  value: search.filter,
95
91
  onChange: (e) => search.setFilter(e.target.value),
96
92
  onFocus: search.openSearch,
93
+ "aria-label": label,
97
94
  onKeyDown: (e) => {
98
- if (e.key === "Enter") {
95
+ if (e.key === "Escape" && search.searchOpen) {
96
+ e.preventDefault();
97
+ search.closeSearch();
98
+ } else if (e.key === "Enter") {
99
99
  e.preventDefault();
100
100
  search.addTerm(search.filter);
101
101
  } else if (e.key === "Backspace" && !search.filter) {
@@ -117,7 +117,7 @@ function ListSearchControl({ search, placeholder, label: labelProp }) {
117
117
  });
118
118
  return /* @__PURE__ */ jsx(Tooltip, {
119
119
  content: label,
120
- shortcut: "⌘+F",
120
+ shortcut: "/",
121
121
  children: /* @__PURE__ */ jsx("button", {
122
122
  type: "button",
123
123
  onClick: search.openSearch,
@@ -132,11 +132,13 @@ function ListSearchControl({ search, placeholder, label: labelProp }) {
132
132
  });
133
133
  }
134
134
  function ListCount({ total, matches, filterActive, noun, nounPlural }) {
135
+ const { list: t } = useStrings();
135
136
  return /* @__PURE__ */ jsx("div", {
136
137
  className: "flex items-center h-9",
137
138
  children: /* @__PURE__ */ jsx("span", {
139
+ role: "status",
138
140
  className: "text-[13px] text-(--theme-muted-text-color)",
139
- children: filterActive ? `${matches} of ${total} ${nounPlural}` : `${total} ${total === 1 ? noun : nounPlural}`
141
+ children: filterActive ? t.matchCount(COUNT.format(matches), COUNT.format(total), nounPlural) : t.count(COUNT.format(total), total === 1 ? noun : nounPlural)
140
142
  })
141
143
  });
142
144
  }
@@ -19,6 +19,10 @@ export interface ColumnDef {
19
19
  * column there: for something short that compares best lined up, like a
20
20
  * total. The first column never stacks; it is what the others stack under. */
21
21
  stack?: boolean;
22
+ /** The `value` of the {@link OrderByOption} this column sorts by. Its header
23
+ * then sorts the list: it shows, and sets, the same order as Display
24
+ * options' Ordering. */
25
+ orderBy?: string;
22
26
  }
23
27
  /** Column responsive importance; `low` drops before `medium`. See {@link ColumnDef.priority}. */
24
28
  type ColumnPriority = 'low' | 'medium';
@@ -65,6 +69,8 @@ export interface ListViewConfig<T> {
65
69
  actions?: ActionDef[];
66
70
  /** Explicit pinned-button count when not using actions+isPinned; sizes the actions column. */
67
71
  actionCount?: number;
72
+ /** Whether the column-header row shows before the user changes Display
73
+ * options (default true). */
68
74
  defaultShowColumnHeaders?: boolean;
69
75
  /** Whether row action affordances show before the user changes Display options. */
70
76
  defaultShowActions?: boolean;
@@ -111,6 +117,9 @@ export interface ListView<T> extends ListViewChrome {
111
117
  setOrderBy: (value: string) => void;
112
118
  orderDir: OrderDir;
113
119
  toggleOrderDir: () => void;
120
+ /** Orders by `value` ascending, or flips the direction when the list
121
+ * already orders by it: a sortable column header's click. */
122
+ sortBy: (value: string) => void;
114
123
  applyOrder: (rows: T[]) => T[];
115
124
  facets: FilterFacet<T>[];
116
125
  filters: Record<string, string[]>;
@@ -121,7 +130,7 @@ export interface ListView<T> extends ListViewChrome {
121
130
  actions: ActionDef[];
122
131
  isPinned: (key: string) => boolean;
123
132
  togglePinned: (key: string) => void;
124
- /** Whether the table renders its column-header row (default off). */
133
+ /** Whether the table renders its column-header row (default on). */
125
134
  showColumnHeaders: boolean;
126
135
  toggleColumnHeaders: () => void;
127
136
  /** Toggle whether rows expose action affordances. */
@@ -18,7 +18,7 @@ var DEFAULT_ORDER = "__default";
18
18
  var FAVORITE_ORDER = "__favorite";
19
19
  /** Persisted grouping + column-visibility + field-filter state for a list page,
20
20
  * driving the shared Display/Filter menus. */
21
- function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], defaultGroupBy, orderBys = [], defaultOrderBy, facets = [], actions = [], actionCount, defaultShowColumnHeaders = false, defaultShowActions = true, isFavorite, isOn, createdAt }) {
21
+ function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], defaultGroupBy, orderBys = [], defaultOrderBy, facets = [], actions = [], actionCount, defaultShowColumnHeaders = true, defaultShowActions = true, isFavorite, isOn, createdAt }) {
22
22
  const t = useStrings().list;
23
23
  const tiebreak = orderBys[0]?.compare;
24
24
  const effectiveOrderBys = useMemo(() => {
@@ -136,6 +136,19 @@ function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], def
136
136
  setShowActions
137
137
  ]);
138
138
  const toggleOrderDir = useCallback(() => setOrderDir((d) => d === "asc" ? "desc" : "asc"), [setOrderDir]);
139
+ const sortBy = useCallback((value) => {
140
+ if (value === orderBy) {
141
+ toggleOrderDir();
142
+ return;
143
+ }
144
+ setOrderBy(value);
145
+ setOrderDir("asc");
146
+ }, [
147
+ orderBy,
148
+ toggleOrderDir,
149
+ setOrderBy,
150
+ setOrderDir
151
+ ]);
139
152
  const applyOrder = useCallback((rows) => {
140
153
  const opt = effectiveOrderBys.find((o) => o.value === orderBy);
141
154
  if (!opt) return rows;
@@ -190,6 +203,7 @@ function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], def
190
203
  setOrderBy,
191
204
  orderDir,
192
205
  toggleOrderDir,
206
+ sortBy,
193
207
  applyOrder,
194
208
  facets,
195
209
  filters,
@@ -1,7 +1,9 @@
1
- export declare function LogViewer({ log, height, width, roundedBottom, lineNumbers, additionalLeftBarComponents, additionalRightBarComponents, additionalBottomLeftBarComponents, additionalBottomRightBarComponents, hideExpand, inline, contentClassName, enableWorkflowCommands: workflowCommandsRequested, onReachTop, loadingTop, endOfLog, prependedSignal, enableTimeRangeFilter, onTimeRangeChange, initialTimeRangeFrom, initialTimeRangeTo, }: {
1
+ export declare function LogViewer({ log, height, width, downloadName, roundedBottom, lineNumbers, additionalLeftBarComponents, additionalRightBarComponents, additionalBottomLeftBarComponents, additionalBottomRightBarComponents, hideExpand, inline, contentClassName, enableWorkflowCommands: workflowCommandsRequested, onReachTop, loadingTop, endOfLog, prependedSignal, enableTimeRangeFilter, onTimeRangeChange, initialTimeRangeFrom, initialTimeRangeTo, }: {
2
2
  log: string;
3
3
  height?: string | number;
4
- width: string | number;
4
+ width?: string | number | undefined;
5
+ /** The file name Download saves the log under. */
6
+ downloadName?: string | undefined;
5
7
  roundedBottom?: boolean;
6
8
  lineNumbers?: boolean;
7
9
  additionalLeftBarComponents?: React.ReactNode;