@openviking/react 0.1.5 → 0.1.7

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 (145) hide show
  1. package/README.md +15 -11
  2. package/dist/2~0.js +6 -5
  3. package/dist/37~0.js +27 -0
  4. package/dist/3c~0.js +24 -12
  5. package/dist/3~0.js +19 -9
  6. package/dist/ae~0.js +8 -1
  7. package/dist/ark-message-list-view~0.js +9 -4
  8. package/dist/a~0.js +13 -7
  9. package/dist/c7r~0.js +50 -0
  10. package/dist/ck~0.js +7 -0
  11. package/dist/client/paths.d.ts +10 -0
  12. package/dist/client/provider.d.ts +6 -1
  13. package/dist/components/agent-connect/style.css +268 -37
  14. package/dist/components/directory-tour/style.css +265 -21
  15. package/dist/components/directory-tour.js +1 -1
  16. package/dist/components/experience-dashboard/dashboard-tabs.d.ts +11 -0
  17. package/dist/components/experience-dashboard/experience-dashboard.d.ts +2 -0
  18. package/dist/components/experience-dashboard/experience-impact.d.ts +11 -0
  19. package/dist/components/experience-dashboard/experience-list.d.ts +9 -0
  20. package/dist/components/experience-dashboard/experience-pagination.d.ts +8 -0
  21. package/dist/components/experience-dashboard/experience-tests.d.ts +6 -0
  22. package/dist/components/experience-dashboard/experience-trajectories.d.ts +6 -0
  23. package/dist/components/experience-dashboard/index.d.ts +2 -0
  24. package/dist/components/experience-dashboard/labels.d.ts +2 -0
  25. package/dist/components/experience-dashboard/style.css +914 -0
  26. package/dist/components/experience-dashboard/style.js +0 -0
  27. package/dist/components/experience-dashboard/types.d.ts +106 -0
  28. package/dist/components/experience-dashboard.js +1 -0
  29. package/dist/components/file-browser-entries/style.css +463 -86
  30. package/dist/components/file-preview/style.css +267 -78
  31. package/dist/components/file-tree/file-tree-toolbar.d.ts +3 -3
  32. package/dist/components/file-tree/locales/en-US.d.ts +3 -0
  33. package/dist/components/file-tree/locales/zh-CN.d.ts +3 -0
  34. package/dist/components/file-tree/style.css +372 -88
  35. package/dist/components/file-tree/types.d.ts +13 -4
  36. package/dist/components/scope-switcher/style.css +412 -39
  37. package/dist/components/style.css +9326 -4553
  38. package/dist/components/tui/style.css +385 -67
  39. package/dist/components/viking-bot/interaction/style.css +143 -22
  40. package/dist/components/viking-bot/style.css +1075 -278
  41. package/dist/components/wiki-dashboard/index.d.ts +2 -0
  42. package/dist/components/wiki-dashboard/labels.d.ts +2 -0
  43. package/dist/components/wiki-dashboard/style.css +956 -0
  44. package/dist/components/wiki-dashboard/style.js +0 -0
  45. package/dist/components/wiki-dashboard/types.d.ts +92 -0
  46. package/dist/components/wiki-dashboard/wiki-action-button.d.ts +9 -0
  47. package/dist/components/wiki-dashboard/wiki-conflict-card.d.ts +15 -0
  48. package/dist/components/wiki-dashboard/wiki-dashboard.d.ts +2 -0
  49. package/dist/components/wiki-dashboard/wiki-file-icon.d.ts +3 -0
  50. package/dist/components/wiki-dashboard/wiki-severity-filter.d.ts +8 -0
  51. package/dist/components/wiki-dashboard.js +1 -0
  52. package/dist/c~0.js +16 -8
  53. package/dist/ep~0.js +345 -0
  54. package/dist/e~0.js +33 -328
  55. package/dist/fj~0.js +507 -0
  56. package/dist/fl~0.js +709 -0
  57. package/dist/f~0.js +12 -27
  58. package/dist/icons/index.d.ts +28 -0
  59. package/dist/icons.js +1 -1
  60. package/dist/index.d.ts +4 -0
  61. package/dist/index.js +3 -1
  62. package/dist/k~0.js +277 -1
  63. package/dist/lk~0.js +56 -14
  64. package/dist/l~0.js +2786 -446
  65. package/dist/modules/console/index.d.ts +1 -2
  66. package/dist/modules/console/style.css +1787 -346
  67. package/dist/modules/console.js +1 -1
  68. package/dist/modules/file-browser/acl/acl-add-principal.d.ts +12 -0
  69. package/dist/modules/file-browser/acl/acl-candidate.d.ts +8 -0
  70. package/dist/modules/file-browser/acl/acl-client.d.ts +13 -0
  71. package/dist/modules/file-browser/acl/acl-confirmation.d.ts +6 -0
  72. package/dist/modules/file-browser/acl/acl-entry.d.ts +13 -0
  73. package/dist/modules/file-browser/acl/acl-labels.d.ts +47 -0
  74. package/dist/modules/file-browser/acl/acl-menu.d.ts +10 -0
  75. package/dist/modules/file-browser/acl/acl-modal.d.ts +17 -0
  76. package/dist/modules/file-browser/acl/acl-model.d.ts +17 -0
  77. package/dist/modules/file-browser/acl/acl-principal-tabs.d.ts +6 -0
  78. package/dist/modules/file-browser/acl/acl-tag.d.ts +7 -0
  79. package/dist/modules/file-browser/acl/locales/en-US.d.ts +44 -0
  80. package/dist/modules/file-browser/acl/locales/zh-CN.d.ts +44 -0
  81. package/dist/modules/file-browser/acl/use-acl-principals.d.ts +17 -0
  82. package/dist/modules/file-browser/acl/use-acl-report.d.ts +27 -0
  83. package/dist/modules/file-browser/acl-types.d.ts +86 -0
  84. package/dist/modules/file-browser/dashboard-data.d.ts +9 -0
  85. package/dist/modules/file-browser/dashboard-indicator.d.ts +8 -0
  86. package/dist/modules/file-browser/dashboard-labels.d.ts +9 -0
  87. package/dist/modules/file-browser/dashboard-model.d.ts +17 -0
  88. package/dist/modules/file-browser/dashboard-resource.d.ts +12 -0
  89. package/dist/modules/file-browser/experience-dashboard-pane.d.ts +8 -0
  90. package/dist/modules/file-browser/experience-data.d.ts +31 -0
  91. package/dist/modules/file-browser/file-browser-detail.d.ts +4 -1
  92. package/dist/modules/file-browser/file-browser-labels.d.ts +1 -1
  93. package/dist/modules/file-browser/file-browser-search.d.ts +20 -0
  94. package/dist/modules/file-browser/file-browser-toolbar.d.ts +3 -10
  95. package/dist/modules/file-browser/file-browser-tree-pane.d.ts +4 -7
  96. package/dist/modules/file-browser/file-browser.d.ts +1 -1
  97. package/dist/modules/file-browser/fs-directories.d.ts +9 -0
  98. package/dist/modules/file-browser/fs-lines.d.ts +36 -0
  99. package/dist/modules/file-browser/fs-memory-fields.d.ts +65 -0
  100. package/dist/modules/file-browser/fs-model.d.ts +15 -3
  101. package/dist/modules/file-browser/fs-read-file.d.ts +14 -0
  102. package/dist/modules/file-browser/index.d.ts +3 -2
  103. package/dist/modules/file-browser/locales/en-US.d.ts +5 -0
  104. package/dist/modules/file-browser/locales/zh-CN.d.ts +5 -0
  105. package/dist/modules/file-browser/style.css +4033 -483
  106. package/dist/modules/file-browser/types.d.ts +82 -20
  107. package/dist/modules/file-browser/use-browser-dashboards.d.ts +31 -0
  108. package/dist/modules/file-browser/use-dashboard-query.d.ts +9 -0
  109. package/dist/modules/file-browser/use-experience-dashboard.d.ts +27 -0
  110. package/dist/modules/file-browser/use-file-browser-acl.d.ts +15 -0
  111. package/dist/modules/file-browser/use-file-browser.d.ts +6 -51
  112. package/dist/modules/file-browser/use-fs-tree.d.ts +3 -0
  113. package/dist/modules/file-browser/use-wiki-dashboard.d.ts +17 -0
  114. package/dist/modules/file-browser/wiki-conflicts.d.ts +12 -0
  115. package/dist/modules/file-browser/wiki-dashboard-pane.d.ts +14 -0
  116. package/dist/modules/file-browser.js +1 -1
  117. package/dist/modules/scope-cascade/controller.d.ts +2 -2
  118. package/dist/modules/scope-cascade/index.d.ts +1 -2
  119. package/dist/modules/scope-cascade/scope-cascade.d.ts +1 -1
  120. package/dist/modules/scope-cascade/style.css +581 -39
  121. package/dist/modules/scope-cascade/types.d.ts +8 -3
  122. package/dist/modules/scope-cascade/use-scope-cascade.d.ts +2 -2
  123. package/dist/modules/scope-cascade.js +1 -1
  124. package/dist/modules/style.css +13589 -7966
  125. package/dist/n~0.js +73 -165
  126. package/dist/rt~0.js +255 -0
  127. package/dist/r~0.js +18 -39
  128. package/dist/shared/components/button/button.d.ts +20 -0
  129. package/dist/shared/components/input/input.d.ts +17 -0
  130. package/dist/shared/components/modal/locales/en-US.d.ts +4 -0
  131. package/dist/shared/components/modal/locales/zh-CN.d.ts +4 -0
  132. package/dist/shared/components/modal/modal.d.ts +2 -0
  133. package/dist/shared/components/modal/types.d.ts +18 -0
  134. package/dist/shared/components/overlay/lock-body-scroll.d.ts +1 -0
  135. package/dist/shared/components/popover/popover.d.ts +1 -1
  136. package/dist/shared/components/popover/types.d.ts +7 -1
  137. package/dist/shared/dashboard/dashboard-state.d.ts +12 -0
  138. package/dist/static/image/dashboard-background.png +0 -0
  139. package/dist/style.css +16088 -5602
  140. package/dist/s~0.js +16 -0
  141. package/dist/u~0.js +15 -6
  142. package/dist/viking-bot-composer~0.js +30 -12
  143. package/dist/viking-bot-interaction~0.js +2 -1
  144. package/package.json +14 -3
  145. package/dist/o~0.js +0 -106
package/dist/l~0.js CHANGED
@@ -1,14 +1,23 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
2
+ import { createElement, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { CSSTransition } from "react-transition-group";
3
5
  import { FilePreview as file_preview_FilePreview } from "./3~0.js";
4
- import { Tooltip } from "./r~0.js";
5
- import { getFileTypeColor, resolveFileTypeIconKey, FolderIcon, getFileTypeIcon } from "./k~0.js";
6
- import { FileBrowserEntries } from "./2~0.js";
7
- import { FileTree as file_tree_FileTree } from "./n~0.js";
8
- import { resolveLocale } from "./nx~0.js";
9
- import { OV_PATHS } from "./ck~0.js";
6
+ import { button_Button } from "./e~0.js";
7
+ import { FileTree as file_tree_FileTree, FileTreeToolbar } from "./n~0.js";
8
+ import { getFileTypeColor, getFileTypeIcon, WikiBookIcon, ExperienceBoardIcon, WikiConflictBoardIcon, FolderIcon, WikiBoardIcon, resolveFileTypeIconKey } from "./k~0.js";
9
+ import { FileBrowserEntries, es2015, lockBodyScroll } from "./2~0.js";
10
+ import { removereact } from "./4~0.js";
10
11
  import { useLocale, LocaleProvider } from "./28~0.js";
11
- import { useOvClient } from "./ae~0.js";
12
+ import { resolveLocale } from "./nx~0.js";
13
+ import { usePortalTheme } from "./9~0.js";
14
+ import { OV_PATHS, OV_PATH_BUILDERS } from "./ck~0.js";
15
+ import { useOvSignal, useOvClient, isOvClientError, useOvIdentity } from "./ae~0.js";
16
+ import { input_Input } from "./c7r~0.js";
17
+ import { ExperienceDashboard } from "./fl~0.js";
18
+ import { WikiDashboard } from "./fj~0.js";
19
+ import { chevron_downreact } from "./s~0.js";
20
+ import { checkreact } from "./0~0.js";
12
21
  function clampResizableWidth(width, min, max) {
13
22
  const rounded = Math.round(width);
14
23
  const lower = Math.max(min, rounded);
@@ -120,11 +129,11 @@ function DownloadIcon() {
120
129
  ]
121
130
  });
122
131
  }
123
- function FileBrowserDetail({ tabs, activeKey, locale, labels, footer, viewLabels, onActiveKeyChange }) {
132
+ function FileBrowserDetail({ tabs, activeKey, locale, labels, footer, titleExtra, extraActions, viewLabels, onActiveKeyChange }) {
124
133
  return /*#__PURE__*/ jsx("div", {
125
134
  className: "ov-file-browser__detail",
126
135
  children: /*#__PURE__*/ jsx(file_preview_FilePreview, {
127
- tabs: decorateTabs(tabs, viewLabels, footer),
136
+ tabs: decorateTabs(tabs, viewLabels, footer, titleExtra, extraActions),
128
137
  activeKey: activeKey ?? void 0,
129
138
  locale: locale,
130
139
  labels: labels,
@@ -132,7 +141,7 @@ function FileBrowserDetail({ tabs, activeKey, locale, labels, footer, viewLabels
132
141
  })
133
142
  });
134
143
  }
135
- function decorateTabs(tabs, viewLabels, footer) {
144
+ function decorateTabs(tabs, viewLabels, footer, titleExtra, extraActions) {
136
145
  return tabs.map((tab)=>{
137
146
  if ('content' === tab.kind) return tab;
138
147
  return {
@@ -140,9 +149,16 @@ function decorateTabs(tabs, viewLabels, footer) {
140
149
  slots: {
141
150
  ...tab.slots,
142
151
  footer,
152
+ titleExtra: /*#__PURE__*/ jsxs(Fragment, {
153
+ children: [
154
+ tab.slots?.titleExtra,
155
+ titleExtra
156
+ ]
157
+ }),
143
158
  actions: (actions)=>/*#__PURE__*/ jsxs("div", {
144
159
  className: "ov-file-browser__detail-actions",
145
160
  children: [
161
+ extraActions,
146
162
  actions.canSwitchView ? /*#__PURE__*/ jsxs("div", {
147
163
  className: "ov-file-browser__segmented",
148
164
  role: "group",
@@ -168,16 +184,22 @@ function decorateTabs(tabs, viewLabels, footer) {
168
184
  "aria-hidden": "true",
169
185
  className: "ov-file-browser__detail-sep"
170
186
  }) : null,
171
- actions.canCopyContent ? /*#__PURE__*/ jsx("button", {
172
- type: "button",
187
+ actions.canCopyContent ? /*#__PURE__*/ jsx(button_Button, {
188
+ size: "small",
189
+ type: "text",
190
+ iconOnly: true,
191
+ htmlType: "button",
173
192
  className: "ov-file-browser__detail-icon",
174
193
  "aria-label": viewLabels.copyContent,
175
194
  title: viewLabels.copyContent,
176
195
  onClick: ()=>void actions.copyContent(),
177
196
  children: /*#__PURE__*/ jsx(CopyIcon, {})
178
197
  }) : null,
179
- actions.canDownload ? /*#__PURE__*/ jsx("button", {
180
- type: "button",
198
+ actions.canDownload ? /*#__PURE__*/ jsx(button_Button, {
199
+ size: "small",
200
+ type: "text",
201
+ iconOnly: true,
202
+ htmlType: "button",
181
203
  className: "ov-file-browser__detail-icon",
182
204
  "aria-label": viewLabels.download,
183
205
  title: viewLabels.download,
@@ -221,8 +243,10 @@ function FileBrowserFailure({ message, detail, retryLabel, onRetry }) {
221
243
  className: "ov-file-browser__failure-detail",
222
244
  children: detail
223
245
  }) : null,
224
- /*#__PURE__*/ jsx("button", {
225
- type: "button",
246
+ /*#__PURE__*/ jsx(button_Button, {
247
+ size: "small",
248
+ type: "default",
249
+ htmlType: "button",
226
250
  className: "ov-file-browser__failure-retry",
227
251
  onClick: onRetry,
228
252
  children: retryLabel
@@ -238,70 +262,59 @@ function resolveTreeMaxWidth(containerWidth) {
238
262
  if (!containerWidth) return TREE_MAX_WIDTH;
239
263
  return Math.max(TREE_MIN_WIDTH, Math.min(TREE_MAX_WIDTH, containerWidth - DETAIL_MIN_WIDTH - RESIZE_FOOTPRINT));
240
264
  }
241
- const file_browser_icons_SIZE = 16;
242
- const strokeProps = {
243
- width: file_browser_icons_SIZE,
244
- height: file_browser_icons_SIZE,
245
- viewBox: '0 0 48 48',
246
- fill: 'none',
247
- stroke: 'currentColor',
248
- strokeWidth: 4,
249
- 'aria-hidden': true,
250
- focusable: 'false'
265
+ const en = {
266
+ experienceTitle: 'Experience dashboard',
267
+ wikiTitle: 'Dashboard',
268
+ openExperience: 'Open experience dashboard',
269
+ openWiki: 'Open Wiki dashboard',
270
+ wikiConflicts: 'View pending Wiki conflicts',
271
+ failed: 'Could not load dashboard information'
251
272
  };
252
- function SearchIcon() {
253
- return /*#__PURE__*/ jsx("svg", {
254
- ...strokeProps,
255
- children: /*#__PURE__*/ jsx("path", {
256
- d: "M33.072 33.071c6.248-6.248 6.248-16.379 0-22.627-6.249-6.249-16.38-6.249-22.628 0-6.248 6.248-6.248 16.379 0 22.627 6.248 6.248 16.38 6.248 22.628 0Zm0 0 8.485 8.485"
257
- })
273
+ const zh = {
274
+ experienceTitle: '经验看板',
275
+ wikiTitle: '看板',
276
+ openExperience: '打开经验看板',
277
+ openWiki: '打开 Wiki 看板',
278
+ wikiConflicts: '查看 Wiki 待确认冲突',
279
+ failed: '看板信息加载失败'
280
+ };
281
+ const dashboard_labels_dashboardLabels = (locale, labels)=>({
282
+ ...'zh-CN' === locale ? zh : en,
283
+ ...labels
258
284
  });
259
- }
260
- function RefreshIcon() {
261
- return /*#__PURE__*/ jsx("svg", {
262
- ...strokeProps,
263
- children: /*#__PURE__*/ jsx("path", {
264
- d: "M38.837 18C36.463 12.136 30.715 8 24 8 15.163 8 8 15.163 8 24s7.163 16 16 16c7.455 0 13.72-5.1 15.496-12M40 8v10H30"
265
- })
285
+ function FileBrowserToolbar({ refreshing, canRefresh, placeholder, refreshLabel, onSearch, onRefresh }) {
286
+ return /*#__PURE__*/ jsx(FileTreeToolbar, {
287
+ className: "ov-file-browser__toolbar",
288
+ search: true,
289
+ refresh: {
290
+ disabled: !canRefresh
291
+ },
292
+ refreshing: refreshing,
293
+ text: {
294
+ search: placeholder,
295
+ refresh: refreshLabel
296
+ },
297
+ onSearch: onSearch,
298
+ onRefresh: onRefresh
266
299
  });
267
300
  }
268
- function FileBrowserToolbar({ searchId, query, refreshing, canRefresh, placeholder, refreshLabel, onQueryChange, onRefresh }) {
269
- return /*#__PURE__*/ jsxs("div", {
270
- className: "ov-file-browser__toolbar",
271
- children: [
272
- /*#__PURE__*/ jsxs("label", {
273
- className: "ov-file-browser__search",
274
- htmlFor: searchId,
275
- children: [
276
- /*#__PURE__*/ jsx("span", {
277
- className: "ov-file-browser__search-icon",
278
- children: /*#__PURE__*/ jsx(SearchIcon, {})
279
- }),
280
- /*#__PURE__*/ jsx("input", {
281
- id: searchId,
282
- type: "search",
283
- className: "ov-file-browser__search-input",
284
- placeholder: placeholder,
285
- value: query,
286
- onChange: (event)=>onQueryChange(event.target.value)
287
- })
288
- ]
289
- }),
290
- /*#__PURE__*/ jsx(Tooltip, {
291
- content: refreshLabel,
292
- children: /*#__PURE__*/ jsx("button", {
293
- type: "button",
294
- className: [
295
- 'ov-file-browser__refresh',
296
- refreshing ? 'ov-file-browser__refresh--spinning' : ''
297
- ].filter(Boolean).join(' '),
298
- "aria-label": refreshLabel,
299
- disabled: !canRefresh,
300
- onClick: onRefresh,
301
- children: /*#__PURE__*/ jsx(RefreshIcon, {})
302
- })
303
- })
304
- ]
301
+ function DashboardIndicator({ dashboard, locale, labels, onOpen }) {
302
+ const text = dashboard_labels_dashboardLabels(locale, labels);
303
+ const label = 'experience' === dashboard.kind ? text.openExperience : dashboard.pendingConflicts ? text.wikiConflicts : text.openWiki;
304
+ const Icon = 'experience' === dashboard.kind ? ExperienceBoardIcon : dashboard.pendingConflicts ? WikiConflictBoardIcon : WikiBoardIcon;
305
+ return /*#__PURE__*/ jsx("button", {
306
+ type: "button",
307
+ className: `ov-file-browser__dashboard${dashboard.pendingConflicts ? ' ov-file-browser__dashboard--pending' : ''}`,
308
+ "aria-label": label,
309
+ title: label,
310
+ onClick: (event)=>{
311
+ event.stopPropagation();
312
+ onOpen(dashboard.uri);
313
+ },
314
+ children: /*#__PURE__*/ jsx(Icon, {
315
+ width: 12,
316
+ height: 12
317
+ })
305
318
  });
306
319
  }
307
320
  function fileTypeIcon(name) {
@@ -320,7 +333,7 @@ function directoryIcon() {
320
333
  className: "ov-file-tree__file-type-icon"
321
334
  });
322
335
  }
323
- function FileBrowserTreePane({ entries, locale, searchId, browser, treeLabels, searchPlaceholder, refreshLabel, searchLoading, searchEmpty, searchErrorLabel, retryLabel }) {
336
+ function FileBrowserTreePane({ entries, locale, dashboardLabels, onSearch, browser, treeLabels, searchPlaceholder, refreshLabel, retryLabel }) {
324
337
  return /*#__PURE__*/ jsxs(Fragment, {
325
338
  children: [
326
339
  /*#__PURE__*/ jsx(FileBrowserEntries, {
@@ -329,152 +342,202 @@ function FileBrowserTreePane({ entries, locale, searchId, browser, treeLabels, s
329
342
  className: "ov-file-browser__entries"
330
343
  }),
331
344
  /*#__PURE__*/ jsx(FileBrowserToolbar, {
332
- searchId: searchId,
333
- query: browser.query,
345
+ onSearch: onSearch,
334
346
  refreshing: browser.refreshing,
335
347
  canRefresh: browser.isReady,
336
348
  placeholder: searchPlaceholder,
337
349
  refreshLabel: refreshLabel,
338
- onQueryChange: browser.actions.setQuery,
339
350
  onRefresh: ()=>void browser.actions.refresh()
340
351
  }),
341
- /*#__PURE__*/ jsx("div", {
352
+ /*#__PURE__*/ jsxs("div", {
342
353
  className: "ov-file-browser__tree-scroll",
343
- children: /*#__PURE__*/ jsx(file_tree_FileTree, {
344
- locale: locale,
345
- rows: browser.rows,
346
- selectedId: browser.selectedId,
347
- isReady: browser.isReady,
348
- labels: treeLabels,
349
- emptyFallback: browser.searching ? browser.searchError ? /*#__PURE__*/ jsxs("div", {
354
+ children: [
355
+ browser.dashboardError ? /*#__PURE__*/ jsxs("div", {
350
356
  className: "ov-file-browser__hint ov-file-browser__hint--error",
357
+ "aria-live": "polite",
351
358
  children: [
352
- /*#__PURE__*/ jsx("p", {
353
- className: "ov-file-browser__failure-title",
354
- children: searchErrorLabel
355
- }),
356
- /*#__PURE__*/ jsx("p", {
357
- className: "ov-file-browser__failure-detail",
358
- children: browser.searchError.message
359
+ /*#__PURE__*/ jsxs("p", {
360
+ children: [
361
+ dashboard_labels_dashboardLabels(locale, dashboardLabels).failed,
362
+ ":",
363
+ ' ',
364
+ browser.dashboardError.message
365
+ ]
359
366
  }),
360
- /*#__PURE__*/ jsx("button", {
361
- type: "button",
362
- className: "ov-file-browser__retry",
367
+ /*#__PURE__*/ jsx(button_Button, {
368
+ size: "small",
369
+ type: "default",
370
+ htmlType: "button",
363
371
  onClick: ()=>void browser.actions.refresh(),
364
372
  children: retryLabel
365
373
  })
366
374
  ]
367
- }) : /*#__PURE__*/ jsx("p", {
368
- className: "ov-file-browser__hint",
369
- children: browser.searchLoading ? searchLoading : searchEmpty
370
- }) : void 0,
371
- renderRowIcon: (row)=>fileTypeIcon(row.name),
372
- operations: {
373
- select: browser.actions.select,
374
- toggleExpand: browser.actions.toggleExpand,
375
- loadMore: browser.actions.loadMore,
376
- retry: browser.actions.retry
377
- }
378
- })
375
+ }) : null,
376
+ /*#__PURE__*/ jsx(file_tree_FileTree, {
377
+ search: false,
378
+ refresh: false,
379
+ locale: locale,
380
+ rows: browser.rows,
381
+ selectedId: browser.selectedId,
382
+ isReady: browser.isReady,
383
+ labels: treeLabels,
384
+ renderRowIcon: (row)=>browser.dashboards.get(row.uri)?.isRoot ? /*#__PURE__*/ jsx(WikiBookIcon, {
385
+ width: 14,
386
+ height: 14
387
+ }) : fileTypeIcon(row.name),
388
+ renderDashboard: (row)=>{
389
+ const dashboard = browser.dashboards.get(row.uri);
390
+ return dashboard ? /*#__PURE__*/ jsx(DashboardIndicator, {
391
+ dashboard: dashboard,
392
+ locale: locale,
393
+ labels: dashboardLabels,
394
+ onOpen: browser.actions.openDashboard
395
+ }) : null;
396
+ },
397
+ operations: {
398
+ select: browser.actions.select,
399
+ toggleExpand: browser.actions.toggleExpand,
400
+ loadMore: browser.actions.loadMore,
401
+ retry: browser.actions.retry
402
+ }
403
+ })
404
+ ]
379
405
  })
380
406
  ]
381
407
  });
382
408
  }
383
- const messages = {
384
- formatCharacterCount: (count)=>`${count.toLocaleString('en-US')} ${1 === count ? 'character' : 'characters'}`,
385
- empty: 'Select a file to preview',
386
- error: 'Failed to load the directory',
387
- retry: 'Retry',
388
- refresh: 'Refresh',
389
- searchPlaceholder: 'Search files ⌘+K...',
390
- searchEmpty: 'No matching resources',
391
- searchFailed: 'Failed to run the search',
392
- loading: 'Loading…',
393
- searchLoading: 'Searching…',
394
- previewMode: 'Preview',
395
- sourceMode: 'Source',
396
- copyPath: 'Copy path',
397
- copyContent: 'Copy content',
398
- download: 'Download',
399
- history: 'Version history',
400
- viewSwitch: 'Switch view',
401
- resizeTree: 'Resize the directory tree',
402
- l0Summary: 'L0 Summary',
403
- l1Overview: 'L1 Overview',
404
- summaryEmpty: 'No summary generated yet',
405
- summaryFailed: 'The summary could not be loaded.'
409
+ const en_US = {
410
+ closeLabel: 'Close dialog'
406
411
  };
407
- const en_US = messages;
408
- const zh_CN_messages = {
409
- formatCharacterCount: (count)=>`${count.toLocaleString('zh-CN')} 个字符`,
410
- empty: '选择一个文件查看内容',
411
- error: '目录加载失败',
412
- retry: '重试',
413
- refresh: '刷新',
414
- searchPlaceholder: '搜索文件 ⌘+K...',
415
- searchEmpty: '没有匹配的资源',
416
- searchFailed: '搜索失败',
417
- loading: '加载中…',
418
- searchLoading: '搜索中…',
419
- previewMode: '预览',
420
- sourceMode: '源码',
421
- copyPath: '复制路径',
422
- copyContent: '复制正文',
423
- download: '下载',
424
- history: '版本历史',
425
- viewSwitch: '切换视图',
426
- resizeTree: '调整目录树宽度',
427
- l0Summary: 'L0 摘要',
428
- l1Overview: 'L1 总览',
429
- summaryEmpty: '暂无生成内容',
430
- summaryFailed: '摘要加载失败。'
412
+ const zh_CN = {
413
+ closeLabel: '关闭弹窗'
431
414
  };
432
- const zh_CN = zh_CN_messages;
433
- const LOCALES = {
434
- 'en-US': en_US,
435
- 'zh-CN': zh_CN
415
+ const MOTION = {
416
+ appear: 'ov-modal--enter',
417
+ appearActive: 'ov-modal--enter-active',
418
+ enter: 'ov-modal--enter',
419
+ enterActive: 'ov-modal--enter-active',
420
+ exitActive: 'ov-modal--exit'
436
421
  };
437
- function resolveLabels(labels, locale) {
438
- return {
439
- ...resolveLocale(LOCALES, locale, labels),
440
- tree: labels?.tree,
441
- previewLabels: labels?.preview
442
- };
443
- }
444
- function resolvePreviewLabels(text) {
445
- return {
446
- loading: text.loading,
447
- copyPath: text.copyPath,
448
- download: text.download,
449
- ...text.previewLabels
450
- };
451
- }
452
- function resolveSummaryLabels(locale, labels) {
453
- const text = resolveLabels(labels, locale);
454
- return {
455
- l0: text.l0Summary,
456
- l1: text.l1Overview,
457
- failure: text.summaryFailed
458
- };
459
- }
460
- function useContainerWidth() {
461
- const ref = useRef(null);
462
- const [width, setWidth] = useState(0);
422
+ function Modal({ open, title, triggerRef, id, className, locale: localeProp, onClose, children, closeLabel }) {
423
+ const locale = useLocale(localeProp);
424
+ const text = resolveLocale({
425
+ 'en-US': en_US,
426
+ 'zh-CN': zh_CN
427
+ }, locale);
428
+ const label = closeLabel ?? text.closeLabel;
429
+ const generatedId = useId();
430
+ const panelRef = useRef(null);
431
+ const maskRef = useRef(null);
432
+ const [present, setPresent] = useState(open);
433
+ const [layer, setLayer] = useState(null);
434
+ usePortalTheme(Boolean(layer), triggerRef, layer);
463
435
  useEffect(()=>{
464
- const element = ref.current;
465
- if (!element) return;
466
- const update = (next)=>setWidth((current)=>current === next ? current : next);
467
- update(Math.round(element.getBoundingClientRect().width));
468
- const observer = new ResizeObserver(([entry])=>{
469
- if (entry) update(Math.round(entry.contentRect.width));
470
- });
471
- observer.observe(element);
472
- return ()=>observer.disconnect();
473
- }, []);
474
- return {
475
- ref,
476
- width
477
- };
436
+ if (layer) return lockBodyScroll(layer.ownerDocument.body);
437
+ }, [
438
+ layer
439
+ ]);
440
+ if ("u" < typeof document || !open && !present) return null;
441
+ return /*#__PURE__*/ createPortal(/*#__PURE__*/ jsxs("div", {
442
+ ref: setLayer,
443
+ role: "presentation",
444
+ lang: locale,
445
+ className: [
446
+ 'ov-modal',
447
+ className
448
+ ].filter(Boolean).join(' '),
449
+ onKeyDown: (event)=>{
450
+ if ('Escape' !== event.key || event.defaultPrevented) return;
451
+ if (!(event.target instanceof Node) || !event.currentTarget.contains(event.target)) return;
452
+ event.preventDefault();
453
+ event.stopPropagation();
454
+ onClose?.();
455
+ },
456
+ children: [
457
+ /*#__PURE__*/ jsx(CSSTransition, {
458
+ nodeRef: maskRef,
459
+ in: open,
460
+ timeout: 300,
461
+ classNames: MOTION,
462
+ appear: true,
463
+ mountOnEnter: true,
464
+ unmountOnExit: true,
465
+ children: /*#__PURE__*/ jsx("button", {
466
+ ref: maskRef,
467
+ type: "button",
468
+ className: "ov-modal__mask",
469
+ "aria-label": label,
470
+ "aria-hidden": "true",
471
+ tabIndex: -1,
472
+ onClick: onClose
473
+ })
474
+ }),
475
+ /*#__PURE__*/ jsx(CSSTransition, {
476
+ nodeRef: panelRef,
477
+ in: open,
478
+ timeout: 300,
479
+ classNames: MOTION,
480
+ appear: true,
481
+ mountOnEnter: true,
482
+ unmountOnExit: true,
483
+ onEnter: ()=>setPresent(true),
484
+ onExited: ()=>setPresent(false),
485
+ children: /*#__PURE__*/ jsx("div", {
486
+ ref: panelRef,
487
+ id: id ?? generatedId,
488
+ role: "dialog",
489
+ "aria-modal": "true",
490
+ "aria-label": title,
491
+ className: "ov-modal__panel",
492
+ children: /*#__PURE__*/ jsxs(es2015, {
493
+ className: "ov-modal__focus-lock",
494
+ returnFocus: {
495
+ preventScroll: true
496
+ },
497
+ crossFrame: false,
498
+ children: [
499
+ /*#__PURE__*/ jsx("button", {
500
+ type: "button",
501
+ className: "ov-modal__close",
502
+ "aria-label": label,
503
+ disabled: !onClose,
504
+ onClick: onClose,
505
+ children: /*#__PURE__*/ jsx(removereact, {
506
+ width: 16,
507
+ height: 16,
508
+ "aria-hidden": "true"
509
+ })
510
+ }),
511
+ /*#__PURE__*/ jsx("div", {
512
+ className: "ov-modal__body",
513
+ children: children
514
+ })
515
+ ]
516
+ })
517
+ })
518
+ })
519
+ ]
520
+ }), document.body);
521
+ }
522
+ function mergeFindResult(result) {
523
+ const seen = new Set();
524
+ const entries = [];
525
+ for (const group of [
526
+ result?.memories,
527
+ result?.resources,
528
+ result?.skills
529
+ ])for (const item of group ?? []){
530
+ const uri = item?.uri;
531
+ if (!(!uri || seen.has(uri))) {
532
+ seen.add(uri);
533
+ entries.push({
534
+ uri,
535
+ isDir: false,
536
+ abstract: item.abstract?.trim() || void 0
537
+ });
538
+ }
539
+ }
540
+ return entries;
478
541
  }
479
542
  const emptyDir = ()=>({
480
543
  entries: [],
@@ -503,6 +566,7 @@ function nameFromUri(uri) {
503
566
  function toEntryRow(entry, depth, expanded, index, busy) {
504
567
  const isDir = true === entry.isDir;
505
568
  return {
569
+ tags: entry.tags,
506
570
  kind: 'entry',
507
571
  id: void 0 === index ? entry.uri : `${entry.uri}#${index}`,
508
572
  depth,
@@ -510,10 +574,13 @@ function toEntryRow(entry, depth, expanded, index, busy) {
510
574
  name: nameFromUri(entry.uri),
511
575
  type: isDir ? 'directory' : 'file',
512
576
  expanded: isDir ? expanded : void 0,
577
+ disabled: 'denied' === entry.access || void 0,
578
+ selectable: 'container' === entry.access ? false : void 0,
579
+ access: entry.access ?? ('read' === entry.permission ? 'read-only' : void 0),
513
580
  busy: busy || void 0
514
581
  };
515
582
  }
516
- function toFileTab(entry, content, maxLines, icon, openParent) {
583
+ function toFileTab(entry, content, maxLines, icon, openParent, truncated = content.split('\n').length >= maxLines) {
517
584
  const name = nameFromUri(entry.uri);
518
585
  return {
519
586
  kind: 'file',
@@ -527,7 +594,7 @@ function toFileTab(entry, content, maxLines, icon, openParent) {
527
594
  content,
528
595
  fileName: name,
529
596
  path: entry.uri,
530
- truncated: content.split('\n').length >= maxLines
597
+ truncated
531
598
  }
532
599
  };
533
600
  }
@@ -609,18 +676,316 @@ function flattenTree(uri, depth, dirs, expanded) {
609
676
  function isAbort(error) {
610
677
  return 'object' == typeof error && null !== error && 'AbortError' === error.name;
611
678
  }
612
- function toError(cause) {
613
- return cause instanceof Error ? cause : new Error(String(cause));
679
+ const toError = (cause)=>cause instanceof Error ? cause : new Error(String(cause));
680
+ const LIMIT = 20;
681
+ function useFsSearch({ client, query, targetUri, generation = 0, requestOptions }) {
682
+ const [results, setResults] = useState(null);
683
+ const [loading, setLoading] = useState(false);
684
+ const [error, setError] = useState(null);
685
+ useEffect(()=>{
686
+ if (!client || !query) {
687
+ setResults(null);
688
+ setLoading(false);
689
+ setError(null);
690
+ return;
691
+ }
692
+ const controller = new AbortController();
693
+ setLoading(true);
694
+ setError(null);
695
+ client.request({
696
+ method: 'POST',
697
+ path: OV_PATHS.searchFind,
698
+ body: {
699
+ query,
700
+ target_uri: targetUri,
701
+ limit: LIMIT
702
+ },
703
+ options: {
704
+ ...requestOptions,
705
+ signal: controller.signal
706
+ }
707
+ }).then((result)=>{
708
+ if (controller.signal.aborted) return;
709
+ setResults(mergeFindResult(result));
710
+ setLoading(false);
711
+ }, (cause)=>{
712
+ if (isAbort(cause) || controller.signal.aborted) return;
713
+ setResults([]);
714
+ setError(toError(cause));
715
+ setLoading(false);
716
+ });
717
+ return ()=>controller.abort();
718
+ }, [
719
+ client,
720
+ query,
721
+ targetUri,
722
+ generation,
723
+ requestOptions
724
+ ]);
725
+ return {
726
+ results,
727
+ loading,
728
+ error
729
+ };
614
730
  }
615
- const BINARY_EXTENSIONS = new Set([
616
- 'png',
617
- 'jpg',
618
- 'jpeg',
619
- 'gif',
620
- 'webp',
621
- 'avif',
622
- 'bmp',
623
- 'ico',
731
+ function FileBrowserSearch({ open, rootUri, triggerRef, locale, text, requestOptions, onClose }) {
732
+ const client = useOvClient();
733
+ const [draft, setDraft] = useState('');
734
+ const [request, setRequest] = useState({
735
+ query: '',
736
+ generation: 0
737
+ });
738
+ const search = useFsSearch({
739
+ client,
740
+ query: open ? request.query : '',
741
+ targetUri: rootUri,
742
+ generation: request.generation,
743
+ requestOptions
744
+ });
745
+ const close = ()=>{
746
+ setDraft('');
747
+ setRequest({
748
+ query: '',
749
+ generation: 0
750
+ });
751
+ onClose();
752
+ };
753
+ return /*#__PURE__*/ jsxs(Modal, {
754
+ open: open,
755
+ title: text.searchTitle,
756
+ locale: locale,
757
+ triggerRef: triggerRef,
758
+ className: "ov-file-browser-search",
759
+ onClose: close,
760
+ children: [
761
+ /*#__PURE__*/ jsx("h2", {
762
+ className: "ov-file-browser-search__title",
763
+ children: text.searchTitle
764
+ }),
765
+ /*#__PURE__*/ jsx("search", {
766
+ children: /*#__PURE__*/ jsxs("form", {
767
+ className: "ov-file-browser-search__form",
768
+ onSubmit: (event)=>{
769
+ event.preventDefault();
770
+ const query = draft.trim();
771
+ if (!query || !client || search.loading) return;
772
+ setRequest((current)=>({
773
+ query,
774
+ generation: current.generation + 1
775
+ }));
776
+ },
777
+ children: [
778
+ /*#__PURE__*/ jsx(input_Input, {
779
+ "data-autofocus": true,
780
+ required: true,
781
+ type: "search",
782
+ name: "query",
783
+ "aria-label": text.searchTitle,
784
+ placeholder: text.searchPlaceholder,
785
+ value: draft,
786
+ onChange: (event)=>setDraft(event.target.value)
787
+ }),
788
+ /*#__PURE__*/ jsx(button_Button, {
789
+ type: "primary",
790
+ htmlType: "submit",
791
+ loading: search.loading,
792
+ disabled: !client,
793
+ children: text.searchAction
794
+ })
795
+ ]
796
+ })
797
+ }),
798
+ /*#__PURE__*/ jsx("div", {
799
+ className: "ov-file-browser-search__results",
800
+ "aria-busy": search.loading,
801
+ children: search.loading ? /*#__PURE__*/ jsx("p", {
802
+ role: "status",
803
+ children: text.searchLoading
804
+ }) : search.error ? /*#__PURE__*/ jsxs("div", {
805
+ role: "alert",
806
+ className: "ov-file-browser-search__error",
807
+ children: [
808
+ /*#__PURE__*/ jsx("p", {
809
+ children: text.searchFailed
810
+ }),
811
+ /*#__PURE__*/ jsx("p", {
812
+ children: search.error.message
813
+ })
814
+ ]
815
+ }) : null === search.results ? /*#__PURE__*/ jsx("p", {
816
+ role: "status",
817
+ children: text.searchHint
818
+ }) : 0 === search.results.length ? /*#__PURE__*/ jsx("p", {
819
+ role: "status",
820
+ children: text.searchEmpty
821
+ }) : /*#__PURE__*/ jsx("ul", {
822
+ className: "ov-file-browser-search__list",
823
+ children: search.results.map((entry)=>/*#__PURE__*/ jsxs("li", {
824
+ className: "ov-file-browser-search__result",
825
+ children: [
826
+ /*#__PURE__*/ jsx("span", {
827
+ className: "ov-file-browser-search__uri",
828
+ children: entry.uri
829
+ }),
830
+ entry.abstract ? /*#__PURE__*/ jsx("p", {
831
+ className: "ov-file-browser-search__summary",
832
+ children: entry.abstract
833
+ }) : null
834
+ ]
835
+ }, entry.uri))
836
+ })
837
+ })
838
+ ]
839
+ });
840
+ }
841
+ const messages = {
842
+ formatCharacterCount: (count)=>`${count.toLocaleString('en-US')} ${1 === count ? 'character' : 'characters'}`,
843
+ accessDenied: 'You do not have permission to view this node.',
844
+ traverseOnly: 'This directory can only be expanded to reach accessible children.',
845
+ empty: 'Select a file to preview',
846
+ error: 'Failed to load the directory',
847
+ retry: 'Retry',
848
+ refresh: 'Refresh',
849
+ searchPlaceholder: 'Search files ⌘+K...',
850
+ searchTitle: 'Search files',
851
+ searchAction: 'Search',
852
+ searchHint: 'Enter a query to search.',
853
+ searchEmpty: 'No matching resources',
854
+ searchFailed: 'Failed to run the search',
855
+ loading: 'Loading…',
856
+ searchLoading: 'Searching…',
857
+ previewMode: 'Preview',
858
+ sourceMode: 'Source',
859
+ copyPath: 'Copy path',
860
+ copyContent: 'Copy content',
861
+ download: 'Download',
862
+ history: 'Version history',
863
+ viewSwitch: 'Switch view',
864
+ resizeTree: 'Resize the directory tree',
865
+ l0Summary: 'L0 Summary',
866
+ l1Overview: 'L1 Overview',
867
+ summaryEmpty: 'No summary generated yet',
868
+ summaryFailed: 'The summary could not be loaded.'
869
+ };
870
+ const locales_en_US = messages;
871
+ const zh_CN_messages = {
872
+ formatCharacterCount: (count)=>`${count.toLocaleString('zh-CN')} 个字符`,
873
+ accessDenied: '无权查看此节点。',
874
+ traverseOnly: '此目录仅可展开以访问有权限的子节点。',
875
+ empty: '选择一个文件查看内容',
876
+ error: '目录加载失败',
877
+ retry: '重试',
878
+ refresh: '刷新',
879
+ searchPlaceholder: '搜索文件 ⌘+K...',
880
+ searchTitle: '搜索文件',
881
+ searchAction: '搜索',
882
+ searchHint: '输入关键词后搜索。',
883
+ searchEmpty: '没有匹配的资源',
884
+ searchFailed: '搜索失败',
885
+ loading: '加载中…',
886
+ searchLoading: '搜索中…',
887
+ previewMode: '预览',
888
+ sourceMode: '源码',
889
+ copyPath: '复制路径',
890
+ copyContent: '复制正文',
891
+ download: '下载',
892
+ history: '版本历史',
893
+ viewSwitch: '切换视图',
894
+ resizeTree: '调整目录树宽度',
895
+ l0Summary: 'L0 摘要',
896
+ l1Overview: 'L1 总览',
897
+ summaryEmpty: '暂无生成内容',
898
+ summaryFailed: '摘要加载失败。'
899
+ };
900
+ const locales_zh_CN = zh_CN_messages;
901
+ const LOCALES = {
902
+ 'en-US': locales_en_US,
903
+ 'zh-CN': locales_zh_CN
904
+ };
905
+ function resolveLabels(labels, locale) {
906
+ return {
907
+ ...resolveLocale(LOCALES, locale, labels),
908
+ tree: labels?.tree,
909
+ previewLabels: labels?.preview
910
+ };
911
+ }
912
+ function resolvePreviewLabels(text) {
913
+ return {
914
+ loading: text.loading,
915
+ copyPath: text.copyPath,
916
+ download: text.download,
917
+ ...text.previewLabels
918
+ };
919
+ }
920
+ function resolveSummaryLabels(locale, labels) {
921
+ const text = resolveLabels(labels, locale);
922
+ return {
923
+ l0: text.l0Summary,
924
+ l1: text.l1Overview,
925
+ failure: text.summaryFailed
926
+ };
927
+ }
928
+ function useContainerWidth() {
929
+ const ref = useRef(null);
930
+ const [width, setWidth] = useState(0);
931
+ useEffect(()=>{
932
+ const element = ref.current;
933
+ if (!element) return;
934
+ const update = (next)=>setWidth((current)=>current === next ? current : next);
935
+ update(Math.round(element.getBoundingClientRect().width));
936
+ const observer = new ResizeObserver(([entry])=>{
937
+ if (entry) update(Math.round(entry.contentRect.width));
938
+ });
939
+ observer.observe(element);
940
+ return ()=>observer.disconnect();
941
+ }, []);
942
+ return {
943
+ ref,
944
+ width
945
+ };
946
+ }
947
+ async function readDirectory(client, uri, options) {
948
+ const read = (path)=>client.request({
949
+ method: 'GET',
950
+ path,
951
+ query: {
952
+ uri
953
+ },
954
+ options
955
+ }).then((value)=>({
956
+ ok: true,
957
+ text: 'string' == typeof value ? value : ''
958
+ })).catch((cause)=>({
959
+ ok: false,
960
+ cause
961
+ }));
962
+ const [abstractResult, overviewResult] = await Promise.all([
963
+ read(OV_PATHS.contentAbstract),
964
+ read(OV_PATHS.contentOverview)
965
+ ]);
966
+ const aborted = [
967
+ abstractResult,
968
+ overviewResult
969
+ ].find((result)=>!result.ok && isAbort(result.cause));
970
+ if (aborted && !aborted.ok) throw aborted.cause;
971
+ if (!abstractResult.ok && !overviewResult.ok) throw abstractResult.cause;
972
+ const reason = (result)=>result.ok ? void 0 : result.cause instanceof Error ? result.cause.message : String(result.cause);
973
+ return {
974
+ abstract: abstractResult.ok ? abstractResult.text : '',
975
+ overview: overviewResult.ok ? overviewResult.text : '',
976
+ abstractError: reason(abstractResult),
977
+ overviewError: reason(overviewResult)
978
+ };
979
+ }
980
+ const BINARY_EXTENSIONS = new Set([
981
+ 'png',
982
+ 'jpg',
983
+ 'jpeg',
984
+ 'gif',
985
+ 'webp',
986
+ 'avif',
987
+ 'bmp',
988
+ 'ico',
624
989
  'svg',
625
990
  'pdf',
626
991
  'doc',
@@ -695,38 +1060,152 @@ function toBinaryTab(uri, name, file, icon, openParent) {
695
1060
  }
696
1061
  };
697
1062
  }
698
- async function readDirectory(client, uri, options) {
699
- const read = (path)=>client.request({
1063
+ const LINE_BREAK_CODES = new Set([
1064
+ 0x0a,
1065
+ 0x0b,
1066
+ 0x0c,
1067
+ 0x0d,
1068
+ 0x1c,
1069
+ 0x1d,
1070
+ 0x1e,
1071
+ 0x85,
1072
+ 0x2028,
1073
+ 0x2029
1074
+ ]);
1075
+ function splitLines(text) {
1076
+ if (!text) return [];
1077
+ const lines = [];
1078
+ let start = 0;
1079
+ for(let index = 0; index < text.length; index++){
1080
+ if (!LINE_BREAK_CODES.has(text.charCodeAt(index))) continue;
1081
+ const paired = 0x0d === text.charCodeAt(index) && 0x0a === text.charCodeAt(index + 1);
1082
+ const end = index + (paired ? 2 : 1);
1083
+ lines.push(text.slice(start, end));
1084
+ start = end;
1085
+ index = end - 1;
1086
+ }
1087
+ if (start < text.length) lines.push(text.slice(start));
1088
+ return lines;
1089
+ }
1090
+ function joinLines(lines) {
1091
+ return lines.join('');
1092
+ }
1093
+ const PY_WHITESPACE_CODES = [
1094
+ 0x09,
1095
+ 0x0a,
1096
+ 0x0b,
1097
+ 0x0c,
1098
+ 0x0d,
1099
+ 0x1c,
1100
+ 0x1d,
1101
+ 0x1e,
1102
+ 0x1f,
1103
+ 0x20,
1104
+ 0x85,
1105
+ 0xa0,
1106
+ 0x1680,
1107
+ 0x2000,
1108
+ 0x2001,
1109
+ 0x2002,
1110
+ 0x2003,
1111
+ 0x2004,
1112
+ 0x2005,
1113
+ 0x2006,
1114
+ 0x2007,
1115
+ 0x2008,
1116
+ 0x2009,
1117
+ 0x200a,
1118
+ 0x2028,
1119
+ 0x2029,
1120
+ 0x202f,
1121
+ 0x205f,
1122
+ 0x3000
1123
+ ];
1124
+ const PY_WHITESPACE = String.fromCharCode(...PY_WHITESPACE_CODES);
1125
+ const PY_WORD = '\\p{L}\\p{N}_';
1126
+ const MEMORY_FIELDS_COMMENT = new RegExp(`<!--[${PY_WHITESPACE}]*MEMORY_FIELDS(?![${PY_WORD}])[\\s\\S]*?[${PY_WHITESPACE}]*-->`, 'gu');
1127
+ function isPythonBlank(text) {
1128
+ for (const char of text)if (!PY_WHITESPACE.includes(char)) return false;
1129
+ return true;
1130
+ }
1131
+ function trimPythonEnd(text) {
1132
+ let end = text.length;
1133
+ while(end > 0 && PY_WHITESPACE.includes(text[end - 1] ?? ''))end--;
1134
+ return text.slice(0, end);
1135
+ }
1136
+ function uriParts(uri) {
1137
+ const [beforeQuery = ''] = uri.split('?');
1138
+ const path = beforeQuery.replace(/\/+$/, '');
1139
+ if (!path.startsWith('viking://')) return [];
1140
+ return path.slice(9).split('/').filter(Boolean);
1141
+ }
1142
+ function isMemoryUri(uri) {
1143
+ const parts = uriParts(uri);
1144
+ if (parts.length < 3) return false;
1145
+ if ('user' !== parts[0] && '~' !== parts[0]) return false;
1146
+ const scoped = '~' === parts[0] ? [
1147
+ 'user',
1148
+ '~',
1149
+ ...parts.slice(1)
1150
+ ] : parts;
1151
+ if ('memories' === scoped[2]) return true;
1152
+ return scoped.length >= 5 && 'peers' === scoped[2] && 'memories' === scoped[4];
1153
+ }
1154
+ function stripMemoryFieldsTrailer(text) {
1155
+ let visible = text;
1156
+ for(;;){
1157
+ let start = -1;
1158
+ MEMORY_FIELDS_COMMENT.lastIndex = 0;
1159
+ let match;
1160
+ while(null !== (match = MEMORY_FIELDS_COMMENT.exec(visible))){
1161
+ const after = visible.slice(match.index + match[0].length);
1162
+ if (isPythonBlank(after)) start = match.index;
1163
+ }
1164
+ if (start < 0) return visible;
1165
+ visible = trimPythonEnd(visible.slice(0, start));
1166
+ }
1167
+ }
1168
+ const MAX_LINES = 2000;
1169
+ async function readFile(client, uri, options, openParent) {
1170
+ const name = nameFromUri(uri);
1171
+ const icon = fileTypeIcon(name);
1172
+ if (isBinaryFile(name)) {
1173
+ const blob = await client.request({
700
1174
  method: 'GET',
701
- path,
1175
+ path: OV_PATHS.contentDownload,
702
1176
  query: {
703
1177
  uri
704
1178
  },
1179
+ responseType: 'blob',
705
1180
  options
706
- }).then((value)=>({
707
- ok: true,
708
- text: 'string' == typeof value ? value : ''
709
- })).catch((cause)=>({
710
- ok: false,
711
- cause
712
- }));
713
- const [abstractResult, overviewResult] = await Promise.all([
714
- read(OV_PATHS.contentAbstract),
715
- read(OV_PATHS.contentOverview)
716
- ]);
717
- const aborted = [
718
- abstractResult,
719
- overviewResult
720
- ].find((result)=>!result.ok && isAbort(result.cause));
721
- if (aborted && !aborted.ok) throw aborted.cause;
722
- if (!abstractResult.ok && !overviewResult.ok) throw abstractResult.cause;
723
- const reason = (result)=>result.ok ? void 0 : result.cause instanceof Error ? result.cause.message : String(result.cause);
724
- return {
725
- abstract: abstractResult.ok ? abstractResult.text : '',
726
- overview: overviewResult.ok ? overviewResult.text : '',
727
- abstractError: reason(abstractResult),
728
- overviewError: reason(overviewResult)
729
- };
1181
+ });
1182
+ const typed = new Blob([
1183
+ blob
1184
+ ], {
1185
+ type: mimeTypeOf(name)
1186
+ });
1187
+ return toBinaryTab(uri, name, typed, icon, openParent);
1188
+ }
1189
+ const read = (limit)=>client.request({
1190
+ method: 'GET',
1191
+ path: OV_PATHS.contentRead,
1192
+ query: {
1193
+ uri,
1194
+ limit
1195
+ },
1196
+ options
1197
+ }).then((value)=>'string' == typeof value ? value : '');
1198
+ const first = await read(MAX_LINES);
1199
+ if (!isMemoryUri(uri)) return toFileTab({
1200
+ uri
1201
+ }, first, MAX_LINES, icon, openParent, splitLines(first).length >= MAX_LINES);
1202
+ const full = splitLines(first).length < MAX_LINES ? first : await read(-1);
1203
+ const lines = splitLines(stripMemoryFieldsTrailer(full));
1204
+ const truncated = lines.length > MAX_LINES;
1205
+ const visible = joinLines(truncated ? lines.slice(0, MAX_LINES) : lines);
1206
+ return toFileTab({
1207
+ uri
1208
+ }, visible, MAX_LINES, icon, openParent, truncated);
730
1209
  }
731
1210
  async function resolveIsDirectory(client, uri, options) {
732
1211
  try {
@@ -748,7 +1227,6 @@ function abortError() {
748
1227
  error.name = 'AbortError';
749
1228
  return error;
750
1229
  }
751
- const MAX_LINES = 2000;
752
1230
  function useFsPreview({ client, uri, resourceType = 'file', labels = resolveSummaryLabels('en-US'), formatCharacterCount = resolveLabels(void 0, 'en-US').formatCharacterCount, openParent, generation = 0, requestOptions }) {
753
1231
  const [directory, setDirectory] = useState(null);
754
1232
  const [tabs, setTabs] = useState(null);
@@ -867,108 +1345,663 @@ function loadingTab(uri, isDirectory) {
867
1345
  }
868
1346
  };
869
1347
  }
870
- async function readFile(client, uri, options, openParent) {
871
- const name = nameFromUri(uri);
872
- const icon = fileTypeIcon(name);
873
- if (isBinaryFile(name)) {
874
- const blob = await client.request({
875
- method: 'GET',
876
- path: OV_PATHS.contentDownload,
877
- query: {
878
- uri
879
- },
880
- responseType: 'blob',
881
- options
882
- });
883
- const typed = new Blob([
884
- blob
885
- ], {
886
- type: mimeTypeOf(name)
887
- });
888
- return toBinaryTab(uri, name, typed, icon, openParent);
889
- }
890
- const content = await client.request({
1348
+ const isHomeUri = (uri)=>'viking://~' === uri || uri.startsWith('viking://~/');
1349
+ async function readDashboardStat(client, uri, options) {
1350
+ const stat = await client.request({
891
1351
  method: 'GET',
892
- path: OV_PATHS.contentRead,
1352
+ path: OV_PATHS.fsStat,
893
1353
  query: {
894
- uri,
895
- limit: MAX_LINES
1354
+ uri
896
1355
  },
897
1356
  options
898
1357
  });
899
- return toFileTab({
900
- uri
901
- }, 'string' == typeof content ? content : '', MAX_LINES, icon, openParent);
1358
+ const canonical = stat?.uri ?? uri;
1359
+ if ('boolean' != typeof stat?.isDir || 'string' != typeof canonical || !canonical || isHomeUri(canonical)) throw new TypeError('Invalid canonical resource stat');
1360
+ return {
1361
+ uri: canonical,
1362
+ isDir: stat.isDir
1363
+ };
902
1364
  }
903
- function mergeFindResult(result) {
904
- const seen = new Set();
905
- const entries = [];
906
- for (const group of [
907
- result?.memories,
908
- result?.resources,
909
- result?.skills
910
- ])for (const item of group ?? []){
911
- const uri = item?.uri;
912
- if (!(!uri || seen.has(uri))) {
913
- seen.add(uri);
914
- entries.push({
915
- uri,
916
- isDir: false,
917
- abstract: item.abstract?.trim() || void 0
918
- });
919
- }
920
- }
921
- return entries;
1365
+ async function resolveDashboardUri(client, uri, options) {
1366
+ return isHomeUri(uri) ? (await readDashboardStat(client, uri, options)).uri : uri;
922
1367
  }
923
- const LIMIT = 20;
924
- function useFsSearch({ client, query, targetUri, generation = 0, requestOptions }) {
925
- const [results, setResults] = useState(null);
926
- const [loading, setLoading] = useState(false);
927
- const [error, setError] = useState(null);
1368
+ async function readDashboardHint(client, uri, options) {
1369
+ const result = await client.request({
1370
+ method: 'GET',
1371
+ path: OV_PATHS.fsAttrs,
1372
+ query: {
1373
+ uri
1374
+ },
1375
+ options
1376
+ });
1377
+ return {
1378
+ uri: await resolveDashboardUri(client, result?.uri ?? uri, options),
1379
+ tags: result?.attrs?.tags ?? []
1380
+ };
1381
+ }
1382
+ const EXPERIENCE_PAGE_SIZE = 20;
1383
+ const isExperienceDirectory = (uri)=>/^viking:\/\/(?:user\/[^/]+|~)\/memories\/experiences\/?$/.test(uri);
1384
+ const isExperienceFile = (uri)=>/^viking:\/\/(?:user\/[^/]+|~)\/memories\/experiences\/[^/]+\.md$/i.test(uri);
1385
+ function experienceDateRange(days, now = new Date()) {
1386
+ if (!days) return {};
1387
+ const start = new Date(now);
1388
+ start.setUTCDate(start.getUTCDate() - days + 1);
1389
+ return {
1390
+ start_date: start.toISOString().slice(0, 10),
1391
+ end_date: now.toISOString().slice(0, 10)
1392
+ };
1393
+ }
1394
+ async function readExperiences(client, uri, page, options) {
1395
+ uri = await resolveDashboardUri(client, uri, options);
1396
+ const result = await client.requestEnvelope({
1397
+ method: 'GET',
1398
+ path: OV_PATHS.fsLs,
1399
+ query: {
1400
+ uri,
1401
+ output: 'original',
1402
+ limit: EXPERIENCE_PAGE_SIZE,
1403
+ offset: page * EXPERIENCE_PAGE_SIZE,
1404
+ sort_by: 'mtime',
1405
+ sort_order: 'desc'
1406
+ },
1407
+ options
1408
+ });
1409
+ if (!Array.isArray(result.result)) throw new TypeError('Invalid Experience listing');
1410
+ const prefix = `${uri.replace(/\/+$/, '')}/`;
1411
+ return {
1412
+ items: result.result.filter((item)=>!item.isDir && item.uri.startsWith(prefix) && isExperienceFile(item.uri)),
1413
+ hasMore: result.hasMore
1414
+ };
1415
+ }
1416
+ async function readExperienceSources(client, uri, options) {
1417
+ uri = await resolveDashboardUri(client, uri, options);
1418
+ const result = await client.request({
1419
+ method: 'GET',
1420
+ path: OV_PATHS.fsAttrs,
1421
+ query: {
1422
+ uri
1423
+ },
1424
+ options
1425
+ });
1426
+ const links = result?.attrs?.memory?.links;
1427
+ if (void 0 !== links && !Array.isArray(links)) throw new TypeError('Invalid Experience links');
1428
+ const prefix = uri.slice(0, uri.indexOf('/memories/experiences/')) + '/memories/trajectories/';
1429
+ const seen = new Set();
1430
+ return (links ?? []).flatMap((link)=>{
1431
+ if ('derived_from' !== link.link_type || 'string' != typeof link.to_uri || !link.to_uri.startsWith(prefix) || seen.has(link.to_uri)) return [];
1432
+ seen.add(link.to_uri);
1433
+ return [
1434
+ {
1435
+ uri: link.to_uri,
1436
+ name: link.to_uri.split('/').pop() ?? link.to_uri,
1437
+ description: link.description ?? link.match_text
1438
+ }
1439
+ ];
1440
+ });
1441
+ }
1442
+ const dashboardKey = (uri)=>`dashboard:${uri}`;
1443
+ const within = (uri, root)=>uri === root || uri.startsWith(`${root.replace(/\/+$/, '')}/`);
1444
+ function resolveDashboard(uri, rows, targets) {
1445
+ if (isExperienceDirectory(uri) || isExperienceFile(uri)) return {
1446
+ kind: 'experience',
1447
+ uri
1448
+ };
1449
+ const entries = rows.filter((row)=>'entry' === row.kind);
1450
+ const row = entries.find((item)=>item.uri === uri);
1451
+ const hasWiki = entries.some((item)=>item.tags?.includes('compile_target=true') && within(uri, item.uri));
1452
+ const owner = targets.filter((item)=>within(uri, item.to)).sort((a, b)=>b.to.length - a.to.length)[0];
1453
+ if (!hasWiki && !owner) return;
1454
+ const tagged = row?.tags?.find((tag)=>tag.startsWith('has_conflict='));
1455
+ return {
1456
+ kind: 'wiki',
1457
+ uri,
1458
+ isRoot: Boolean(row?.tags?.includes('compile_target=true') || owner?.to === uri),
1459
+ pendingConflicts: tagged ? 'has_conflict=true' === tagged : owner?.to === uri && void 0 !== owner.conflicts_num ? owner.conflicts_num > 0 : void 0
1460
+ };
1461
+ }
1462
+ function mergeDashboardTab(tabs, dashboard, blocked) {
1463
+ return blocked ? null : dashboard ? [
1464
+ ...tabs ?? [],
1465
+ dashboard
1466
+ ] : tabs;
1467
+ }
1468
+ async function readCompileTargets(client, rootUri, options) {
1469
+ rootUri = await resolveDashboardUri(client, rootUri, options);
1470
+ const targets = [];
1471
+ let cursor;
1472
+ const seen = new Set();
1473
+ do {
1474
+ const result = await client.request({
1475
+ method: 'GET',
1476
+ path: OV_PATHS.compileConfigs,
1477
+ query: {
1478
+ limit: 100,
1479
+ cursor
1480
+ },
1481
+ options
1482
+ });
1483
+ if (!Array.isArray(result?.targets)) throw new TypeError('Invalid Compile target registry');
1484
+ targets.push(...result.targets.filter((item)=>'string' == typeof item.to && 'memory' !== item.compile_type && false !== item.initialized && (within(item.to, rootUri) || within(rootUri, item.to))));
1485
+ cursor = result.next_cursor ?? void 0;
1486
+ if (cursor && seen.has(cursor)) throw new TypeError('Compile target cursor did not advance');
1487
+ if (cursor) seen.add(cursor);
1488
+ }while (cursor)
1489
+ return targets;
1490
+ }
1491
+ function useDashboardQuery(load, requestOptions, enabled = true, refreshGeneration = 0) {
1492
+ const client = useOvClient();
1493
+ const inheritedSignal = useOvSignal();
1494
+ const { baseURL, apiKey, accountId, userId, resourceId, signal } = requestOptions ?? {};
1495
+ const [generation, setGeneration] = useState(0);
1496
+ const [state, setState] = useState({
1497
+ owner: [],
1498
+ error: null
1499
+ });
1500
+ const owner = useMemo(()=>[
1501
+ client,
1502
+ load,
1503
+ baseURL,
1504
+ apiKey,
1505
+ accountId,
1506
+ userId,
1507
+ resourceId,
1508
+ signal,
1509
+ inheritedSignal,
1510
+ generation,
1511
+ enabled,
1512
+ refreshGeneration
1513
+ ], [
1514
+ client,
1515
+ load,
1516
+ baseURL,
1517
+ apiKey,
1518
+ accountId,
1519
+ userId,
1520
+ resourceId,
1521
+ signal,
1522
+ inheritedSignal,
1523
+ generation,
1524
+ enabled,
1525
+ refreshGeneration
1526
+ ]);
1527
+ const current = owner.every((value, i)=>value === state.owner[i]);
928
1528
  useEffect(()=>{
929
- if (!client || !query) {
930
- setResults(null);
931
- setLoading(false);
932
- setError(null);
933
- return;
934
- }
1529
+ if (!enabled) return;
1530
+ if (!client) return void setState({
1531
+ owner,
1532
+ error: new Error('OpenVikingProvider is required')
1533
+ });
935
1534
  const controller = new AbortController();
936
- setLoading(true);
937
- setError(null);
938
- client.request({
939
- method: 'POST',
940
- path: OV_PATHS.searchFind,
941
- body: {
942
- query,
943
- target_uri: targetUri,
944
- limit: LIMIT
945
- },
946
- options: {
947
- ...requestOptions,
948
- signal: controller.signal
949
- }
950
- }).then((result)=>{
951
- if (controller.signal.aborted) return;
952
- setResults(mergeFindResult(result));
953
- setLoading(false);
1535
+ const signals = [
1536
+ controller.signal,
1537
+ signal,
1538
+ inheritedSignal
1539
+ ].filter((value)=>Boolean(value));
1540
+ const options = {
1541
+ baseURL,
1542
+ apiKey,
1543
+ accountId,
1544
+ userId,
1545
+ resourceId,
1546
+ signal: AbortSignal.any(signals)
1547
+ };
1548
+ load(client, options).then((data)=>{
1549
+ if (!options.signal.aborted) setState({
1550
+ owner,
1551
+ data,
1552
+ error: null
1553
+ });
954
1554
  }, (cause)=>{
955
- if (isAbort(cause) || controller.signal.aborted) return;
956
- setResults([]);
957
- setError(toError(cause));
958
- setLoading(false);
1555
+ if (!options.signal.aborted) setState({
1556
+ owner,
1557
+ error: cause instanceof Error ? cause : new Error(String(cause))
1558
+ });
959
1559
  });
960
1560
  return ()=>controller.abort();
961
1561
  }, [
962
1562
  client,
963
- query,
964
- targetUri,
1563
+ load,
1564
+ baseURL,
1565
+ apiKey,
1566
+ accountId,
1567
+ userId,
1568
+ resourceId,
1569
+ signal,
1570
+ inheritedSignal,
965
1571
  generation,
966
- requestOptions
1572
+ enabled,
1573
+ owner
967
1574
  ]);
968
1575
  return {
969
- results,
970
- loading,
971
- error
1576
+ data: current ? state.data : void 0,
1577
+ loading: enabled && !current,
1578
+ error: current ? state.error : null,
1579
+ retry: ()=>setGeneration((value)=>value + 1)
1580
+ };
1581
+ }
1582
+ function useExperienceDashboard({ uri, requestOptions, enabled = true, refreshGeneration = 0 }) {
1583
+ const client = useOvClient();
1584
+ const signal = useOvSignal();
1585
+ const owner = useMemo(()=>[
1586
+ uri,
1587
+ client,
1588
+ signal,
1589
+ requestOptions?.baseURL,
1590
+ requestOptions?.apiKey,
1591
+ requestOptions?.accountId,
1592
+ requestOptions?.userId,
1593
+ requestOptions?.resourceId
1594
+ ], [
1595
+ uri,
1596
+ client,
1597
+ signal,
1598
+ requestOptions?.baseURL,
1599
+ requestOptions?.apiKey,
1600
+ requestOptions?.accountId,
1601
+ requestOptions?.userId,
1602
+ requestOptions?.resourceId
1603
+ ]);
1604
+ const [state, setState] = useState({
1605
+ owner,
1606
+ page: 0,
1607
+ days: 7,
1608
+ tab: 'impact'
1609
+ });
1610
+ const current = state.owner === owner ? state : {
1611
+ owner,
1612
+ page: 0,
1613
+ days: 7,
1614
+ tab: 'impact'
1615
+ };
1616
+ const { page, days, tab } = current;
1617
+ const setPage = (value)=>setState({
1618
+ ...current,
1619
+ page: Math.max(0, value)
1620
+ });
1621
+ const setDays = (value)=>setState({
1622
+ ...current,
1623
+ days: value,
1624
+ page: 0
1625
+ });
1626
+ const setTab = (value)=>setState({
1627
+ ...current,
1628
+ tab: value,
1629
+ page: 0
1630
+ });
1631
+ const directory = isExperienceDirectory(uri);
1632
+ const loadList = useCallback((client, options)=>readExperiences(client, uri, page, options), [
1633
+ uri,
1634
+ page
1635
+ ]);
1636
+ const loadSources = useCallback((client, options)=>readExperienceSources(client, uri, options), [
1637
+ uri
1638
+ ]);
1639
+ const loadTrajectories = useCallback(async (client, options)=>{
1640
+ const result = await client.request({
1641
+ method: 'GET',
1642
+ path: OV_PATHS.experienceTrajectories,
1643
+ query: {
1644
+ experience_uri: uri,
1645
+ offset: 20 * page,
1646
+ limit: 20,
1647
+ ...experienceDateRange(days)
1648
+ },
1649
+ options
1650
+ });
1651
+ if (!Array.isArray(result?.items) || !Number.isSafeInteger(result.total) || result.total < 0 || result.items.some((item)=>'string' != typeof item?.uri || 'string' != typeof item.name)) throw new TypeError('Invalid Experience trajectories');
1652
+ return result;
1653
+ }, [
1654
+ uri,
1655
+ page,
1656
+ days
1657
+ ]);
1658
+ const loadOutcomes = useCallback(async (client, options)=>{
1659
+ const result = await client.request({
1660
+ method: 'GET',
1661
+ path: OV_PATHS.experienceOutcomes,
1662
+ query: {
1663
+ experience_uri: uri,
1664
+ ...experienceDateRange(days)
1665
+ },
1666
+ options
1667
+ });
1668
+ if (!Array.isArray(result?.outcome_distribution) || result.outcome_distribution.some((entry)=>![
1669
+ 'success',
1670
+ 'partial',
1671
+ 'unfinished',
1672
+ 'failure',
1673
+ 'unknown'
1674
+ ].includes(entry?.outcome) || !Number.isSafeInteger(entry.count) || entry.count < 0)) throw new TypeError('Invalid Experience outcomes');
1675
+ return result.outcome_distribution;
1676
+ }, [
1677
+ uri,
1678
+ days
1679
+ ]);
1680
+ const list = useDashboardQuery(loadList, requestOptions, enabled && directory, refreshGeneration);
1681
+ const sources = useDashboardQuery(loadSources, requestOptions, enabled && !directory && 'source' === tab, refreshGeneration);
1682
+ const trajectories = useDashboardQuery(loadTrajectories, requestOptions, enabled && !directory && 'impact' === tab, refreshGeneration);
1683
+ const outcomes = useDashboardQuery(loadOutcomes, requestOptions, enabled && !directory && 'impact' === tab, refreshGeneration);
1684
+ return {
1685
+ list,
1686
+ sources,
1687
+ trajectories,
1688
+ outcomes,
1689
+ page,
1690
+ setPage,
1691
+ days,
1692
+ setDays,
1693
+ tab,
1694
+ setTab
1695
+ };
1696
+ }
1697
+ function ExperienceDashboardPane({ uri, locale, requestOptions, refreshGeneration, onOpenUri }) {
1698
+ const browser = useExperienceDashboard({
1699
+ uri,
1700
+ requestOptions,
1701
+ refreshGeneration
1702
+ });
1703
+ const directory = isExperienceDirectory(uri);
1704
+ const status = (query)=>({
1705
+ loading: query.loading,
1706
+ error: query.error,
1707
+ onRetry: query.retry
1708
+ });
1709
+ return /*#__PURE__*/ jsx(ExperienceDashboard, {
1710
+ uri: uri,
1711
+ kind: directory ? 'directory' : 'file',
1712
+ locale: locale,
1713
+ onOpenUri: onOpenUri,
1714
+ data: {
1715
+ experiences: browser.list.data?.items,
1716
+ outcomes: browser.outcomes.data,
1717
+ trajectories: browser.trajectories.data?.items,
1718
+ trajectoryTotal: browser.trajectories.data?.total,
1719
+ sources: browser.sources.data
1720
+ },
1721
+ tab: browser.tab,
1722
+ days: browser.days,
1723
+ page: browser.page,
1724
+ startIndex: 20 * browser.page,
1725
+ hasMore: Boolean(directory ? browser.list.data?.hasMore : 'impact' === browser.tab && browser.trajectories.data?.has_more),
1726
+ states: {
1727
+ list: status(browser.list),
1728
+ sources: status(browser.sources),
1729
+ outcomes: status(browser.outcomes),
1730
+ trajectories: status(browser.trajectories)
1731
+ },
1732
+ onTabChange: browser.setTab,
1733
+ onDaysChange: browser.setDays,
1734
+ onPageChange: browser.setPage
1735
+ });
1736
+ }
1737
+ const wiki_conflicts_prefix = '<!-- CONFLICTS_CONTENT';
1738
+ const manualResolutionBasis = '\u4eba\u5de5\u4fee\u590d';
1739
+ const timestamp = (value)=>'number' == typeof value && Number.isSafeInteger(value) && value >= 0 && Number.isFinite(new Date(1000 * value).getTime());
1740
+ const locateWikiConflict = (document1, matchText)=>{
1741
+ const mask = (value)=>value.replace(/[^\r\n]/g, ' ');
1742
+ const body = document1.replace(/^(?:\uFEFF)?---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/, mask).replace(/<!--[\s\S]*?(?:-->|$)/g, mask);
1743
+ const start = matchText ? body.indexOf(matchText) : -1;
1744
+ if (start < 0) return {
1745
+ status: 'missing'
1746
+ };
1747
+ if (body.indexOf(matchText, start + 1) >= 0) return {
1748
+ status: 'ambiguous'
1749
+ };
1750
+ return {
1751
+ status: 'unique',
1752
+ start,
1753
+ end: start + matchText.length,
1754
+ line: document1.slice(0, start).split('\n').length
1755
+ };
1756
+ };
1757
+ const parseWikiFileConflicts = (content)=>{
1758
+ const start = content.lastIndexOf(wiki_conflicts_prefix);
1759
+ if (start < 0) return {
1760
+ status: 'missing'
1761
+ };
1762
+ const footer = content.slice(start).trimEnd();
1763
+ if (!footer.endsWith('-->')) return {
1764
+ status: 'invalid'
1765
+ };
1766
+ const document1 = content.slice(0, start);
1767
+ const source = footer.slice(wiki_conflicts_prefix.length, -3).trim();
1768
+ let parsed;
1769
+ try {
1770
+ parsed = source ? JSON.parse(`[${source}]`) : [];
1771
+ } catch {
1772
+ return {
1773
+ status: 'invalid'
1774
+ };
1775
+ }
1776
+ if (!Array.isArray(parsed)) return {
1777
+ status: 'invalid'
1778
+ };
1779
+ const records = [];
1780
+ for (const [index, raw] of parsed.entries()){
1781
+ if (!raw || 'object' != typeof raw || Array.isArray(raw)) return {
1782
+ status: 'invalid'
1783
+ };
1784
+ const value = raw;
1785
+ if ('string' != typeof value.match_text || !value.match_text || 'string' != typeof value.problem_description || 'string' != typeof value.reason || ![
1786
+ 'high',
1787
+ 'medium',
1788
+ 'low'
1789
+ ].includes(value.severity) || ![
1790
+ 'unfixed',
1791
+ 'fixed'
1792
+ ].includes(value.state) || !timestamp(value.begin_time) || 'string' != typeof value.fix_plan || 'string' != typeof value.resolution_basis) return {
1793
+ status: 'invalid'
1794
+ };
1795
+ if ('fixed' === value.state ? !timestamp(value.fix_time) || value.fix_time < value.begin_time : null !== value.fix_time) return {
1796
+ status: 'invalid'
1797
+ };
1798
+ const matchText = value.match_text;
1799
+ const location = locateWikiConflict(document1, matchText);
1800
+ records.push({
1801
+ id: JSON.stringify([
1802
+ index,
1803
+ value.begin_time,
1804
+ matchText
1805
+ ]),
1806
+ matchText,
1807
+ description: value.problem_description,
1808
+ reason: value.reason,
1809
+ severity: value.severity,
1810
+ state: value.state,
1811
+ beginTime: value.begin_time,
1812
+ fixTime: value.fix_time,
1813
+ fixPlan: value.fix_plan,
1814
+ resolutionBasis: value.resolution_basis,
1815
+ resolutionSource: 'fixed' === value.state && value.resolution_basis.trim() ? value.resolution_basis.trim() === manualResolutionBasis ? 'manual' : 'automatic' : void 0,
1816
+ location
1817
+ });
1818
+ }
1819
+ return {
1820
+ status: 'available',
1821
+ document: document1,
1822
+ records
1823
+ };
1824
+ };
1825
+ async function readWikiDashboard(client, uri, options) {
1826
+ const stat = await readDashboardStat(client, uri, options);
1827
+ uri = stat.uri;
1828
+ if (!stat.isDir) {
1829
+ const result = await client.request({
1830
+ method: 'GET',
1831
+ path: OV_PATHS.contentRead,
1832
+ query: {
1833
+ uri,
1834
+ limit: -1
1835
+ },
1836
+ options
1837
+ });
1838
+ if ('string' != typeof result) throw new TypeError('Invalid Wiki content response');
1839
+ const parsed = parseWikiFileConflicts(result);
1840
+ if ('invalid' === parsed.status) throw new TypeError('Invalid CONFLICTS_CONTENT metadata');
1841
+ return {
1842
+ kind: 'file',
1843
+ data: {
1844
+ path: uri,
1845
+ conflicts: 'available' === parsed.status ? parsed.records : []
1846
+ }
1847
+ };
1848
+ }
1849
+ const page = await client.requestEnvelope({
1850
+ method: 'GET',
1851
+ path: OV_PATHS.fsLs,
1852
+ query: {
1853
+ uri,
1854
+ recursive: true,
1855
+ output: 'agent',
1856
+ tags: [
1857
+ 'has_conflict=true'
1858
+ ],
1859
+ limit: 100,
1860
+ sort_by: 'mtime',
1861
+ sort_order: 'desc'
1862
+ },
1863
+ options
1864
+ });
1865
+ if (!Array.isArray(page.result)) throw new TypeError('Invalid Wiki conflict listing');
1866
+ const prefix = `${uri.replace(/\/+$/, '')}/`;
1867
+ const files = page.result.filter((entry)=>!entry.isDir && entry.uri.startsWith(prefix)).map((entry)=>entry.uri);
1868
+ return {
1869
+ kind: 'directory',
1870
+ data: {
1871
+ files,
1872
+ conflictFileCount: files.length,
1873
+ limited: page.hasMore || files.length >= 100
1874
+ }
1875
+ };
1876
+ }
1877
+ function useWikiDashboard({ uri, requestOptions, enabled = true, refreshGeneration = 0 }) {
1878
+ const load = useCallback((client, options)=>readWikiDashboard(client, uri, options), [
1879
+ uri
1880
+ ]);
1881
+ return useDashboardQuery(load, requestOptions, enabled, refreshGeneration);
1882
+ }
1883
+ function WikiDashboardPane({ uri, kind, requestOptions, refreshGeneration, onOpenUri, onOpenDashboard, onEdit, onDiscuss, ...props }) {
1884
+ const query = useWikiDashboard({
1885
+ uri,
1886
+ requestOptions,
1887
+ refreshGeneration
1888
+ });
1889
+ const common = {
1890
+ ...props,
1891
+ loading: query.loading,
1892
+ error: query.error,
1893
+ onRetry: query.retry
1894
+ };
1895
+ if ((query.data?.kind ?? kind) === 'file') return /*#__PURE__*/ createElement(WikiDashboard, {
1896
+ ...common,
1897
+ key: uri,
1898
+ kind: "file",
1899
+ data: query.data?.kind === 'file' ? query.data.data : void 0,
1900
+ onOpenUri: onOpenUri && (()=>onOpenUri(uri)),
1901
+ onEdit: onEdit && ((conflict)=>onEdit(uri, conflict)),
1902
+ onDiscuss: onDiscuss && ((conflict)=>onDiscuss(uri, conflict))
1903
+ });
1904
+ return /*#__PURE__*/ createElement(WikiDashboard, {
1905
+ ...common,
1906
+ key: uri,
1907
+ kind: "directory",
1908
+ data: query.data?.kind === 'directory' ? query.data.data : void 0,
1909
+ onOpenUri: onOpenUri,
1910
+ onOpenDashboard: onOpenDashboard,
1911
+ onEdit: onEdit
1912
+ });
1913
+ }
1914
+ function useBrowserDashboards({ rows, metadataRows, uri, rootUri, generation, locale, requestOptions, onOpenUri, dashboardKinds, renderDashboard, dashboardLabels }) {
1915
+ const [opened, setOpened] = useState(null);
1916
+ const load = useCallback((client, options)=>readCompileTargets(client, rootUri, options), [
1917
+ rootUri
1918
+ ]);
1919
+ const wikiEnabled = void 0 === dashboardKinds || dashboardKinds.includes('wiki');
1920
+ const catalog = useDashboardQuery(load, requestOptions, wikiEnabled, generation);
1921
+ const loadHint = useCallback(async (client, options)=>uri ? readDashboardHint(client, uri, options) : void 0, [
1922
+ uri
1923
+ ]);
1924
+ const hint = useDashboardQuery(loadHint, requestOptions, wikiEnabled && Boolean(uri), generation);
1925
+ const canonicalUri = hint.data?.uri ?? uri;
1926
+ const knownRows = canonicalUri && hint.data?.tags.includes('compile_target=true') ? [
1927
+ ...metadataRows,
1928
+ {
1929
+ kind: 'entry',
1930
+ uri: canonicalUri,
1931
+ id: canonicalUri,
1932
+ name: canonicalUri,
1933
+ depth: 0,
1934
+ type: 'directory',
1935
+ tags: hint.data.tags
1936
+ }
1937
+ ] : metadataRows;
1938
+ const unsupported = isOvClientError(catalog.error) && 404 === catalog.error.statusCode;
1939
+ const targets = catalog.data ?? [];
1940
+ const resolve = (target)=>{
1941
+ const match = resolveDashboard(target === uri ? canonicalUri ?? target : target, knownRows, targets);
1942
+ return match ? {
1943
+ ...match,
1944
+ uri: target
1945
+ } : void 0;
1946
+ };
1947
+ const dashboards = new Map(rows.filter((row)=>'entry' === row.kind).filter((row)=>!row.disabled && false !== row.selectable).flatMap((row)=>{
1948
+ const dashboard = resolve(row.uri);
1949
+ return dashboard && (void 0 === dashboardKinds || dashboardKinds.includes(dashboard.kind)) ? [
1950
+ [
1951
+ row.uri,
1952
+ dashboard
1953
+ ]
1954
+ ] : [];
1955
+ }));
1956
+ const resolved = uri ? resolve(uri) : void 0;
1957
+ const current = resolved && (void 0 === dashboardKinds || dashboardKinds.includes(resolved.kind)) ? resolved : void 0;
1958
+ const open = useCallback((target)=>{
1959
+ setOpened(target);
1960
+ onOpenUri(target);
1961
+ }, [
1962
+ onOpenUri
1963
+ ]);
1964
+ const close = useCallback(()=>setOpened(null), []);
1965
+ const text = dashboard_labels_dashboardLabels(locale, dashboardLabels);
1966
+ const title = current?.kind === 'experience' ? text.experienceTitle : text.wikiTitle;
1967
+ const tab = current && uri ? {
1968
+ key: dashboardKey(uri),
1969
+ title,
1970
+ icon: createElement('experience' === current.kind ? ExperienceBoardIcon : WikiBoardIcon, {
1971
+ width: 14,
1972
+ height: 14
1973
+ }),
1974
+ kind: 'content',
1975
+ content: renderDashboard?.(current) ?? ('experience' === current.kind ? createElement(ExperienceDashboardPane, {
1976
+ uri,
1977
+ locale,
1978
+ requestOptions,
1979
+ refreshGeneration: generation,
1980
+ onOpenUri: (target)=>{
1981
+ setOpened(null);
1982
+ onOpenUri(target);
1983
+ }
1984
+ }) : createElement(WikiDashboardPane, {
1985
+ uri,
1986
+ kind: knownRows.some((row)=>'entry' === row.kind && row.uri === canonicalUri && 'file' === row.type) ? 'file' : 'directory',
1987
+ locale,
1988
+ requestOptions,
1989
+ refreshGeneration: generation,
1990
+ onOpenUri: (target)=>{
1991
+ setOpened(null);
1992
+ onOpenUri(target);
1993
+ },
1994
+ onOpenDashboard: open
1995
+ }))
1996
+ } : void 0;
1997
+ return {
1998
+ dashboards,
1999
+ tab,
2000
+ isOpen: null !== uri && opened === uri && Boolean(current),
2001
+ open,
2002
+ close,
2003
+ error: unsupported ? null : catalog.error,
2004
+ retry: catalog.retry
972
2005
  };
973
2006
  }
974
2007
  function knownDirectories(rootUri, dirs) {
@@ -994,6 +2027,28 @@ function useKnownFile(dirs) {
994
2027
  files
995
2028
  ]);
996
2029
  }
2030
+ function useTreeMetadata(rootUri, dirs) {
2031
+ const directoryUris = useMemo(()=>knownDirectories(rootUri, dirs), [
2032
+ rootUri,
2033
+ dirs
2034
+ ]);
2035
+ const isKnownFile = useKnownFile(dirs);
2036
+ const metadataRows = useMemo(()=>Object.values(dirs).flatMap((dir)=>dir.entries.map((entry)=>toEntryRow(entry, 0, false))), [
2037
+ dirs
2038
+ ]);
2039
+ const accessOf = useCallback((uri)=>{
2040
+ const row = metadataRows.find((entry)=>entry.uri === uri);
2041
+ return row?.access === 'denied' || row?.access === 'container' ? row.access : void 0;
2042
+ }, [
2043
+ metadataRows
2044
+ ]);
2045
+ return {
2046
+ directoryUris,
2047
+ isKnownFile,
2048
+ metadataRows,
2049
+ accessOf
2050
+ };
2051
+ }
997
2052
  const ids = new WeakMap();
998
2053
  let nextId = 1;
999
2054
  function idOf(value) {
@@ -1064,6 +2119,7 @@ async function loadPage(client, uri, offset, signal, requestOptions) {
1064
2119
  query: {
1065
2120
  uri,
1066
2121
  output: 'agent',
2122
+ include_tags: true,
1067
2123
  node_limit: PAGE_SIZE,
1068
2124
  offset
1069
2125
  },
@@ -1189,7 +2245,16 @@ function useFsTree({ client, rootUri, defaultExpandedUri, requestOptions }) {
1189
2245
  setExpanded((prev)=>new Set(prev).add(uri));
1190
2246
  if (!dirsRef.current[uri]) await fetchPage(uri, 0);
1191
2247
  },
2248
+ openPath: async (uri)=>{
2249
+ const current = scope.current();
2250
+ for (const parent of expandChain(rootUri, uri)){
2251
+ if (current.controller.signal.aborted || scope.current().key !== current.key) return;
2252
+ setExpanded((prev)=>new Set(prev).add(parent));
2253
+ if (!dirsRef.current[parent]) await fetchPage(parent, 0);
2254
+ }
2255
+ },
1192
2256
  toggleExpand: async (row)=>{
2257
+ if (row.disabled) return;
1193
2258
  const isOpen = expandedRef.current.has(row.uri);
1194
2259
  setExpanded((prev)=>{
1195
2260
  const next = new Set(prev);
@@ -1226,24 +2291,18 @@ function useFsTree({ client, rootUri, defaultExpandedUri, requestOptions }) {
1226
2291
  dirs,
1227
2292
  expanded
1228
2293
  ]);
1229
- const directoryUris = useMemo(()=>knownDirectories(rootUri, dirs), [
1230
- rootUri,
1231
- dirs
1232
- ]);
1233
- const isKnownFile = useKnownFile(dirs);
1234
2294
  return {
1235
2295
  rows,
2296
+ ...useTreeMetadata(rootUri, dirs),
1236
2297
  isReady,
1237
2298
  error,
1238
- directoryUris,
1239
- isKnownFile,
1240
2299
  ...state
1241
2300
  };
1242
2301
  }
1243
- function useFileBrowser({ rootUri, locale: localeProp, controlled = false, summaryLabels, formatCharacterCount, defaultExpandedUri, currentUri, requestOptions, onUriChange }) {
2302
+ function useFileBrowser({ rootUri, locale: localeProp, dashboardKinds, renderDashboard, dashboardLabels, controlled = false, summaryLabels, accessLabels, formatCharacterCount, defaultExpandedUri, currentUri, requestOptions, onUriChange }) {
1244
2303
  const locale = useLocale(localeProp);
2304
+ const text = resolveLabels(accessLabels, locale);
1245
2305
  const client = useOvClient();
1246
- const [query, setQuery] = useState('');
1247
2306
  const [pendingUri, setPendingUri] = useState(currentUri);
1248
2307
  const [refreshing, setRefreshing] = useState(false);
1249
2308
  const [refreshGeneration, setRefreshGeneration] = useState(0);
@@ -1253,14 +2312,8 @@ function useFileBrowser({ rootUri, locale: localeProp, controlled = false, summa
1253
2312
  defaultExpandedUri,
1254
2313
  requestOptions
1255
2314
  });
1256
- const search = useFsSearch({
1257
- client,
1258
- query,
1259
- targetUri: rootUri,
1260
- generation: refreshGeneration,
1261
- requestOptions
1262
- });
1263
2315
  const activeUri = controlled ? currentUri : pendingUri;
2316
+ const blocked = tree.accessOf(activeUri);
1264
2317
  const resourceType = null === activeUri ? 'file' : tree.directoryUris.has(activeUri) ? 'directory' : tree.isKnownFile(activeUri) ? 'file' : 'unknown';
1265
2318
  const openTree = tree.open;
1266
2319
  const notifyUriChange = useRef(onUriChange);
@@ -1274,32 +2327,46 @@ function useFileBrowser({ rootUri, locale: localeProp, controlled = false, summa
1274
2327
  ]);
1275
2328
  const preview = useFsPreview({
1276
2329
  client,
1277
- uri: activeUri,
2330
+ uri: blocked ? null : activeUri,
1278
2331
  resourceType,
1279
2332
  labels: summaryLabels ?? resolveSummaryLabels(locale),
1280
- formatCharacterCount: formatCharacterCount ?? resolveLabels(void 0, locale).formatCharacterCount,
2333
+ formatCharacterCount: formatCharacterCount ?? text.formatCharacterCount,
1281
2334
  openParent,
1282
2335
  generation: refreshGeneration,
1283
2336
  requestOptions
1284
2337
  });
1285
- const rows = useMemo(()=>{
1286
- if (null === search.results) return tree.rows;
1287
- return search.results.map((entry, index)=>toEntryRow(entry, 0, false, index));
1288
- }, [
1289
- search.results,
1290
- tree.rows
1291
- ]);
1292
- const selectedId = useMemo(()=>{
1293
- if (!activeUri || null === search.results) return activeUri;
1294
- const index = search.results.findIndex((entry)=>entry.uri === activeUri);
1295
- return index < 0 ? activeUri : `${activeUri}#${index}`;
1296
- }, [
1297
- activeUri,
1298
- search.results
2338
+ const rows = tree.rows;
2339
+ const openPath = tree.openPath;
2340
+ const openResource = useMemo(()=>async (uri)=>{
2341
+ setPendingUri(uri);
2342
+ notifyUriChange.current(uri);
2343
+ const directory = tree.directoryUris.has(uri) ? uri : uri.slice(0, uri.lastIndexOf('/'));
2344
+ await openPath(directory);
2345
+ }, [
2346
+ openPath,
2347
+ tree.directoryUris
1299
2348
  ]);
2349
+ const boards = useBrowserDashboards({
2350
+ rows,
2351
+ metadataRows: tree.metadataRows,
2352
+ uri: blocked ? null : activeUri,
2353
+ rootUri,
2354
+ generation: refreshGeneration,
2355
+ locale,
2356
+ requestOptions,
2357
+ onOpenUri: openResource,
2358
+ dashboardKinds,
2359
+ renderDashboard,
2360
+ dashboardLabels
2361
+ });
2362
+ const selectedId = activeUri;
1300
2363
  const { open, toggleExpand, loadMore, retry, refresh } = tree;
2364
+ const { open: openDashboard, close: closeDashboard } = boards;
2365
+ const setActiveKey = preview.setActiveKey;
1301
2366
  const actions = useMemo(()=>({
1302
2367
  select: async (row)=>{
2368
+ if (row.disabled || false === row.selectable) return;
2369
+ closeDashboard();
1303
2370
  setPendingUri(row.uri);
1304
2371
  notifyUriChange.current(row.uri);
1305
2372
  if ('directory' === row.type) await open(row.uri);
@@ -1322,40 +2389,1286 @@ function useFileBrowser({ rootUri, locale: localeProp, controlled = false, summa
1322
2389
  setRefreshing(false);
1323
2390
  }
1324
2391
  },
1325
- setPreviewKey: preview.setActiveKey,
1326
- setQuery
2392
+ openDashboard: openDashboard,
2393
+ setPreviewKey: (key)=>{
2394
+ if (activeUri && key === dashboardKey(activeUri)) openDashboard(activeUri);
2395
+ else {
2396
+ closeDashboard();
2397
+ setActiveKey(key);
2398
+ }
2399
+ }
1327
2400
  }), [
1328
- preview.setActiveKey,
2401
+ setActiveKey,
1329
2402
  open,
1330
2403
  toggleExpand,
1331
2404
  loadMore,
1332
2405
  retry,
1333
- refresh
2406
+ refresh,
2407
+ openDashboard,
2408
+ closeDashboard,
2409
+ activeUri
1334
2410
  ]);
2411
+ const previewError = blocked ? new Error(text['denied' === blocked ? 'accessDenied' : 'traverseOnly']) : preview.error;
1335
2412
  return {
1336
2413
  rows,
1337
2414
  selectedId,
1338
2415
  currentUri: activeUri,
1339
2416
  isReady: tree.isReady,
1340
- error: tree.error ?? search.error ?? preview.error,
2417
+ error: tree.error ?? previewError,
1341
2418
  treeError: tree.error,
1342
- previewError: preview.error,
1343
- searchError: search.error,
1344
- previewTabs: preview.tabs,
1345
- previewKey: preview.activeKey,
2419
+ previewError,
2420
+ dashboards: boards.dashboards,
2421
+ dashboardError: boards.error,
2422
+ previewTabs: mergeDashboardTab(preview.tabs, boards.tab, Boolean(blocked)),
2423
+ previewKey: boards.isOpen && activeUri ? dashboardKey(activeUri) : preview.activeKey,
1346
2424
  previewFooter: preview.footer,
1347
- query,
1348
- searching: null !== search.results,
1349
- searchLoading: search.loading,
1350
2425
  refreshing,
1351
2426
  actions
1352
2427
  };
1353
2428
  }
1354
- function FileBrowser({ uri, defaultUri, rootUri = 'viking://', defaultExpandedUri, locale: localeProp, agentConnect, onUriChange, onImportResources, renderImportResources, labels, requestOptions, className, style, ...rest }) {
2429
+ function AclPrincipalTabs({ type, text, onChange }) {
2430
+ return /*#__PURE__*/ jsx("div", {
2431
+ className: "ov-file-browser-acl__types",
2432
+ role: "group",
2433
+ "aria-label": text.addPrincipal,
2434
+ children: [
2435
+ 'user',
2436
+ 'group'
2437
+ ].map((value)=>/*#__PURE__*/ jsx(button_Button, {
2438
+ size: "small",
2439
+ type: type === value ? 'primary' : 'secondary',
2440
+ "aria-pressed": type === value,
2441
+ onClick: ()=>onChange(value),
2442
+ children: 'user' === value ? text.users : text.groups
2443
+ }, value))
2444
+ });
2445
+ }
2446
+ const file_browser_icons_SIZE = 16;
2447
+ const strokeProps = {
2448
+ width: file_browser_icons_SIZE,
2449
+ height: file_browser_icons_SIZE,
2450
+ viewBox: '0 0 48 48',
2451
+ fill: 'none',
2452
+ stroke: 'currentColor',
2453
+ strokeWidth: 4,
2454
+ 'aria-hidden': true,
2455
+ focusable: 'false'
2456
+ };
2457
+ function SearchIcon() {
2458
+ return /*#__PURE__*/ jsx("svg", {
2459
+ ...strokeProps,
2460
+ children: /*#__PURE__*/ jsx("path", {
2461
+ d: "M33.072 33.071c6.248-6.248 6.248-16.379 0-22.627-6.249-6.249-16.38-6.249-22.628 0-6.248 6.248-6.248 16.379 0 22.627 6.248 6.248 16.38 6.248 22.628 0Zm0 0 8.485 8.485"
2462
+ })
2463
+ });
2464
+ }
2465
+ const LEVELS = [
2466
+ 'read',
2467
+ 'write',
2468
+ 'manage'
2469
+ ];
2470
+ const RANK = {
2471
+ read: 1,
2472
+ write: 2,
2473
+ manage: 3
2474
+ };
2475
+ const isLevel = (value)=>LEVELS.includes(value);
2476
+ function isAclTarget(uri) {
2477
+ if (!uri?.startsWith('viking://resources/')) return false;
2478
+ const segments = uri.slice(19).split('/');
2479
+ return segments.every((part)=>part && '.' !== part && '..' !== part);
2480
+ }
2481
+ function normalizeEntries(entries) {
2482
+ const byPrincipal = new Map();
2483
+ for (const entry of entries){
2484
+ const principal = entry.principal.trim();
2485
+ if (!/^(user|group):[^\s:]+$/.test(principal) || 'group:*' === principal || !isLevel(entry.level)) throw new TypeError('Invalid ACL entry');
2486
+ const previous = byPrincipal.get(principal);
2487
+ if (!previous || RANK[entry.level] > RANK[previous.level]) byPrincipal.set(principal, {
2488
+ principal,
2489
+ level: entry.level
2490
+ });
2491
+ }
2492
+ return [
2493
+ ...byPrincipal.values()
2494
+ ].sort((a, b)=>a.principal.localeCompare(b.principal));
2495
+ }
2496
+ function parseReport(value, uri) {
2497
+ const report = value;
2498
+ if (!report || report.uri !== uri || ![
2499
+ 'none',
2500
+ 'inherit',
2501
+ 'restricted'
2502
+ ].includes(report.acl_mode)) throw new TypeError('Invalid ACL report');
2503
+ for (const key of [
2504
+ 'direct_entries',
2505
+ 'inherited_entries',
2506
+ 'effective_entries'
2507
+ ])if (!Array.isArray(report[key])) throw new TypeError('Invalid ACL entries');
2508
+ return {
2509
+ uri,
2510
+ acl_mode: report.acl_mode,
2511
+ direct_entries: normalizeEntries(report.direct_entries),
2512
+ inherited_entries: normalizeEntries(report.inherited_entries),
2513
+ effective_entries: normalizeEntries(report.effective_entries)
2514
+ };
2515
+ }
2516
+ function planChange(report, next) {
2517
+ const parent = new Map(report.inherited_entries.map((entry)=>[
2518
+ entry.principal,
2519
+ entry
2520
+ ]));
2521
+ const target = new Map(next.map((entry)=>[
2522
+ entry.principal,
2523
+ entry
2524
+ ]));
2525
+ const retained = [
2526
+ ...report.inherited_entries,
2527
+ ...report.effective_entries
2528
+ ].every((entry)=>{
2529
+ const value = target.get(entry.principal);
2530
+ return value && RANK[value.level] >= RANK[entry.level];
2531
+ });
2532
+ const entries = normalizeEntries(next);
2533
+ if ('restricted' === report.acl_mode) return {
2534
+ entries,
2535
+ mode: 'restricted',
2536
+ confirmation: false
2537
+ };
2538
+ if (!retained) return {
2539
+ entries,
2540
+ mode: 'restricted',
2541
+ confirmation: true
2542
+ };
2543
+ return {
2544
+ entries: entries.filter((entry)=>{
2545
+ const inherited = parent.get(entry.principal);
2546
+ return !inherited || RANK[entry.level] > RANK[inherited.level];
2547
+ }),
2548
+ mode: 'inherit',
2549
+ confirmation: false
2550
+ };
2551
+ }
2552
+ function restoreDiff(report) {
2553
+ const inherited = new Map(report.inherited_entries.map((entry)=>[
2554
+ entry.principal,
2555
+ RANK[entry.level]
2556
+ ]));
2557
+ return report.direct_entries.filter((entry)=>Math.max(inherited.get(entry.principal) ?? 0, entry.principal.startsWith('user:') ? inherited.get('user:*') ?? 0 : 0) < RANK[entry.level]);
2558
+ }
2559
+ const acl_locales_en_US = {
2560
+ title: 'Manage node permissions',
2561
+ manage: 'Manage permissions',
2562
+ read: 'Can read',
2563
+ write: 'Can edit',
2564
+ manageLevel: 'Can manage',
2565
+ readDescription: 'Can view content, but cannot edit or manage permissions.',
2566
+ writeDescription: 'Can view and edit content, but cannot manage permissions.',
2567
+ manageDescription: 'Can manage the permissions of this node.',
2568
+ effectivePermission: 'Current user’s effective permission',
2569
+ addPrincipal: 'Add a User or Group',
2570
+ search: 'Search users or groups',
2571
+ users: 'User',
2572
+ groups: 'Group',
2573
+ allUsers: 'All users',
2574
+ administrator: 'Account administrators',
2575
+ administratorDescription: 'Administrators retain implicit management access.',
2576
+ direct: 'Direct grant',
2577
+ inherited: 'Inherited from parent',
2578
+ remove: 'Remove permission',
2579
+ add: 'Add',
2580
+ cancel: 'Cancel',
2581
+ confirm: 'Confirm',
2582
+ close: 'Close permissions',
2583
+ retry: 'Retry',
2584
+ loadMore: 'Load more',
2585
+ loading: 'Loading permissions…',
2586
+ saving: 'Saving…',
2587
+ saved: 'Permissions saved',
2588
+ loadFailed: 'Could not load permissions',
2589
+ saveFailed: 'Could not save permissions',
2590
+ principalsFailed: 'Could not load users or groups',
2591
+ empty: 'No principals available to add',
2592
+ noMatches: 'No matching principals',
2593
+ restricted: 'This node does not inherit its parent’s permissions.',
2594
+ restore: 'Restore inheritance',
2595
+ stopTitle: 'Stop inheriting parent permissions?',
2596
+ stopDescription: 'This node will stop inheriting parent permissions and use the grants shown here.',
2597
+ restoreTitle: 'Restore parent permission inheritance?',
2598
+ restoreDescription: 'Direct grants on this node will be cleared and parent permissions inherited again.',
2599
+ restoreRemoved: 'These direct grants will no longer apply:',
2600
+ readOnly: 'Permission changes are disabled by the host.'
2601
+ };
2602
+ const acl_locales_zh_CN = {
2603
+ title: '管理节点权限',
2604
+ manage: '管理权限',
2605
+ read: '可阅读',
2606
+ write: '可编辑',
2607
+ manageLevel: '可管理',
2608
+ readDescription: '可查看节点内容,不能编辑或管理节点权限。',
2609
+ writeDescription: '可查看并编辑内容,不能管理节点权限。',
2610
+ manageDescription: '可管理该节点的权限配置。',
2611
+ effectivePermission: '当前 User 有效权限',
2612
+ addPrincipal: '添加 User 或 Group',
2613
+ search: '搜索用户或用户组',
2614
+ users: 'User',
2615
+ groups: 'Group',
2616
+ allUsers: '全部 User',
2617
+ administrator: '数据空间管理员',
2618
+ administratorDescription: '管理员始终保留隐式管理权限。',
2619
+ direct: '直接授权',
2620
+ inherited: '从父级目录继承权限',
2621
+ remove: '移除权限',
2622
+ add: '添加',
2623
+ cancel: '取消',
2624
+ confirm: '确定',
2625
+ close: '关闭权限管理',
2626
+ retry: '重试',
2627
+ loadMore: '加载更多',
2628
+ loading: '正在加载权限…',
2629
+ saving: '正在保存…',
2630
+ saved: '权限已保存',
2631
+ loadFailed: '权限加载失败',
2632
+ saveFailed: '权限保存失败',
2633
+ principalsFailed: '用户或用户组加载失败',
2634
+ empty: '暂无可添加成员',
2635
+ noMatches: '暂无匹配结果',
2636
+ restricted: '当前节点不继承父目录的权限。',
2637
+ restore: '恢复继承',
2638
+ stopTitle: '停止继承上级权限?',
2639
+ stopDescription: '确定后,此节点将不再继承父级目录的权限,并按当前设置的访问权限生效。',
2640
+ restoreTitle: '恢复继承上级权限?',
2641
+ restoreDescription: '将清空本节点的直接授权,重新跟随上级权限。',
2642
+ restoreRemoved: '以下直接授权将不再生效:',
2643
+ readOnly: '宿主已禁止修改权限。'
2644
+ };
2645
+ const aclLabels = (locale, labels)=>resolveLocale({
2646
+ 'en-US': acl_locales_en_US,
2647
+ 'zh-CN': acl_locales_zh_CN
2648
+ }, locale, labels);
2649
+ const levelLabel = (level, text)=>'manage' === level ? text.manageLevel : text[level];
2650
+ function AclMenu({ level, text, name, disabled, onChange, onRemove }) {
2651
+ const [open, setOpen] = useState(false);
2652
+ const root = useRef(null);
2653
+ const trigger = useRef(null);
2654
+ const menu = useRef(null);
2655
+ const id = useId();
2656
+ const close = ()=>{
2657
+ setOpen(false);
2658
+ trigger.current?.focus();
2659
+ };
2660
+ useEffect(()=>{
2661
+ if (!open) return;
2662
+ menu.current?.querySelector('button')?.focus();
2663
+ const outside = (event)=>{
2664
+ if (event.target instanceof Node && !root.current?.contains(event.target)) setOpen(false);
2665
+ };
2666
+ document.addEventListener('pointerdown', outside);
2667
+ return ()=>document.removeEventListener('pointerdown', outside);
2668
+ }, [
2669
+ open
2670
+ ]);
2671
+ return /*#__PURE__*/ jsxs("div", {
2672
+ className: "ov-file-browser-acl__menu-anchor",
2673
+ ref: root,
2674
+ onBlur: (event)=>{
2675
+ if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
2676
+ },
2677
+ children: [
2678
+ /*#__PURE__*/ jsxs("button", {
2679
+ ref: trigger,
2680
+ type: "button",
2681
+ className: "ov-file-browser-acl__level",
2682
+ "aria-label": `${name}: ${levelLabel(level, text)}`,
2683
+ "aria-haspopup": "menu",
2684
+ "aria-expanded": open,
2685
+ "aria-controls": open ? id : void 0,
2686
+ disabled: disabled,
2687
+ onClick: ()=>setOpen(!open),
2688
+ children: [
2689
+ levelLabel(level, text),
2690
+ /*#__PURE__*/ jsx(chevron_downreact, {
2691
+ "aria-hidden": "true",
2692
+ width: 12,
2693
+ height: 12
2694
+ })
2695
+ ]
2696
+ }),
2697
+ open && !disabled ? /*#__PURE__*/ jsxs("div", {
2698
+ id: id,
2699
+ role: "menu",
2700
+ tabIndex: -1,
2701
+ "aria-label": name,
2702
+ ref: menu,
2703
+ className: "ov-file-browser-acl__menu",
2704
+ onKeyDown: (event)=>{
2705
+ if ('Escape' === event.key) {
2706
+ event.preventDefault();
2707
+ event.stopPropagation();
2708
+ close();
2709
+ }
2710
+ const buttons = [
2711
+ ...event.currentTarget.querySelectorAll('button')
2712
+ ];
2713
+ const index = buttons.indexOf(document.activeElement);
2714
+ const last = buttons.length - 1;
2715
+ const next = 'ArrowDown' === event.key ? (index + 1) % buttons.length : 'ArrowUp' === event.key ? (index + last) % buttons.length : 'Home' === event.key ? 0 : 'End' === event.key ? last : -1;
2716
+ if (next >= 0) {
2717
+ event.preventDefault();
2718
+ buttons[next]?.focus();
2719
+ }
2720
+ },
2721
+ children: [
2722
+ LEVELS.map((value)=>/*#__PURE__*/ jsxs("button", {
2723
+ type: "button",
2724
+ role: "menuitemradio",
2725
+ "aria-checked": level === value,
2726
+ onClick: ()=>{
2727
+ close();
2728
+ if (value !== level) onChange(value);
2729
+ },
2730
+ children: [
2731
+ /*#__PURE__*/ jsx("span", {
2732
+ className: "ov-file-browser-acl__check",
2733
+ children: level === value ? /*#__PURE__*/ jsx(checkreact, {
2734
+ width: 12,
2735
+ height: 12,
2736
+ "aria-hidden": "true"
2737
+ }) : null
2738
+ }),
2739
+ levelLabel(value, text)
2740
+ ]
2741
+ }, value)),
2742
+ onRemove ? /*#__PURE__*/ jsx("button", {
2743
+ type: "button",
2744
+ role: "menuitem",
2745
+ className: "ov-file-browser-acl__remove",
2746
+ onClick: ()=>{
2747
+ close();
2748
+ onRemove();
2749
+ },
2750
+ children: text.remove
2751
+ }) : null
2752
+ ]
2753
+ }) : null
2754
+ ]
2755
+ });
2756
+ }
2757
+ function AclAvatar({ principal }) {
2758
+ const [type, id] = principal.split(':');
2759
+ return /*#__PURE__*/ jsx("span", {
2760
+ "aria-hidden": "true",
2761
+ className: "ov-file-browser-acl__avatar",
2762
+ "data-group": 'group' === type || void 0,
2763
+ children: 'group' === type ? 'G' : id?.charAt(0).toUpperCase()
2764
+ });
2765
+ }
2766
+ function AclEntry({ entry, source, text, disabled, onChange, onRemove }) {
2767
+ const name = 'user:*' === entry.principal ? text.allUsers : entry.principal.slice(entry.principal.indexOf(':') + 1);
2768
+ return /*#__PURE__*/ jsxs("li", {
2769
+ className: "ov-file-browser-acl__entry",
2770
+ children: [
2771
+ /*#__PURE__*/ jsx(AclAvatar, {
2772
+ principal: entry.principal
2773
+ }),
2774
+ /*#__PURE__*/ jsxs("div", {
2775
+ className: "ov-file-browser-acl__identity",
2776
+ children: [
2777
+ /*#__PURE__*/ jsx("span", {
2778
+ className: "ov-file-browser-acl__name",
2779
+ children: name
2780
+ }),
2781
+ /*#__PURE__*/ jsx("span", {
2782
+ className: "ov-file-browser-acl__source",
2783
+ children: source
2784
+ })
2785
+ ]
2786
+ }),
2787
+ /*#__PURE__*/ jsx(AclMenu, {
2788
+ level: entry.level,
2789
+ text: text,
2790
+ name: entry.principal,
2791
+ disabled: disabled,
2792
+ onChange: onChange,
2793
+ onRemove: onRemove
2794
+ })
2795
+ ]
2796
+ });
2797
+ }
2798
+ function AclCandidate({ principal, level, text, onChange }) {
2799
+ const name = 'user:*' === principal ? text.allUsers : principal.slice(principal.indexOf(':') + 1);
2800
+ return /*#__PURE__*/ jsxs("div", {
2801
+ className: "ov-file-browser-acl__candidate",
2802
+ children: [
2803
+ /*#__PURE__*/ jsxs("label", {
2804
+ children: [
2805
+ /*#__PURE__*/ jsx("input", {
2806
+ type: "checkbox",
2807
+ checked: void 0 !== level,
2808
+ onChange: (event)=>onChange(event.target.checked ? 'read' : void 0)
2809
+ }),
2810
+ /*#__PURE__*/ jsx(AclAvatar, {
2811
+ principal: principal
2812
+ }),
2813
+ /*#__PURE__*/ jsx("span", {
2814
+ children: name
2815
+ })
2816
+ ]
2817
+ }),
2818
+ /*#__PURE__*/ jsx(AclMenu, {
2819
+ text: text,
2820
+ name: principal,
2821
+ level: level ?? 'read',
2822
+ onChange: onChange
2823
+ })
2824
+ ]
2825
+ });
2826
+ }
2827
+ const readAcl = async (client, uri, options)=>parseReport(await client.request({
2828
+ method: 'GET',
2829
+ path: OV_PATHS.acl,
2830
+ query: {
2831
+ uri
2832
+ },
2833
+ options
2834
+ }), uri);
2835
+ async function readPermission(client, uri, options) {
2836
+ const stat = await client.request({
2837
+ method: 'GET',
2838
+ path: OV_PATHS.fsStat,
2839
+ query: {
2840
+ uri
2841
+ },
2842
+ options
2843
+ });
2844
+ if (void 0 !== stat.permission) {
2845
+ if (!isLevel(stat.permission)) throw new TypeError('Unknown resource permission');
2846
+ return stat.permission;
2847
+ }
2848
+ try {
2849
+ await readAcl(client, uri, options);
2850
+ return 'manage';
2851
+ } catch (error) {
2852
+ if (isOvClientError(error) && 403 === error.statusCode) return;
2853
+ throw error;
2854
+ }
2855
+ }
2856
+ async function writeAcl(client, uri, options, change) {
2857
+ const value = await client.request({
2858
+ method: change ? 'PUT' : 'DELETE',
2859
+ path: OV_PATHS.acl,
2860
+ ...change ? {
2861
+ body: {
2862
+ uri,
2863
+ acl_mode: change.mode,
2864
+ entries: change.direct_entries
2865
+ }
2866
+ } : {
2867
+ query: {
2868
+ uri
2869
+ }
2870
+ },
2871
+ options
2872
+ });
2873
+ return parseReport(value, uri);
2874
+ }
2875
+ async function identityValue(value) {
2876
+ return ('function' == typeof value ? await value() : value)?.trim() ?? '';
2877
+ }
2878
+ async function listPrincipals(query, context, accountId) {
2879
+ const account = await identityValue(accountId);
2880
+ if (!account) throw new TypeError('Principal listing requires an account ID');
2881
+ if ('group' === query.type) {
2882
+ const groups = await context.client.request({
2883
+ method: 'GET',
2884
+ path: OV_PATH_BUILDERS.accountGroups(account),
2885
+ options: context.requestOptions
2886
+ });
2887
+ return {
2888
+ principals: groups.map((group)=>`group:${group.group_id}`),
2889
+ hasMore: false
2890
+ };
2891
+ }
2892
+ const result = await context.client.request({
2893
+ method: 'GET',
2894
+ path: OV_PATH_BUILDERS.accountUsers(account),
2895
+ query: {
2896
+ query: query.keyword,
2897
+ page: query.page,
2898
+ limit: 20,
2899
+ include_summary: true,
2900
+ include_credentials: false
2901
+ },
2902
+ options: context.requestOptions
2903
+ });
2904
+ return {
2905
+ principals: result.users.map((user)=>`user:${user.user_id}`),
2906
+ hasMore: 20 * query.page < result.total
2907
+ };
2908
+ }
2909
+ function useAclPrincipals({ open, type, keyword, context, accountId, options }) {
2910
+ const key = JSON.stringify([
2911
+ type,
2912
+ keyword,
2913
+ open
2914
+ ]);
2915
+ const [paging, setPaging] = useState({
2916
+ key,
2917
+ page: 1
2918
+ });
2919
+ const page = paging.key === key ? paging.page : 1;
2920
+ if (paging.key !== key) setPaging({
2921
+ key,
2922
+ page: 1
2923
+ });
2924
+ const [generation, setGeneration] = useState(0);
2925
+ const [state, setState] = useState({
2926
+ key: '',
2927
+ items: [],
2928
+ hasMore: false
2929
+ });
2930
+ const [loading, setLoading] = useState(false);
2931
+ const [error, setError] = useState();
2932
+ const load = options?.listPrincipals;
2933
+ useEffect(()=>{
2934
+ if (!open) return;
2935
+ const controller = new AbortController();
2936
+ setLoading(true);
2937
+ setError(void 0);
2938
+ const timer = setTimeout(()=>{
2939
+ const inherited = context.requestOptions.signal;
2940
+ const signal = inherited ? AbortSignal.any([
2941
+ controller.signal,
2942
+ inherited
2943
+ ]) : controller.signal;
2944
+ const request = {
2945
+ ...context,
2946
+ requestOptions: {
2947
+ ...context.requestOptions,
2948
+ signal
2949
+ }
2950
+ };
2951
+ const query = {
2952
+ type,
2953
+ keyword,
2954
+ page
2955
+ };
2956
+ (load ? load(query, request) : listPrincipals(query, request, accountId)).then((result)=>{
2957
+ if (signal.aborted) return;
2958
+ setState((previous)=>({
2959
+ key,
2960
+ hasMore: result.hasMore,
2961
+ items: [
2962
+ ...new Set([
2963
+ ...page > 1 && previous.key === key ? previous.items : [],
2964
+ ...result.principals
2965
+ ])
2966
+ ]
2967
+ }));
2968
+ setLoading(false);
2969
+ }, (cause)=>{
2970
+ if (!signal.aborted) {
2971
+ setError(toError(cause));
2972
+ setLoading(false);
2973
+ }
2974
+ });
2975
+ }, 200);
2976
+ return ()=>{
2977
+ clearTimeout(timer);
2978
+ controller.abort();
2979
+ };
2980
+ }, [
2981
+ open,
2982
+ type,
2983
+ keyword,
2984
+ key,
2985
+ page,
2986
+ generation,
2987
+ context,
2988
+ accountId,
2989
+ load
2990
+ ]);
2991
+ return {
2992
+ items: state.key === key ? state.items : [],
2993
+ loading,
2994
+ error,
2995
+ hasMore: state.key === key && state.hasMore,
2996
+ more: ()=>{
2997
+ if (!loading) setPaging({
2998
+ key,
2999
+ page: page + 1
3000
+ });
3001
+ },
3002
+ retry: ()=>setGeneration((value)=>value + 1)
3003
+ };
3004
+ }
3005
+ function AclAddPrincipal({ entries, text, disabled, context, accountId, options, onAdd }) {
3006
+ const [open, setOpen] = useState(false);
3007
+ const [keyword, setKeyword] = useState('');
3008
+ const [type, setType] = useState('user');
3009
+ const [pending, setPending] = useState({});
3010
+ const id = useId();
3011
+ const directory = useAclPrincipals({
3012
+ open: open && !disabled,
3013
+ type,
3014
+ keyword,
3015
+ context,
3016
+ accountId,
3017
+ options
3018
+ });
3019
+ const existing = new Set(entries.map((entry)=>entry.principal));
3020
+ const candidates = [
3021
+ ...new Set([
3022
+ ...'user' === type ? [
3023
+ 'user:*'
3024
+ ] : [],
3025
+ ...directory.items
3026
+ ])
3027
+ ].filter((principal)=>!existing.has(principal) && principal.startsWith(`${type}:`)).filter((principal)=>('user:*' === principal ? text.allUsers : principal.slice(principal.indexOf(':') + 1)).toLocaleLowerCase().includes(keyword.trim().toLocaleLowerCase()));
3028
+ const reset = ()=>{
3029
+ setOpen(false);
3030
+ setKeyword('');
3031
+ setPending({});
3032
+ };
3033
+ const selected = Object.entries(pending).filter(([principal])=>!existing.has(principal));
3034
+ return /*#__PURE__*/ jsxs("div", {
3035
+ className: "ov-file-browser-acl__add",
3036
+ children: [
3037
+ /*#__PURE__*/ jsx("label", {
3038
+ htmlFor: id,
3039
+ children: text.addPrincipal
3040
+ }),
3041
+ /*#__PURE__*/ jsx(input_Input, {
3042
+ prefix: /*#__PURE__*/ jsx(SearchIcon, {}),
3043
+ id: id,
3044
+ type: "search",
3045
+ role: "combobox",
3046
+ "aria-haspopup": "dialog",
3047
+ value: keyword,
3048
+ placeholder: text.search,
3049
+ disabled: disabled,
3050
+ "aria-expanded": open && !disabled,
3051
+ "aria-controls": open ? `${id}-options` : void 0,
3052
+ onFocus: ()=>setOpen(true),
3053
+ onChange: (event)=>{
3054
+ setKeyword(event.target.value);
3055
+ setOpen(true);
3056
+ }
3057
+ }),
3058
+ open && !disabled ? /*#__PURE__*/ jsxs("div", {
3059
+ id: `${id}-options`,
3060
+ className: "ov-file-browser-acl__picker",
3061
+ role: "dialog",
3062
+ "aria-label": text.addPrincipal,
3063
+ children: [
3064
+ /*#__PURE__*/ jsx(AclPrincipalTabs, {
3065
+ type: type,
3066
+ text: text,
3067
+ onChange: setType
3068
+ }),
3069
+ /*#__PURE__*/ jsxs("div", {
3070
+ className: "ov-file-browser-acl__candidates",
3071
+ "aria-busy": directory.loading,
3072
+ children: [
3073
+ candidates.map((principal)=>/*#__PURE__*/ jsx(AclCandidate, {
3074
+ principal: principal,
3075
+ level: pending[principal],
3076
+ text: text,
3077
+ onChange: (level)=>setPending((previous)=>{
3078
+ const next = {
3079
+ ...previous
3080
+ };
3081
+ if (level) next[principal] = level;
3082
+ else delete next[principal];
3083
+ return next;
3084
+ })
3085
+ }, principal)),
3086
+ directory.loading || candidates.length || directory.error ? null : /*#__PURE__*/ jsx("p", {
3087
+ className: "ov-file-browser-acl__hint",
3088
+ children: keyword.trim() ? text.noMatches : text.empty
3089
+ })
3090
+ ]
3091
+ }),
3092
+ directory.error ? /*#__PURE__*/ jsxs("div", {
3093
+ className: "ov-file-browser-acl__failure",
3094
+ role: "alert",
3095
+ children: [
3096
+ /*#__PURE__*/ jsx("p", {
3097
+ children: text.principalsFailed
3098
+ }),
3099
+ /*#__PURE__*/ jsx("p", {
3100
+ children: directory.error.message
3101
+ }),
3102
+ /*#__PURE__*/ jsx(button_Button, {
3103
+ size: "small",
3104
+ type: "default",
3105
+ htmlType: "button",
3106
+ onClick: directory.retry,
3107
+ children: text.retry
3108
+ })
3109
+ ]
3110
+ }) : null,
3111
+ directory.loading ? /*#__PURE__*/ jsx("p", {
3112
+ className: "ov-file-browser-acl__hint",
3113
+ children: text.loading
3114
+ }) : null,
3115
+ directory.hasMore ? /*#__PURE__*/ jsx(button_Button, {
3116
+ size: "small",
3117
+ type: "default",
3118
+ htmlType: "button",
3119
+ className: "ov-file-browser-acl__more",
3120
+ long: true,
3121
+ disabled: directory.loading,
3122
+ onClick: directory.more,
3123
+ children: text.loadMore
3124
+ }) : null,
3125
+ /*#__PURE__*/ jsxs("div", {
3126
+ className: "ov-file-browser-acl__buttons",
3127
+ children: [
3128
+ /*#__PURE__*/ jsx(button_Button, {
3129
+ size: "small",
3130
+ type: "default",
3131
+ htmlType: "button",
3132
+ onClick: reset,
3133
+ children: text.cancel
3134
+ }),
3135
+ /*#__PURE__*/ jsx(button_Button, {
3136
+ size: "small",
3137
+ type: "primary",
3138
+ htmlType: "button",
3139
+ className: "ov-file-browser-acl__primary",
3140
+ disabled: !selected.length,
3141
+ onClick: ()=>{
3142
+ onAdd(selected.map(([principal, level])=>({
3143
+ principal,
3144
+ level
3145
+ })));
3146
+ reset();
3147
+ },
3148
+ children: text.add
3149
+ })
3150
+ ]
3151
+ })
3152
+ ]
3153
+ }) : null
3154
+ ]
3155
+ });
3156
+ }
3157
+ function AclConfirmation({ model, text }) {
3158
+ const cancel = useRef(null);
3159
+ const titleId = useId();
3160
+ const descriptionId = useId();
3161
+ const restore = model.confirmation?.kind === 'restore';
3162
+ const removed = restore && model.report ? restoreDiff(model.report) : [];
3163
+ useEffect(()=>{
3164
+ cancel.current?.focus();
3165
+ }, []);
3166
+ return /*#__PURE__*/ jsxs("section", {
3167
+ className: "ov-file-browser-acl__confirmation",
3168
+ "aria-labelledby": titleId,
3169
+ "aria-describedby": descriptionId,
3170
+ children: [
3171
+ /*#__PURE__*/ jsx("h3", {
3172
+ id: titleId,
3173
+ children: restore ? text.restoreTitle : text.stopTitle
3174
+ }),
3175
+ /*#__PURE__*/ jsx("p", {
3176
+ id: descriptionId,
3177
+ children: restore ? text.restoreDescription : text.stopDescription
3178
+ }),
3179
+ removed.length ? /*#__PURE__*/ jsxs(Fragment, {
3180
+ children: [
3181
+ /*#__PURE__*/ jsx("p", {
3182
+ children: text.restoreRemoved
3183
+ }),
3184
+ /*#__PURE__*/ jsx("ul", {
3185
+ children: removed.map((entry)=>/*#__PURE__*/ jsx("li", {
3186
+ children: entry.principal
3187
+ }, entry.principal))
3188
+ })
3189
+ ]
3190
+ }) : null,
3191
+ /*#__PURE__*/ jsxs("div", {
3192
+ className: "ov-file-browser-acl__buttons",
3193
+ children: [
3194
+ /*#__PURE__*/ jsx(button_Button, {
3195
+ size: "small",
3196
+ type: "default",
3197
+ ref: cancel,
3198
+ htmlType: "button",
3199
+ disabled: 'saving' === model.phase,
3200
+ onClick: model.cancel,
3201
+ children: text.cancel
3202
+ }),
3203
+ /*#__PURE__*/ jsx(button_Button, {
3204
+ size: "small",
3205
+ type: "primary",
3206
+ htmlType: "button",
3207
+ className: "ov-file-browser-acl__primary",
3208
+ disabled: 'saving' === model.phase,
3209
+ onClick: model.confirm,
3210
+ children: 'saving' === model.phase ? text.saving : text.confirm
3211
+ })
3212
+ ]
3213
+ })
3214
+ ]
3215
+ });
3216
+ }
3217
+ function useAclReport({ uri, context, open, readOnly, onUpdated }) {
3218
+ const [report, setReport] = useState();
3219
+ const [confirmation, setConfirmation] = useState();
3220
+ const [error, setError] = useState();
3221
+ const [phase, setPhase] = useState('loading');
3222
+ const [saved, setSaved] = useState(false);
3223
+ const [generation, setGeneration] = useState(0);
3224
+ const pending = useRef(false);
3225
+ const lifetime = useRef(null);
3226
+ useEffect(()=>{
3227
+ if (!open) return;
3228
+ const controller = new AbortController();
3229
+ lifetime.current = controller;
3230
+ pending.current = false;
3231
+ setPhase('loading');
3232
+ setReport(void 0);
3233
+ setError(void 0);
3234
+ setConfirmation(void 0);
3235
+ setSaved(false);
3236
+ const { client, requestOptions } = context;
3237
+ const signal = requestOptions.signal ? AbortSignal.any([
3238
+ controller.signal,
3239
+ requestOptions.signal
3240
+ ]) : controller.signal;
3241
+ readAcl(client, uri, {
3242
+ ...requestOptions,
3243
+ signal
3244
+ }).then((value)=>{
3245
+ if (!signal.aborted) {
3246
+ setReport(value);
3247
+ setPhase('ready');
3248
+ }
3249
+ }, (cause)=>{
3250
+ if (!signal.aborted) {
3251
+ setError(toError(cause));
3252
+ setPhase('ready');
3253
+ }
3254
+ });
3255
+ return ()=>controller.abort();
3256
+ }, [
3257
+ uri,
3258
+ open,
3259
+ generation,
3260
+ context
3261
+ ]);
3262
+ async function save(change) {
3263
+ const controller = lifetime.current;
3264
+ if (pending.current || readOnly || !report || !controller || controller.signal.aborted) return;
3265
+ pending.current = true;
3266
+ setPhase('saving');
3267
+ setError(void 0);
3268
+ setSaved(false);
3269
+ const { client, requestOptions } = context;
3270
+ const signal = requestOptions.signal ? AbortSignal.any([
3271
+ controller.signal,
3272
+ requestOptions.signal
3273
+ ]) : controller.signal;
3274
+ try {
3275
+ const value = await writeAcl(client, uri, {
3276
+ ...requestOptions,
3277
+ signal
3278
+ }, change && {
3279
+ direct_entries: change.entries,
3280
+ mode: change.mode
3281
+ });
3282
+ if (signal.aborted) return;
3283
+ setReport(value);
3284
+ setConfirmation(void 0);
3285
+ setSaved(true);
3286
+ onUpdated(value);
3287
+ } catch (cause) {
3288
+ if (!signal.aborted) setError(toError(cause));
3289
+ } finally{
3290
+ if (!controller.signal.aborted && lifetime.current === controller) {
3291
+ pending.current = false;
3292
+ setPhase('ready');
3293
+ }
3294
+ }
3295
+ }
3296
+ function change(entries) {
3297
+ if (!report || pending.current || readOnly) return;
3298
+ const plan = planChange(report, normalizeEntries(entries));
3299
+ setSaved(false);
3300
+ setError(void 0);
3301
+ if (plan.confirmation) setConfirmation({
3302
+ kind: 'restrict',
3303
+ entries: plan.entries
3304
+ });
3305
+ else save(plan);
3306
+ }
3307
+ return {
3308
+ report,
3309
+ confirmation,
3310
+ error,
3311
+ phase,
3312
+ saved,
3313
+ retry: ()=>setGeneration((value)=>value + 1),
3314
+ change,
3315
+ restore: ()=>{
3316
+ if (!pending.current && !readOnly) setConfirmation({
3317
+ kind: 'restore'
3318
+ });
3319
+ },
3320
+ cancel: ()=>{
3321
+ if (!pending.current) {
3322
+ setConfirmation(void 0);
3323
+ setError(void 0);
3324
+ }
3325
+ },
3326
+ confirm: ()=>{
3327
+ if (confirmation?.kind === 'restore') save();
3328
+ else if (confirmation) save({
3329
+ entries: confirmation.entries,
3330
+ mode: 'restricted'
3331
+ });
3332
+ }
3333
+ };
3334
+ }
3335
+ function AclModal({ uri, open, text, locale, context, triggerRef, accountId, options, onClose, onUpdated }) {
3336
+ const id = useId();
3337
+ const heading = useRef(null);
3338
+ const model = useAclReport({
3339
+ uri,
3340
+ context,
3341
+ open,
3342
+ readOnly: Boolean(options?.readOnly),
3343
+ onUpdated
3344
+ });
3345
+ const disabled = Boolean(options?.readOnly) || 'ready' !== model.phase;
3346
+ const entries = model.report?.effective_entries ?? [];
3347
+ const direct = new Map(model.report?.direct_entries.map((entry)=>[
3348
+ entry.principal,
3349
+ entry.level
3350
+ ]));
3351
+ const wasConfirming = useRef(false);
3352
+ useEffect(()=>{
3353
+ if (wasConfirming.current && !model.confirmation) heading.current?.focus();
3354
+ wasConfirming.current = Boolean(model.confirmation);
3355
+ }, [
3356
+ model.confirmation
3357
+ ]);
3358
+ return /*#__PURE__*/ jsxs(Modal, {
3359
+ id: id,
3360
+ open: open,
3361
+ title: text.title,
3362
+ closeLabel: text.close,
3363
+ triggerRef: triggerRef,
3364
+ className: "ov-file-browser-acl",
3365
+ locale: locale,
3366
+ onClose: ()=>{
3367
+ if ('saving' === model.phase) return;
3368
+ if (model.confirmation) model.cancel();
3369
+ else onClose();
3370
+ },
3371
+ children: [
3372
+ /*#__PURE__*/ jsx("h2", {
3373
+ ref: heading,
3374
+ tabIndex: -1,
3375
+ className: "ov-file-browser-acl__title",
3376
+ children: text.title
3377
+ }),
3378
+ /*#__PURE__*/ jsx("p", {
3379
+ className: "ov-file-browser-acl__uri",
3380
+ children: uri
3381
+ }),
3382
+ model.error ? /*#__PURE__*/ jsxs("div", {
3383
+ className: "ov-file-browser-acl__failure",
3384
+ role: "alert",
3385
+ children: [
3386
+ /*#__PURE__*/ jsx("p", {
3387
+ children: model.report ? text.saveFailed : text.loadFailed
3388
+ }),
3389
+ /*#__PURE__*/ jsx("p", {
3390
+ children: model.error.message
3391
+ }),
3392
+ model.report ? null : /*#__PURE__*/ jsx(button_Button, {
3393
+ size: "small",
3394
+ type: "default",
3395
+ htmlType: "button",
3396
+ onClick: model.retry,
3397
+ children: text.retry
3398
+ })
3399
+ ]
3400
+ }) : null,
3401
+ /*#__PURE__*/ jsx("div", {
3402
+ role: "status",
3403
+ className: "ov-file-browser-acl__status",
3404
+ children: 'saving' === model.phase ? text.saving : 'loading' === model.phase ? text.loading : model.saved ? text.saved : ''
3405
+ }),
3406
+ model.confirmation ? /*#__PURE__*/ jsx(AclConfirmation, {
3407
+ model: model,
3408
+ text: text
3409
+ }) : model.report ? /*#__PURE__*/ jsxs(Fragment, {
3410
+ children: [
3411
+ 'restricted' === model.report.acl_mode ? /*#__PURE__*/ jsxs("div", {
3412
+ className: "ov-file-browser-acl__restricted",
3413
+ children: [
3414
+ /*#__PURE__*/ jsx("p", {
3415
+ children: text.restricted
3416
+ }),
3417
+ /*#__PURE__*/ jsx(button_Button, {
3418
+ size: "small",
3419
+ type: "default",
3420
+ htmlType: "button",
3421
+ disabled: disabled,
3422
+ onClick: model.restore,
3423
+ children: text.restore
3424
+ })
3425
+ ]
3426
+ }) : null,
3427
+ options?.readOnly ? /*#__PURE__*/ jsx("p", {
3428
+ className: "ov-file-browser-acl__hint",
3429
+ children: text.readOnly
3430
+ }) : null,
3431
+ /*#__PURE__*/ jsx(AclAddPrincipal, {
3432
+ entries: entries,
3433
+ text: text,
3434
+ disabled: disabled,
3435
+ context: context,
3436
+ accountId: accountId,
3437
+ options: options,
3438
+ onAdd: (added)=>model.change([
3439
+ ...entries,
3440
+ ...added
3441
+ ])
3442
+ }),
3443
+ /*#__PURE__*/ jsxs("ul", {
3444
+ className: "ov-file-browser-acl__entries",
3445
+ "aria-busy": 'saving' === model.phase,
3446
+ children: [
3447
+ /*#__PURE__*/ jsxs("li", {
3448
+ className: "ov-file-browser-acl__entry",
3449
+ children: [
3450
+ /*#__PURE__*/ jsx("span", {
3451
+ className: "ov-file-browser-acl__avatar",
3452
+ "aria-hidden": "true",
3453
+ children: "A"
3454
+ }),
3455
+ /*#__PURE__*/ jsxs("div", {
3456
+ className: "ov-file-browser-acl__identity",
3457
+ children: [
3458
+ /*#__PURE__*/ jsx("span", {
3459
+ className: "ov-file-browser-acl__name",
3460
+ children: text.administrator
3461
+ }),
3462
+ /*#__PURE__*/ jsx("span", {
3463
+ className: "ov-file-browser-acl__source",
3464
+ children: text.administratorDescription
3465
+ })
3466
+ ]
3467
+ }),
3468
+ /*#__PURE__*/ jsx("span", {
3469
+ className: "ov-file-browser-acl__fixed",
3470
+ children: levelLabel('manage', text)
3471
+ })
3472
+ ]
3473
+ }),
3474
+ entries.map((entry)=>/*#__PURE__*/ jsx(AclEntry, {
3475
+ entry: entry,
3476
+ text: text,
3477
+ disabled: disabled,
3478
+ source: direct.get(entry.principal) === entry.level ? text.direct : text.inherited,
3479
+ onChange: (level)=>model.change(entries.map((item)=>item.principal === entry.principal ? {
3480
+ ...item,
3481
+ level
3482
+ } : item)),
3483
+ onRemove: ()=>model.change(entries.filter((item)=>item.principal !== entry.principal))
3484
+ }, entry.principal))
3485
+ ]
3486
+ })
3487
+ ]
3488
+ }) : null
3489
+ ]
3490
+ });
3491
+ }
3492
+ function AclTag({ level, text, onManage }) {
3493
+ const [open, setOpen] = useState(false);
3494
+ const id = useId();
3495
+ const description = 'read' === level ? text.readDescription : 'write' === level ? text.writeDescription : text.manageDescription;
3496
+ return /*#__PURE__*/ jsxs("div", {
3497
+ className: "ov-file-browser-acl__tag-anchor",
3498
+ onBlur: (event)=>{
3499
+ if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
3500
+ },
3501
+ children: [
3502
+ /*#__PURE__*/ jsx("button", {
3503
+ onKeyDown: (event)=>{
3504
+ if ('Escape' === event.key) {
3505
+ event.preventDefault();
3506
+ setOpen(false);
3507
+ }
3508
+ },
3509
+ type: "button",
3510
+ className: "ov-file-browser-acl__tag",
3511
+ "aria-expanded": open,
3512
+ "aria-controls": open ? id : void 0,
3513
+ onClick: ()=>setOpen(!open),
3514
+ children: levelLabel(level, text)
3515
+ }),
3516
+ open ? /*#__PURE__*/ jsxs("div", {
3517
+ id: id,
3518
+ className: "ov-file-browser-acl__tag-detail",
3519
+ children: [
3520
+ /*#__PURE__*/ jsx("p", {
3521
+ children: description
3522
+ }),
3523
+ /*#__PURE__*/ jsxs("p", {
3524
+ className: "ov-file-browser-acl__source",
3525
+ children: [
3526
+ text.effectivePermission,
3527
+ ": ",
3528
+ levelLabel(level, text)
3529
+ ]
3530
+ }),
3531
+ 'manage' === level ? /*#__PURE__*/ jsx(button_Button, {
3532
+ size: "small",
3533
+ type: "default",
3534
+ htmlType: "button",
3535
+ onClick: ()=>{
3536
+ setOpen(false);
3537
+ onManage();
3538
+ },
3539
+ children: text.manage
3540
+ }) : null
3541
+ ]
3542
+ }) : null
3543
+ ]
3544
+ });
3545
+ }
3546
+ function useFileBrowserAcl({ uri, acl, locale, labels, requestOptions, refresh }) {
3547
+ const client = useOvClient();
3548
+ const identity = useOvIdentity(requestOptions);
3549
+ const inheritedSignal = useOvSignal();
3550
+ const signal = requestOptions?.signal ?? inheritedSignal;
3551
+ const options = 'object' == typeof acl ? acl : void 0;
3552
+ const key = scopeKeyOf(client, requestOptions, uri ?? void 0);
3553
+ const [permission, setPermission] = useState();
3554
+ const [opened, setOpened] = useState();
3555
+ const [generation, setGeneration] = useState(0);
3556
+ const triggerRef = useRef(null);
3557
+ const { baseURL, apiKey, accountId, userId, resourceId } = requestOptions ?? {};
3558
+ const context = useMemo(()=>client ? {
3559
+ client,
3560
+ requestOptions: {
3561
+ baseURL,
3562
+ apiKey,
3563
+ accountId,
3564
+ userId,
3565
+ resourceId,
3566
+ signal
3567
+ }
3568
+ } : void 0, [
3569
+ client,
3570
+ baseURL,
3571
+ apiKey,
3572
+ accountId,
3573
+ userId,
3574
+ resourceId,
3575
+ signal
3576
+ ]);
3577
+ const enabled = Boolean(acl) && Boolean(client) && isAclTarget(uri);
3578
+ const resolver = options?.resolvePermission;
3579
+ useEffect(()=>{
3580
+ if (!enabled || !uri || !context) return;
3581
+ const controller = new AbortController();
3582
+ const composed = signal ? AbortSignal.any([
3583
+ controller.signal,
3584
+ signal
3585
+ ]) : controller.signal;
3586
+ const request = {
3587
+ ...context,
3588
+ requestOptions: {
3589
+ ...context.requestOptions,
3590
+ signal: composed
3591
+ }
3592
+ };
3593
+ const load = resolver ? resolver(uri, request) : readPermission(request.client, uri, request.requestOptions);
3594
+ load.then((level)=>{
3595
+ if (!composed.aborted) setPermission({
3596
+ key,
3597
+ level
3598
+ });
3599
+ }, ()=>{
3600
+ if (!composed.aborted) setPermission({
3601
+ key,
3602
+ error: true
3603
+ });
3604
+ });
3605
+ return ()=>controller.abort();
3606
+ }, [
3607
+ enabled,
3608
+ key,
3609
+ uri,
3610
+ resolver,
3611
+ signal,
3612
+ generation,
3613
+ context
3614
+ ]);
3615
+ const level = enabled && permission?.key === key ? permission.level : void 0;
3616
+ const text = aclLabels(locale, labels);
3617
+ return {
3618
+ titleExtra: enabled && permission?.key === key && permission.error ? /*#__PURE__*/ jsxs("span", {
3619
+ className: "ov-file-browser-acl__lookup-error",
3620
+ role: "status",
3621
+ children: [
3622
+ text.loadFailed,
3623
+ /*#__PURE__*/ jsx(button_Button, {
3624
+ size: "small",
3625
+ type: "default",
3626
+ htmlType: "button",
3627
+ onClick: ()=>setGeneration((value)=>value + 1),
3628
+ children: text.retry
3629
+ })
3630
+ ]
3631
+ }) : level ? /*#__PURE__*/ jsx(AclTag, {
3632
+ level: level,
3633
+ text: text,
3634
+ onManage: ()=>setOpened(key)
3635
+ }) : null,
3636
+ actions: 'manage' === level ? /*#__PURE__*/ jsx(button_Button, {
3637
+ size: "small",
3638
+ type: "default",
3639
+ ref: triggerRef,
3640
+ htmlType: "button",
3641
+ className: "ov-file-browser-acl__manage",
3642
+ onClick: ()=>setOpened(key),
3643
+ children: text.manage
3644
+ }) : null,
3645
+ drawer: enabled && uri && context && 'manage' === level ? /*#__PURE__*/ jsx(AclModal, {
3646
+ uri: uri,
3647
+ open: opened === key,
3648
+ text: text,
3649
+ locale: locale,
3650
+ triggerRef: triggerRef,
3651
+ context: context,
3652
+ accountId: identity.accountId,
3653
+ options: options,
3654
+ onClose: ()=>setOpened(void 0),
3655
+ onUpdated: (report)=>{
3656
+ setGeneration((value)=>value + 1);
3657
+ refresh();
3658
+ try {
3659
+ options?.onChange?.(report);
3660
+ } catch {
3661
+ return;
3662
+ }
3663
+ }
3664
+ }, key) : null
3665
+ };
3666
+ }
3667
+ function FileBrowser({ uri, defaultUri, rootUri = 'viking://', defaultExpandedUri, locale: localeProp, agentConnect, dashboardKinds, renderDashboard, acl, onUriChange, onImportResources, renderImportResources, labels, requestOptions, className, style, ...rest }) {
1355
3668
  const locale = useLocale(localeProp);
3669
+ const [searchOpen, setSearchOpen] = useState(false);
1356
3670
  const [internalUri, setInternalUri] = useState(()=>defaultUri ?? null);
1357
3671
  const [preferredTreeWidth, setPreferredTreeWidth] = useState(352);
1358
- const searchId = useId();
1359
3672
  const isControlled = void 0 !== uri;
1360
3673
  const currentUri = isControlled ? uri : internalUri;
1361
3674
  const text = resolveLabels(labels, locale);
@@ -1385,88 +3698,115 @@ function FileBrowser({ uri, defaultUri, rootUri = 'viking://', defaultExpandedUr
1385
3698
  const browser = useFileBrowser({
1386
3699
  rootUri,
1387
3700
  locale,
3701
+ dashboardKinds,
3702
+ renderDashboard,
3703
+ dashboardLabels: labels?.dashboard,
1388
3704
  controlled: isControlled,
1389
3705
  summaryLabels,
1390
3706
  formatCharacterCount: text.formatCharacterCount,
3707
+ accessLabels: {
3708
+ accessDenied: text.accessDenied,
3709
+ traverseOnly: text.traverseOnly
3710
+ },
1391
3711
  defaultExpandedUri,
1392
3712
  currentUri,
1393
3713
  requestOptions,
1394
3714
  onUriChange: changeUri
1395
3715
  });
1396
- return /*#__PURE__*/ jsx(LocaleProvider, {
3716
+ const permissions = useFileBrowserAcl({
3717
+ uri: browser.currentUri,
3718
+ acl,
3719
+ locale,
3720
+ labels: labels?.acl,
3721
+ requestOptions,
3722
+ refresh: browser.actions.refresh
3723
+ });
3724
+ return /*#__PURE__*/ jsxs(LocaleProvider, {
1397
3725
  locale: locale,
1398
- children: /*#__PURE__*/ jsxs("div", {
1399
- ...rest,
1400
- ref: layoutRef,
1401
- className: [
1402
- 'ov-file-browser',
1403
- className
1404
- ].filter(Boolean).join(' '),
1405
- style: {
1406
- ...style,
1407
- '--ov-file-browser-tree-width': `${treeWidth}px`
1408
- },
1409
- children: [
1410
- /*#__PURE__*/ jsx("div", {
1411
- className: "ov-file-browser__tree",
1412
- children: /*#__PURE__*/ jsx(FileBrowserTreePane, {
1413
- entries: {
1414
- agentConnect,
1415
- onImportResources,
1416
- renderImportResources,
1417
- labels: labels?.entries
1418
- },
1419
- searchId: searchId,
1420
- browser: browser,
1421
- treeLabels: text.tree,
3726
+ children: [
3727
+ /*#__PURE__*/ jsxs("div", {
3728
+ ...rest,
3729
+ ref: layoutRef,
3730
+ className: [
3731
+ 'ov-file-browser',
3732
+ className
3733
+ ].filter(Boolean).join(' '),
3734
+ style: {
3735
+ ...style,
3736
+ '--ov-file-browser-tree-width': `${treeWidth}px`
3737
+ },
3738
+ children: [
3739
+ /*#__PURE__*/ jsx("div", {
3740
+ className: "ov-file-browser__tree",
3741
+ children: /*#__PURE__*/ jsx(FileBrowserTreePane, {
3742
+ entries: {
3743
+ agentConnect,
3744
+ onImportResources,
3745
+ renderImportResources,
3746
+ labels: labels?.entries
3747
+ },
3748
+ onSearch: ()=>setSearchOpen(true),
3749
+ browser: browser,
3750
+ treeLabels: text.tree,
3751
+ locale: locale,
3752
+ dashboardLabels: labels?.dashboard,
3753
+ searchPlaceholder: text.searchPlaceholder,
3754
+ refreshLabel: text.refresh,
3755
+ retryLabel: text.retry
3756
+ })
3757
+ }),
3758
+ /*#__PURE__*/ jsx(ResizableHandle, {
3759
+ label: text.resizeTree,
3760
+ min: 200,
3761
+ max: treeMaxWidth,
3762
+ value: treeWidth,
3763
+ onResize: setPreferredTreeWidth,
3764
+ className: "ov-file-browser__resizer"
3765
+ }),
3766
+ browser.previewTabs && browser.previewTabs.length > 0 ? /*#__PURE__*/ jsx(FileBrowserDetail, {
3767
+ tabs: browser.previewTabs,
3768
+ activeKey: browser.previewKey,
1422
3769
  locale: locale,
1423
- searchPlaceholder: text.searchPlaceholder,
1424
- refreshLabel: text.refresh,
1425
- searchLoading: text.searchLoading,
1426
- searchEmpty: text.searchEmpty,
1427
- searchErrorLabel: text.searchFailed,
1428
- retryLabel: text.retry
1429
- })
1430
- }),
1431
- /*#__PURE__*/ jsx(ResizableHandle, {
1432
- label: text.resizeTree,
1433
- min: 200,
1434
- max: treeMaxWidth,
1435
- value: treeWidth,
1436
- onResize: setPreferredTreeWidth,
1437
- className: "ov-file-browser__resizer"
1438
- }),
1439
- browser.previewTabs && browser.previewTabs.length > 0 ? /*#__PURE__*/ jsx(FileBrowserDetail, {
1440
- tabs: browser.previewTabs,
1441
- activeKey: browser.previewKey,
1442
- locale: locale,
1443
- labels: previewLabels,
1444
- footer: browser.previewFooter,
1445
- viewLabels: {
1446
- preview: text.previewMode,
1447
- source: text.sourceMode,
1448
- copyPath: text.copyPath,
1449
- copyContent: text.copyContent,
1450
- download: text.download,
1451
- viewSwitch: text.viewSwitch
1452
- },
1453
- onActiveKeyChange: browser.actions.setPreviewKey
1454
- }) : browser.currentUri && browser.previewError ? /*#__PURE__*/ jsx("div", {
1455
- className: "ov-file-browser__detail",
1456
- children: /*#__PURE__*/ jsx(FileBrowserFailure, {
1457
- message: text.error,
1458
- detail: browser.previewError.message,
1459
- retryLabel: text.retry,
1460
- onRetry: ()=>void browser.actions.refresh()
1461
- })
1462
- }) : /*#__PURE__*/ jsx("div", {
1463
- className: "ov-file-browser__detail",
1464
- children: /*#__PURE__*/ jsx(FileBrowserEmpty, {
1465
- label: text.empty
3770
+ labels: previewLabels,
3771
+ footer: browser.previewFooter,
3772
+ titleExtra: permissions.titleExtra,
3773
+ extraActions: permissions.actions,
3774
+ viewLabels: {
3775
+ preview: text.previewMode,
3776
+ source: text.sourceMode,
3777
+ copyPath: text.copyPath,
3778
+ copyContent: text.copyContent,
3779
+ download: text.download,
3780
+ viewSwitch: text.viewSwitch
3781
+ },
3782
+ onActiveKeyChange: browser.actions.setPreviewKey
3783
+ }) : browser.currentUri && browser.previewError ? /*#__PURE__*/ jsx("div", {
3784
+ className: "ov-file-browser__detail",
3785
+ children: /*#__PURE__*/ jsx(FileBrowserFailure, {
3786
+ message: text.error,
3787
+ detail: browser.previewError.message,
3788
+ retryLabel: text.retry,
3789
+ onRetry: ()=>void browser.actions.refresh()
3790
+ })
3791
+ }) : /*#__PURE__*/ jsx("div", {
3792
+ className: "ov-file-browser__detail",
3793
+ children: /*#__PURE__*/ jsx(FileBrowserEmpty, {
3794
+ label: text.empty
3795
+ })
1466
3796
  })
1467
- })
1468
- ]
1469
- })
3797
+ ]
3798
+ }),
3799
+ permissions.drawer,
3800
+ /*#__PURE__*/ jsx(FileBrowserSearch, {
3801
+ open: searchOpen,
3802
+ locale: locale,
3803
+ rootUri: rootUri,
3804
+ triggerRef: layoutRef,
3805
+ text: text,
3806
+ requestOptions: requestOptions,
3807
+ onClose: ()=>setSearchOpen(false)
3808
+ })
3809
+ ]
1470
3810
  });
1471
3811
  }
1472
- export { FileBrowser, useFileBrowser };
3812
+ export { FileBrowser };