@openviking/react 0.1.6 → 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.
- package/README.md +2 -0
- package/dist/2~0.js +6 -5
- package/dist/3c~0.js +10 -4
- package/dist/3~0.js +18 -8
- package/dist/ae~0.js +8 -1
- package/dist/ark-message-list-view~0.js +9 -4
- package/dist/a~0.js +12 -6
- package/dist/c7r~0.js +50 -0
- package/dist/ck~0.js +3 -0
- package/dist/client/paths.d.ts +6 -0
- package/dist/client/provider.d.ts +6 -1
- package/dist/components/agent-connect/style.css +222 -35
- package/dist/components/directory-tour/style.css +221 -21
- package/dist/components/directory-tour.js +1 -1
- package/dist/components/experience-dashboard/style.css +227 -12
- package/dist/components/experience-dashboard.js +1 -1
- package/dist/components/file-browser-entries/style.css +325 -80
- package/dist/components/file-preview/style.css +221 -76
- package/dist/components/file-tree/file-tree-toolbar.d.ts +3 -3
- package/dist/components/file-tree/locales/en-US.d.ts +3 -0
- package/dist/components/file-tree/locales/zh-CN.d.ts +3 -0
- package/dist/components/file-tree/style.css +311 -86
- package/dist/components/file-tree/types.d.ts +13 -4
- package/dist/components/scope-switcher/style.css +366 -37
- package/dist/components/scope-switcher.js +1 -1
- package/dist/components/style.css +3329 -640
- package/dist/components/tui/style.css +267 -35
- package/dist/components/viking-bot/interaction/style.css +143 -22
- package/dist/components/viking-bot/style.css +945 -295
- package/dist/components/wiki-dashboard/style.css +227 -12
- package/dist/c~0.js +16 -8
- package/dist/ep~0.js +345 -0
- package/dist/e~0.js +33 -328
- package/dist/fj~0.js +8 -4
- package/dist/fl~0.js +709 -0
- package/dist/f~0.js +302 -663
- package/dist/index.js +3 -3
- package/dist/lk~0.js +6 -3
- package/dist/l~0.js +1874 -437
- package/dist/modules/console/style.css +1471 -351
- package/dist/modules/file-browser/acl/acl-add-principal.d.ts +12 -0
- package/dist/modules/file-browser/acl/acl-candidate.d.ts +8 -0
- package/dist/modules/file-browser/acl/acl-client.d.ts +13 -0
- package/dist/modules/file-browser/acl/acl-confirmation.d.ts +6 -0
- package/dist/modules/file-browser/acl/acl-entry.d.ts +13 -0
- package/dist/modules/file-browser/acl/acl-labels.d.ts +47 -0
- package/dist/modules/file-browser/acl/acl-menu.d.ts +10 -0
- package/dist/modules/file-browser/acl/acl-modal.d.ts +17 -0
- package/dist/modules/file-browser/acl/acl-model.d.ts +17 -0
- package/dist/modules/file-browser/acl/acl-principal-tabs.d.ts +6 -0
- package/dist/modules/file-browser/acl/acl-tag.d.ts +7 -0
- package/dist/modules/file-browser/acl/locales/en-US.d.ts +44 -0
- package/dist/modules/file-browser/acl/locales/zh-CN.d.ts +44 -0
- package/dist/modules/file-browser/acl/use-acl-principals.d.ts +17 -0
- package/dist/modules/file-browser/acl/use-acl-report.d.ts +27 -0
- package/dist/modules/file-browser/acl-types.d.ts +86 -0
- package/dist/modules/file-browser/dashboard-model.d.ts +3 -0
- package/dist/modules/file-browser/file-browser-detail.d.ts +4 -1
- package/dist/modules/file-browser/file-browser-labels.d.ts +1 -1
- package/dist/modules/file-browser/file-browser-search.d.ts +20 -0
- package/dist/modules/file-browser/file-browser-toolbar.d.ts +3 -10
- package/dist/modules/file-browser/file-browser-tree-pane.d.ts +2 -6
- package/dist/modules/file-browser/file-browser.d.ts +1 -1
- package/dist/modules/file-browser/fs-directories.d.ts +1 -0
- package/dist/modules/file-browser/fs-model.d.ts +3 -1
- package/dist/modules/file-browser/index.d.ts +1 -0
- package/dist/modules/file-browser/locales/en-US.d.ts +5 -0
- package/dist/modules/file-browser/locales/zh-CN.d.ts +5 -0
- package/dist/modules/file-browser/style.css +2409 -603
- package/dist/modules/file-browser/types.d.ts +20 -20
- package/dist/modules/file-browser/use-file-browser-acl.d.ts +15 -0
- package/dist/modules/file-browser/use-file-browser.d.ts +5 -7
- package/dist/modules/file-browser/use-fs-tree.d.ts +1 -0
- package/dist/modules/scope-cascade/style.css +535 -37
- package/dist/modules/style.css +12227 -8757
- package/dist/n~0.js +60 -36
- package/dist/rt~0.js +107 -4
- package/dist/r~0.js +1 -1
- package/dist/shared/components/button/button.d.ts +20 -0
- package/dist/shared/components/input/input.d.ts +17 -0
- package/dist/shared/components/modal/locales/en-US.d.ts +4 -0
- package/dist/shared/components/modal/locales/zh-CN.d.ts +4 -0
- package/dist/shared/components/modal/modal.d.ts +2 -0
- package/dist/shared/components/modal/types.d.ts +18 -0
- package/dist/shared/components/overlay/lock-body-scroll.d.ts +1 -0
- package/dist/style.css +7776 -1571
- package/dist/s~0.js +16 -0
- package/dist/u~0.js +15 -6
- package/dist/viking-bot-composer~0.js +29 -11
- package/dist/viking-bot-interaction~0.js +2 -1
- package/package.json +3 -2
- package/dist/fi~0.js +0 -354
- package/dist/modules/file-browser/fs-selection.d.ts +0 -4
- package/dist/o~0.js +0 -106
package/dist/l~0.js
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
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 {
|
|
6
|
+
import { button_Button } from "./e~0.js";
|
|
7
|
+
import { FileTree as file_tree_FileTree, FileTreeToolbar } from "./n~0.js";
|
|
5
8
|
import { getFileTypeColor, getFileTypeIcon, WikiBookIcon, ExperienceBoardIcon, WikiConflictBoardIcon, FolderIcon, WikiBoardIcon, resolveFileTypeIconKey } from "./k~0.js";
|
|
6
|
-
import { FileBrowserEntries } from "./2~0.js";
|
|
7
|
-
import {
|
|
9
|
+
import { FileBrowserEntries, es2015, lockBodyScroll } from "./2~0.js";
|
|
10
|
+
import { removereact } from "./4~0.js";
|
|
11
|
+
import { useLocale, LocaleProvider } from "./28~0.js";
|
|
8
12
|
import { resolveLocale } from "./nx~0.js";
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
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";
|
|
12
18
|
import { WikiDashboard } from "./fj~0.js";
|
|
13
|
-
import {
|
|
19
|
+
import { chevron_downreact } from "./s~0.js";
|
|
20
|
+
import { checkreact } from "./0~0.js";
|
|
14
21
|
function clampResizableWidth(width, min, max) {
|
|
15
22
|
const rounded = Math.round(width);
|
|
16
23
|
const lower = Math.max(min, rounded);
|
|
@@ -122,11 +129,11 @@ function DownloadIcon() {
|
|
|
122
129
|
]
|
|
123
130
|
});
|
|
124
131
|
}
|
|
125
|
-
function FileBrowserDetail({ tabs, activeKey, locale, labels, footer, viewLabels, onActiveKeyChange }) {
|
|
132
|
+
function FileBrowserDetail({ tabs, activeKey, locale, labels, footer, titleExtra, extraActions, viewLabels, onActiveKeyChange }) {
|
|
126
133
|
return /*#__PURE__*/ jsx("div", {
|
|
127
134
|
className: "ov-file-browser__detail",
|
|
128
135
|
children: /*#__PURE__*/ jsx(file_preview_FilePreview, {
|
|
129
|
-
tabs: decorateTabs(tabs, viewLabels, footer),
|
|
136
|
+
tabs: decorateTabs(tabs, viewLabels, footer, titleExtra, extraActions),
|
|
130
137
|
activeKey: activeKey ?? void 0,
|
|
131
138
|
locale: locale,
|
|
132
139
|
labels: labels,
|
|
@@ -134,7 +141,7 @@ function FileBrowserDetail({ tabs, activeKey, locale, labels, footer, viewLabels
|
|
|
134
141
|
})
|
|
135
142
|
});
|
|
136
143
|
}
|
|
137
|
-
function decorateTabs(tabs, viewLabels, footer) {
|
|
144
|
+
function decorateTabs(tabs, viewLabels, footer, titleExtra, extraActions) {
|
|
138
145
|
return tabs.map((tab)=>{
|
|
139
146
|
if ('content' === tab.kind) return tab;
|
|
140
147
|
return {
|
|
@@ -142,9 +149,16 @@ function decorateTabs(tabs, viewLabels, footer) {
|
|
|
142
149
|
slots: {
|
|
143
150
|
...tab.slots,
|
|
144
151
|
footer,
|
|
152
|
+
titleExtra: /*#__PURE__*/ jsxs(Fragment, {
|
|
153
|
+
children: [
|
|
154
|
+
tab.slots?.titleExtra,
|
|
155
|
+
titleExtra
|
|
156
|
+
]
|
|
157
|
+
}),
|
|
145
158
|
actions: (actions)=>/*#__PURE__*/ jsxs("div", {
|
|
146
159
|
className: "ov-file-browser__detail-actions",
|
|
147
160
|
children: [
|
|
161
|
+
extraActions,
|
|
148
162
|
actions.canSwitchView ? /*#__PURE__*/ jsxs("div", {
|
|
149
163
|
className: "ov-file-browser__segmented",
|
|
150
164
|
role: "group",
|
|
@@ -170,16 +184,22 @@ function decorateTabs(tabs, viewLabels, footer) {
|
|
|
170
184
|
"aria-hidden": "true",
|
|
171
185
|
className: "ov-file-browser__detail-sep"
|
|
172
186
|
}) : null,
|
|
173
|
-
actions.canCopyContent ? /*#__PURE__*/ jsx(
|
|
174
|
-
|
|
187
|
+
actions.canCopyContent ? /*#__PURE__*/ jsx(button_Button, {
|
|
188
|
+
size: "small",
|
|
189
|
+
type: "text",
|
|
190
|
+
iconOnly: true,
|
|
191
|
+
htmlType: "button",
|
|
175
192
|
className: "ov-file-browser__detail-icon",
|
|
176
193
|
"aria-label": viewLabels.copyContent,
|
|
177
194
|
title: viewLabels.copyContent,
|
|
178
195
|
onClick: ()=>void actions.copyContent(),
|
|
179
196
|
children: /*#__PURE__*/ jsx(CopyIcon, {})
|
|
180
197
|
}) : null,
|
|
181
|
-
actions.canDownload ? /*#__PURE__*/ jsx(
|
|
182
|
-
|
|
198
|
+
actions.canDownload ? /*#__PURE__*/ jsx(button_Button, {
|
|
199
|
+
size: "small",
|
|
200
|
+
type: "text",
|
|
201
|
+
iconOnly: true,
|
|
202
|
+
htmlType: "button",
|
|
183
203
|
className: "ov-file-browser__detail-icon",
|
|
184
204
|
"aria-label": viewLabels.download,
|
|
185
205
|
title: viewLabels.download,
|
|
@@ -223,8 +243,10 @@ function FileBrowserFailure({ message, detail, retryLabel, onRetry }) {
|
|
|
223
243
|
className: "ov-file-browser__failure-detail",
|
|
224
244
|
children: detail
|
|
225
245
|
}) : null,
|
|
226
|
-
/*#__PURE__*/ jsx(
|
|
227
|
-
|
|
246
|
+
/*#__PURE__*/ jsx(button_Button, {
|
|
247
|
+
size: "small",
|
|
248
|
+
type: "default",
|
|
249
|
+
htmlType: "button",
|
|
228
250
|
className: "ov-file-browser__failure-retry",
|
|
229
251
|
onClick: onRetry,
|
|
230
252
|
children: retryLabel
|
|
@@ -260,70 +282,20 @@ const dashboard_labels_dashboardLabels = (locale, labels)=>({
|
|
|
260
282
|
...'zh-CN' === locale ? zh : en,
|
|
261
283
|
...labels
|
|
262
284
|
});
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
width: file_browser_icons_SIZE,
|
|
266
|
-
height: file_browser_icons_SIZE,
|
|
267
|
-
viewBox: '0 0 48 48',
|
|
268
|
-
fill: 'none',
|
|
269
|
-
stroke: 'currentColor',
|
|
270
|
-
strokeWidth: 4,
|
|
271
|
-
'aria-hidden': true,
|
|
272
|
-
focusable: 'false'
|
|
273
|
-
};
|
|
274
|
-
function SearchIcon() {
|
|
275
|
-
return /*#__PURE__*/ jsx("svg", {
|
|
276
|
-
...strokeProps,
|
|
277
|
-
children: /*#__PURE__*/ jsx("path", {
|
|
278
|
-
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"
|
|
279
|
-
})
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
function RefreshIcon() {
|
|
283
|
-
return /*#__PURE__*/ jsx("svg", {
|
|
284
|
-
...strokeProps,
|
|
285
|
-
children: /*#__PURE__*/ jsx("path", {
|
|
286
|
-
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"
|
|
287
|
-
})
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
function FileBrowserToolbar({ searchId, query, refreshing, canRefresh, placeholder, refreshLabel, onQueryChange, onRefresh }) {
|
|
291
|
-
return /*#__PURE__*/ jsxs("div", {
|
|
285
|
+
function FileBrowserToolbar({ refreshing, canRefresh, placeholder, refreshLabel, onSearch, onRefresh }) {
|
|
286
|
+
return /*#__PURE__*/ jsx(FileTreeToolbar, {
|
|
292
287
|
className: "ov-file-browser__toolbar",
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
type: "search",
|
|
305
|
-
className: "ov-file-browser__search-input",
|
|
306
|
-
placeholder: placeholder,
|
|
307
|
-
value: query,
|
|
308
|
-
onChange: (event)=>onQueryChange(event.target.value)
|
|
309
|
-
})
|
|
310
|
-
]
|
|
311
|
-
}),
|
|
312
|
-
/*#__PURE__*/ jsx(Tooltip, {
|
|
313
|
-
content: refreshLabel,
|
|
314
|
-
children: /*#__PURE__*/ jsx("button", {
|
|
315
|
-
type: "button",
|
|
316
|
-
className: [
|
|
317
|
-
'ov-file-browser__refresh',
|
|
318
|
-
refreshing ? 'ov-file-browser__refresh--spinning' : ''
|
|
319
|
-
].filter(Boolean).join(' '),
|
|
320
|
-
"aria-label": refreshLabel,
|
|
321
|
-
disabled: !canRefresh,
|
|
322
|
-
onClick: onRefresh,
|
|
323
|
-
children: /*#__PURE__*/ jsx(RefreshIcon, {})
|
|
324
|
-
})
|
|
325
|
-
})
|
|
326
|
-
]
|
|
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
|
|
327
299
|
});
|
|
328
300
|
}
|
|
329
301
|
function DashboardIndicator({ dashboard, locale, labels, onOpen }) {
|
|
@@ -361,7 +333,7 @@ function directoryIcon() {
|
|
|
361
333
|
className: "ov-file-tree__file-type-icon"
|
|
362
334
|
});
|
|
363
335
|
}
|
|
364
|
-
function FileBrowserTreePane({ entries, locale, dashboardLabels,
|
|
336
|
+
function FileBrowserTreePane({ entries, locale, dashboardLabels, onSearch, browser, treeLabels, searchPlaceholder, refreshLabel, retryLabel }) {
|
|
365
337
|
return /*#__PURE__*/ jsxs(Fragment, {
|
|
366
338
|
children: [
|
|
367
339
|
/*#__PURE__*/ jsx(FileBrowserEntries, {
|
|
@@ -370,13 +342,11 @@ function FileBrowserTreePane({ entries, locale, dashboardLabels, searchId, brows
|
|
|
370
342
|
className: "ov-file-browser__entries"
|
|
371
343
|
}),
|
|
372
344
|
/*#__PURE__*/ jsx(FileBrowserToolbar, {
|
|
373
|
-
|
|
374
|
-
query: browser.query,
|
|
345
|
+
onSearch: onSearch,
|
|
375
346
|
refreshing: browser.refreshing,
|
|
376
347
|
canRefresh: browser.isReady,
|
|
377
348
|
placeholder: searchPlaceholder,
|
|
378
349
|
refreshLabel: refreshLabel,
|
|
379
|
-
onQueryChange: browser.actions.setQuery,
|
|
380
350
|
onRefresh: ()=>void browser.actions.refresh()
|
|
381
351
|
}),
|
|
382
352
|
/*#__PURE__*/ jsxs("div", {
|
|
@@ -394,41 +364,23 @@ function FileBrowserTreePane({ entries, locale, dashboardLabels, searchId, brows
|
|
|
394
364
|
browser.dashboardError.message
|
|
395
365
|
]
|
|
396
366
|
}),
|
|
397
|
-
/*#__PURE__*/ jsx(
|
|
398
|
-
|
|
367
|
+
/*#__PURE__*/ jsx(button_Button, {
|
|
368
|
+
size: "small",
|
|
369
|
+
type: "default",
|
|
370
|
+
htmlType: "button",
|
|
399
371
|
onClick: ()=>void browser.actions.refresh(),
|
|
400
372
|
children: retryLabel
|
|
401
373
|
})
|
|
402
374
|
]
|
|
403
375
|
}) : null,
|
|
404
376
|
/*#__PURE__*/ jsx(file_tree_FileTree, {
|
|
377
|
+
search: false,
|
|
378
|
+
refresh: false,
|
|
405
379
|
locale: locale,
|
|
406
380
|
rows: browser.rows,
|
|
407
381
|
selectedId: browser.selectedId,
|
|
408
382
|
isReady: browser.isReady,
|
|
409
383
|
labels: treeLabels,
|
|
410
|
-
emptyFallback: browser.searching ? browser.searchError ? /*#__PURE__*/ jsxs("div", {
|
|
411
|
-
className: "ov-file-browser__hint ov-file-browser__hint--error",
|
|
412
|
-
children: [
|
|
413
|
-
/*#__PURE__*/ jsx("p", {
|
|
414
|
-
className: "ov-file-browser__failure-title",
|
|
415
|
-
children: searchErrorLabel
|
|
416
|
-
}),
|
|
417
|
-
/*#__PURE__*/ jsx("p", {
|
|
418
|
-
className: "ov-file-browser__failure-detail",
|
|
419
|
-
children: browser.searchError.message
|
|
420
|
-
}),
|
|
421
|
-
/*#__PURE__*/ jsx("button", {
|
|
422
|
-
type: "button",
|
|
423
|
-
className: "ov-file-browser__retry",
|
|
424
|
-
onClick: ()=>void browser.actions.refresh(),
|
|
425
|
-
children: retryLabel
|
|
426
|
-
})
|
|
427
|
-
]
|
|
428
|
-
}) : /*#__PURE__*/ jsx("p", {
|
|
429
|
-
className: "ov-file-browser__hint",
|
|
430
|
-
children: browser.searchLoading ? searchLoading : searchEmpty
|
|
431
|
-
}) : void 0,
|
|
432
384
|
renderRowIcon: (row)=>browser.dashboards.get(row.uri)?.isRoot ? /*#__PURE__*/ jsx(WikiBookIcon, {
|
|
433
385
|
width: 14,
|
|
434
386
|
height: 14
|
|
@@ -454,106 +406,138 @@ function FileBrowserTreePane({ entries, locale, dashboardLabels, searchId, brows
|
|
|
454
406
|
]
|
|
455
407
|
});
|
|
456
408
|
}
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
empty: 'Select a file to preview',
|
|
460
|
-
error: 'Failed to load the directory',
|
|
461
|
-
retry: 'Retry',
|
|
462
|
-
refresh: 'Refresh',
|
|
463
|
-
searchPlaceholder: 'Search files ⌘+K...',
|
|
464
|
-
searchEmpty: 'No matching resources',
|
|
465
|
-
searchFailed: 'Failed to run the search',
|
|
466
|
-
loading: 'Loading…',
|
|
467
|
-
searchLoading: 'Searching…',
|
|
468
|
-
previewMode: 'Preview',
|
|
469
|
-
sourceMode: 'Source',
|
|
470
|
-
copyPath: 'Copy path',
|
|
471
|
-
copyContent: 'Copy content',
|
|
472
|
-
download: 'Download',
|
|
473
|
-
history: 'Version history',
|
|
474
|
-
viewSwitch: 'Switch view',
|
|
475
|
-
resizeTree: 'Resize the directory tree',
|
|
476
|
-
l0Summary: 'L0 Summary',
|
|
477
|
-
l1Overview: 'L1 Overview',
|
|
478
|
-
summaryEmpty: 'No summary generated yet',
|
|
479
|
-
summaryFailed: 'The summary could not be loaded.'
|
|
409
|
+
const en_US = {
|
|
410
|
+
closeLabel: 'Close dialog'
|
|
480
411
|
};
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
formatCharacterCount: (count)=>`${count.toLocaleString('zh-CN')} 个字符`,
|
|
484
|
-
empty: '选择一个文件查看内容',
|
|
485
|
-
error: '目录加载失败',
|
|
486
|
-
retry: '重试',
|
|
487
|
-
refresh: '刷新',
|
|
488
|
-
searchPlaceholder: '搜索文件 ⌘+K...',
|
|
489
|
-
searchEmpty: '没有匹配的资源',
|
|
490
|
-
searchFailed: '搜索失败',
|
|
491
|
-
loading: '加载中…',
|
|
492
|
-
searchLoading: '搜索中…',
|
|
493
|
-
previewMode: '预览',
|
|
494
|
-
sourceMode: '源码',
|
|
495
|
-
copyPath: '复制路径',
|
|
496
|
-
copyContent: '复制正文',
|
|
497
|
-
download: '下载',
|
|
498
|
-
history: '版本历史',
|
|
499
|
-
viewSwitch: '切换视图',
|
|
500
|
-
resizeTree: '调整目录树宽度',
|
|
501
|
-
l0Summary: 'L0 摘要',
|
|
502
|
-
l1Overview: 'L1 总览',
|
|
503
|
-
summaryEmpty: '暂无生成内容',
|
|
504
|
-
summaryFailed: '摘要加载失败。'
|
|
412
|
+
const zh_CN = {
|
|
413
|
+
closeLabel: '关闭弹窗'
|
|
505
414
|
};
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
'
|
|
509
|
-
'
|
|
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'
|
|
510
421
|
};
|
|
511
|
-
function
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
function resolveSummaryLabels(locale, labels) {
|
|
527
|
-
const text = resolveLabels(labels, locale);
|
|
528
|
-
return {
|
|
529
|
-
l0: text.l0Summary,
|
|
530
|
-
l1: text.l1Overview,
|
|
531
|
-
failure: text.summaryFailed
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
function useContainerWidth() {
|
|
535
|
-
const ref = useRef(null);
|
|
536
|
-
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);
|
|
537
435
|
useEffect(()=>{
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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);
|
|
552
521
|
}
|
|
553
|
-
function
|
|
554
|
-
|
|
555
|
-
const
|
|
556
|
-
|
|
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;
|
|
557
541
|
}
|
|
558
542
|
const emptyDir = ()=>({
|
|
559
543
|
entries: [],
|
|
@@ -590,6 +574,9 @@ function toEntryRow(entry, depth, expanded, index, busy) {
|
|
|
590
574
|
name: nameFromUri(entry.uri),
|
|
591
575
|
type: isDir ? 'directory' : 'file',
|
|
592
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),
|
|
593
580
|
busy: busy || void 0
|
|
594
581
|
};
|
|
595
582
|
}
|
|
@@ -689,37 +676,302 @@ function flattenTree(uri, depth, dirs, expanded) {
|
|
|
689
676
|
function isAbort(error) {
|
|
690
677
|
return 'object' == typeof error && null !== error && 'AbortError' === error.name;
|
|
691
678
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
const
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
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
|
|
701
702
|
},
|
|
702
|
-
options
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
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
|
|
713
724
|
]);
|
|
714
|
-
const aborted = [
|
|
715
|
-
abstractResult,
|
|
716
|
-
overviewResult
|
|
717
|
-
].find((result)=>!result.ok && isAbort(result.cause));
|
|
718
|
-
if (aborted && !aborted.ok) throw aborted.cause;
|
|
719
|
-
if (!abstractResult.ok && !overviewResult.ok) throw abstractResult.cause;
|
|
720
|
-
const reason = (result)=>result.ok ? void 0 : result.cause instanceof Error ? result.cause.message : String(result.cause);
|
|
721
725
|
return {
|
|
722
|
-
|
|
726
|
+
results,
|
|
727
|
+
loading,
|
|
728
|
+
error
|
|
729
|
+
};
|
|
730
|
+
}
|
|
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 : '',
|
|
723
975
|
overview: overviewResult.ok ? overviewResult.text : '',
|
|
724
976
|
abstractError: reason(abstractResult),
|
|
725
977
|
overviewError: reason(overviewResult)
|
|
@@ -1093,77 +1345,6 @@ function loadingTab(uri, isDirectory) {
|
|
|
1093
1345
|
}
|
|
1094
1346
|
};
|
|
1095
1347
|
}
|
|
1096
|
-
function mergeFindResult(result) {
|
|
1097
|
-
const seen = new Set();
|
|
1098
|
-
const entries = [];
|
|
1099
|
-
for (const group of [
|
|
1100
|
-
result?.memories,
|
|
1101
|
-
result?.resources,
|
|
1102
|
-
result?.skills
|
|
1103
|
-
])for (const item of group ?? []){
|
|
1104
|
-
const uri = item?.uri;
|
|
1105
|
-
if (!(!uri || seen.has(uri))) {
|
|
1106
|
-
seen.add(uri);
|
|
1107
|
-
entries.push({
|
|
1108
|
-
uri,
|
|
1109
|
-
isDir: false,
|
|
1110
|
-
abstract: item.abstract?.trim() || void 0
|
|
1111
|
-
});
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
return entries;
|
|
1115
|
-
}
|
|
1116
|
-
const LIMIT = 20;
|
|
1117
|
-
function useFsSearch({ client, query, targetUri, generation = 0, requestOptions }) {
|
|
1118
|
-
const [results, setResults] = useState(null);
|
|
1119
|
-
const [loading, setLoading] = useState(false);
|
|
1120
|
-
const [error, setError] = useState(null);
|
|
1121
|
-
useEffect(()=>{
|
|
1122
|
-
if (!client || !query) {
|
|
1123
|
-
setResults(null);
|
|
1124
|
-
setLoading(false);
|
|
1125
|
-
setError(null);
|
|
1126
|
-
return;
|
|
1127
|
-
}
|
|
1128
|
-
const controller = new AbortController();
|
|
1129
|
-
setLoading(true);
|
|
1130
|
-
setError(null);
|
|
1131
|
-
client.request({
|
|
1132
|
-
method: 'POST',
|
|
1133
|
-
path: OV_PATHS.searchFind,
|
|
1134
|
-
body: {
|
|
1135
|
-
query,
|
|
1136
|
-
target_uri: targetUri,
|
|
1137
|
-
limit: LIMIT
|
|
1138
|
-
},
|
|
1139
|
-
options: {
|
|
1140
|
-
...requestOptions,
|
|
1141
|
-
signal: controller.signal
|
|
1142
|
-
}
|
|
1143
|
-
}).then((result)=>{
|
|
1144
|
-
if (controller.signal.aborted) return;
|
|
1145
|
-
setResults(mergeFindResult(result));
|
|
1146
|
-
setLoading(false);
|
|
1147
|
-
}, (cause)=>{
|
|
1148
|
-
if (isAbort(cause) || controller.signal.aborted) return;
|
|
1149
|
-
setResults([]);
|
|
1150
|
-
setError(toError(cause));
|
|
1151
|
-
setLoading(false);
|
|
1152
|
-
});
|
|
1153
|
-
return ()=>controller.abort();
|
|
1154
|
-
}, [
|
|
1155
|
-
client,
|
|
1156
|
-
query,
|
|
1157
|
-
targetUri,
|
|
1158
|
-
generation,
|
|
1159
|
-
requestOptions
|
|
1160
|
-
]);
|
|
1161
|
-
return {
|
|
1162
|
-
results,
|
|
1163
|
-
loading,
|
|
1164
|
-
error
|
|
1165
|
-
};
|
|
1166
|
-
}
|
|
1167
1348
|
const isHomeUri = (uri)=>'viking://~' === uri || uri.startsWith('viking://~/');
|
|
1168
1349
|
async function readDashboardStat(client, uri, options) {
|
|
1169
1350
|
const stat = await client.request({
|
|
@@ -1278,6 +1459,12 @@ function resolveDashboard(uri, rows, targets) {
|
|
|
1278
1459
|
pendingConflicts: tagged ? 'has_conflict=true' === tagged : owner?.to === uri && void 0 !== owner.conflicts_num ? owner.conflicts_num > 0 : void 0
|
|
1279
1460
|
};
|
|
1280
1461
|
}
|
|
1462
|
+
function mergeDashboardTab(tabs, dashboard, blocked) {
|
|
1463
|
+
return blocked ? null : dashboard ? [
|
|
1464
|
+
...tabs ?? [],
|
|
1465
|
+
dashboard
|
|
1466
|
+
] : tabs;
|
|
1467
|
+
}
|
|
1281
1468
|
async function readCompileTargets(client, rootUri, options) {
|
|
1282
1469
|
rootUri = await resolveDashboardUri(client, rootUri, options);
|
|
1283
1470
|
const targets = [];
|
|
@@ -1550,9 +1737,9 @@ function ExperienceDashboardPane({ uri, locale, requestOptions, refreshGeneratio
|
|
|
1550
1737
|
const wiki_conflicts_prefix = '<!-- CONFLICTS_CONTENT';
|
|
1551
1738
|
const manualResolutionBasis = '\u4eba\u5de5\u4fee\u590d';
|
|
1552
1739
|
const timestamp = (value)=>'number' == typeof value && Number.isSafeInteger(value) && value >= 0 && Number.isFinite(new Date(1000 * value).getTime());
|
|
1553
|
-
const locateWikiConflict = (
|
|
1740
|
+
const locateWikiConflict = (document1, matchText)=>{
|
|
1554
1741
|
const mask = (value)=>value.replace(/[^\r\n]/g, ' ');
|
|
1555
|
-
const body =
|
|
1742
|
+
const body = document1.replace(/^(?:\uFEFF)?---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/, mask).replace(/<!--[\s\S]*?(?:-->|$)/g, mask);
|
|
1556
1743
|
const start = matchText ? body.indexOf(matchText) : -1;
|
|
1557
1744
|
if (start < 0) return {
|
|
1558
1745
|
status: 'missing'
|
|
@@ -1564,7 +1751,7 @@ const locateWikiConflict = (document, matchText)=>{
|
|
|
1564
1751
|
status: 'unique',
|
|
1565
1752
|
start,
|
|
1566
1753
|
end: start + matchText.length,
|
|
1567
|
-
line:
|
|
1754
|
+
line: document1.slice(0, start).split('\n').length
|
|
1568
1755
|
};
|
|
1569
1756
|
};
|
|
1570
1757
|
const parseWikiFileConflicts = (content)=>{
|
|
@@ -1576,7 +1763,7 @@ const parseWikiFileConflicts = (content)=>{
|
|
|
1576
1763
|
if (!footer.endsWith('-->')) return {
|
|
1577
1764
|
status: 'invalid'
|
|
1578
1765
|
};
|
|
1579
|
-
const
|
|
1766
|
+
const document1 = content.slice(0, start);
|
|
1580
1767
|
const source = footer.slice(wiki_conflicts_prefix.length, -3).trim();
|
|
1581
1768
|
let parsed;
|
|
1582
1769
|
try {
|
|
@@ -1609,7 +1796,7 @@ const parseWikiFileConflicts = (content)=>{
|
|
|
1609
1796
|
status: 'invalid'
|
|
1610
1797
|
};
|
|
1611
1798
|
const matchText = value.match_text;
|
|
1612
|
-
const location = locateWikiConflict(
|
|
1799
|
+
const location = locateWikiConflict(document1, matchText);
|
|
1613
1800
|
records.push({
|
|
1614
1801
|
id: JSON.stringify([
|
|
1615
1802
|
index,
|
|
@@ -1631,7 +1818,7 @@ const parseWikiFileConflicts = (content)=>{
|
|
|
1631
1818
|
}
|
|
1632
1819
|
return {
|
|
1633
1820
|
status: 'available',
|
|
1634
|
-
document,
|
|
1821
|
+
document: document1,
|
|
1635
1822
|
records
|
|
1636
1823
|
};
|
|
1637
1824
|
};
|
|
@@ -1757,7 +1944,7 @@ function useBrowserDashboards({ rows, metadataRows, uri, rootUri, generation, lo
|
|
|
1757
1944
|
uri: target
|
|
1758
1945
|
} : void 0;
|
|
1759
1946
|
};
|
|
1760
|
-
const dashboards = new Map(rows.filter((row)=>'entry' === row.kind).flatMap((row)=>{
|
|
1947
|
+
const dashboards = new Map(rows.filter((row)=>'entry' === row.kind).filter((row)=>!row.disabled && false !== row.selectable).flatMap((row)=>{
|
|
1761
1948
|
const dashboard = resolve(row.uri);
|
|
1762
1949
|
return dashboard && (void 0 === dashboardKinds || dashboardKinds.includes(dashboard.kind)) ? [
|
|
1763
1950
|
[
|
|
@@ -1849,10 +2036,17 @@ function useTreeMetadata(rootUri, dirs) {
|
|
|
1849
2036
|
const metadataRows = useMemo(()=>Object.values(dirs).flatMap((dir)=>dir.entries.map((entry)=>toEntryRow(entry, 0, false))), [
|
|
1850
2037
|
dirs
|
|
1851
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
|
+
]);
|
|
1852
2045
|
return {
|
|
1853
2046
|
directoryUris,
|
|
1854
2047
|
isKnownFile,
|
|
1855
|
-
metadataRows
|
|
2048
|
+
metadataRows,
|
|
2049
|
+
accessOf
|
|
1856
2050
|
};
|
|
1857
2051
|
}
|
|
1858
2052
|
const ids = new WeakMap();
|
|
@@ -2060,6 +2254,7 @@ function useFsTree({ client, rootUri, defaultExpandedUri, requestOptions }) {
|
|
|
2060
2254
|
}
|
|
2061
2255
|
},
|
|
2062
2256
|
toggleExpand: async (row)=>{
|
|
2257
|
+
if (row.disabled) return;
|
|
2063
2258
|
const isOpen = expandedRef.current.has(row.uri);
|
|
2064
2259
|
setExpanded((prev)=>{
|
|
2065
2260
|
const next = new Set(prev);
|
|
@@ -2096,19 +2291,18 @@ function useFsTree({ client, rootUri, defaultExpandedUri, requestOptions }) {
|
|
|
2096
2291
|
dirs,
|
|
2097
2292
|
expanded
|
|
2098
2293
|
]);
|
|
2099
|
-
const metadata = useTreeMetadata(rootUri, dirs);
|
|
2100
2294
|
return {
|
|
2101
2295
|
rows,
|
|
2102
|
-
...
|
|
2296
|
+
...useTreeMetadata(rootUri, dirs),
|
|
2103
2297
|
isReady,
|
|
2104
2298
|
error,
|
|
2105
2299
|
...state
|
|
2106
2300
|
};
|
|
2107
2301
|
}
|
|
2108
|
-
function useFileBrowser({ rootUri, locale: localeProp, dashboardKinds, renderDashboard, dashboardLabels, 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 }) {
|
|
2109
2303
|
const locale = useLocale(localeProp);
|
|
2304
|
+
const text = resolveLabels(accessLabels, locale);
|
|
2110
2305
|
const client = useOvClient();
|
|
2111
|
-
const [query, setQuery] = useState('');
|
|
2112
2306
|
const [pendingUri, setPendingUri] = useState(currentUri);
|
|
2113
2307
|
const [refreshing, setRefreshing] = useState(false);
|
|
2114
2308
|
const [refreshGeneration, setRefreshGeneration] = useState(0);
|
|
@@ -2118,14 +2312,8 @@ function useFileBrowser({ rootUri, locale: localeProp, dashboardKinds, renderDas
|
|
|
2118
2312
|
defaultExpandedUri,
|
|
2119
2313
|
requestOptions
|
|
2120
2314
|
});
|
|
2121
|
-
const search = useFsSearch({
|
|
2122
|
-
client,
|
|
2123
|
-
query,
|
|
2124
|
-
targetUri: rootUri,
|
|
2125
|
-
generation: refreshGeneration,
|
|
2126
|
-
requestOptions
|
|
2127
|
-
});
|
|
2128
2315
|
const activeUri = controlled ? currentUri : pendingUri;
|
|
2316
|
+
const blocked = tree.accessOf(activeUri);
|
|
2129
2317
|
const resourceType = null === activeUri ? 'file' : tree.directoryUris.has(activeUri) ? 'directory' : tree.isKnownFile(activeUri) ? 'file' : 'unknown';
|
|
2130
2318
|
const openTree = tree.open;
|
|
2131
2319
|
const notifyUriChange = useRef(onUriChange);
|
|
@@ -2139,21 +2327,15 @@ function useFileBrowser({ rootUri, locale: localeProp, dashboardKinds, renderDas
|
|
|
2139
2327
|
]);
|
|
2140
2328
|
const preview = useFsPreview({
|
|
2141
2329
|
client,
|
|
2142
|
-
uri: activeUri,
|
|
2330
|
+
uri: blocked ? null : activeUri,
|
|
2143
2331
|
resourceType,
|
|
2144
2332
|
labels: summaryLabels ?? resolveSummaryLabels(locale),
|
|
2145
|
-
formatCharacterCount: formatCharacterCount ??
|
|
2333
|
+
formatCharacterCount: formatCharacterCount ?? text.formatCharacterCount,
|
|
2146
2334
|
openParent,
|
|
2147
2335
|
generation: refreshGeneration,
|
|
2148
2336
|
requestOptions
|
|
2149
2337
|
});
|
|
2150
|
-
const rows =
|
|
2151
|
-
if (null === search.results) return tree.rows;
|
|
2152
|
-
return search.results.map((entry, index)=>toEntryRow(entry, 0, false, index));
|
|
2153
|
-
}, [
|
|
2154
|
-
search.results,
|
|
2155
|
-
tree.rows
|
|
2156
|
-
]);
|
|
2338
|
+
const rows = tree.rows;
|
|
2157
2339
|
const openPath = tree.openPath;
|
|
2158
2340
|
const openResource = useMemo(()=>async (uri)=>{
|
|
2159
2341
|
setPendingUri(uri);
|
|
@@ -2167,7 +2349,7 @@ function useFileBrowser({ rootUri, locale: localeProp, dashboardKinds, renderDas
|
|
|
2167
2349
|
const boards = useBrowserDashboards({
|
|
2168
2350
|
rows,
|
|
2169
2351
|
metadataRows: tree.metadataRows,
|
|
2170
|
-
uri: activeUri,
|
|
2352
|
+
uri: blocked ? null : activeUri,
|
|
2171
2353
|
rootUri,
|
|
2172
2354
|
generation: refreshGeneration,
|
|
2173
2355
|
locale,
|
|
@@ -2177,12 +2359,13 @@ function useFileBrowser({ rootUri, locale: localeProp, dashboardKinds, renderDas
|
|
|
2177
2359
|
renderDashboard,
|
|
2178
2360
|
dashboardLabels
|
|
2179
2361
|
});
|
|
2180
|
-
const selectedId =
|
|
2362
|
+
const selectedId = activeUri;
|
|
2181
2363
|
const { open, toggleExpand, loadMore, retry, refresh } = tree;
|
|
2182
2364
|
const { open: openDashboard, close: closeDashboard } = boards;
|
|
2183
2365
|
const setActiveKey = preview.setActiveKey;
|
|
2184
2366
|
const actions = useMemo(()=>({
|
|
2185
2367
|
select: async (row)=>{
|
|
2368
|
+
if (row.disabled || false === row.selectable) return;
|
|
2186
2369
|
closeDashboard();
|
|
2187
2370
|
setPendingUri(row.uri);
|
|
2188
2371
|
notifyUriChange.current(row.uri);
|
|
@@ -2213,8 +2396,7 @@ function useFileBrowser({ rootUri, locale: localeProp, dashboardKinds, renderDas
|
|
|
2213
2396
|
closeDashboard();
|
|
2214
2397
|
setActiveKey(key);
|
|
2215
2398
|
}
|
|
2216
|
-
}
|
|
2217
|
-
setQuery
|
|
2399
|
+
}
|
|
2218
2400
|
}), [
|
|
2219
2401
|
setActiveKey,
|
|
2220
2402
|
open,
|
|
@@ -2226,52 +2408,1284 @@ function useFileBrowser({ rootUri, locale: localeProp, dashboardKinds, renderDas
|
|
|
2226
2408
|
closeDashboard,
|
|
2227
2409
|
activeUri
|
|
2228
2410
|
]);
|
|
2411
|
+
const previewError = blocked ? new Error(text['denied' === blocked ? 'accessDenied' : 'traverseOnly']) : preview.error;
|
|
2229
2412
|
return {
|
|
2230
2413
|
rows,
|
|
2231
2414
|
selectedId,
|
|
2232
2415
|
currentUri: activeUri,
|
|
2233
2416
|
isReady: tree.isReady,
|
|
2234
|
-
error: tree.error ??
|
|
2417
|
+
error: tree.error ?? previewError,
|
|
2235
2418
|
treeError: tree.error,
|
|
2236
|
-
previewError
|
|
2237
|
-
searchError: search.error,
|
|
2419
|
+
previewError,
|
|
2238
2420
|
dashboards: boards.dashboards,
|
|
2239
2421
|
dashboardError: boards.error,
|
|
2240
|
-
previewTabs: boards.tab
|
|
2241
|
-
...preview.tabs ?? [],
|
|
2242
|
-
boards.tab
|
|
2243
|
-
] : preview.tabs,
|
|
2422
|
+
previewTabs: mergeDashboardTab(preview.tabs, boards.tab, Boolean(blocked)),
|
|
2244
2423
|
previewKey: boards.isOpen && activeUri ? dashboardKey(activeUri) : preview.activeKey,
|
|
2245
2424
|
previewFooter: preview.footer,
|
|
2246
|
-
query,
|
|
2247
|
-
searching: null !== search.results,
|
|
2248
|
-
searchLoading: search.loading,
|
|
2249
2425
|
refreshing,
|
|
2250
2426
|
actions
|
|
2251
2427
|
};
|
|
2252
2428
|
}
|
|
2253
|
-
function
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
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 }) {
|
|
3668
|
+
const locale = useLocale(localeProp);
|
|
3669
|
+
const [searchOpen, setSearchOpen] = useState(false);
|
|
3670
|
+
const [internalUri, setInternalUri] = useState(()=>defaultUri ?? null);
|
|
3671
|
+
const [preferredTreeWidth, setPreferredTreeWidth] = useState(352);
|
|
3672
|
+
const isControlled = void 0 !== uri;
|
|
3673
|
+
const currentUri = isControlled ? uri : internalUri;
|
|
3674
|
+
const text = resolveLabels(labels, locale);
|
|
3675
|
+
const { ref: layoutRef, width: layoutWidth } = useContainerWidth();
|
|
3676
|
+
const treeMaxWidth = resolveTreeMaxWidth(layoutWidth);
|
|
3677
|
+
const treeWidth = Math.min(preferredTreeWidth, treeMaxWidth);
|
|
3678
|
+
const changeUri = useCallback((next)=>{
|
|
3679
|
+
if (!isControlled) setInternalUri(next);
|
|
3680
|
+
onUriChange?.(next);
|
|
3681
|
+
}, [
|
|
3682
|
+
isControlled,
|
|
3683
|
+
onUriChange
|
|
3684
|
+
]);
|
|
3685
|
+
const summaryLabels = useMemo(()=>({
|
|
3686
|
+
l0: text.l0Summary,
|
|
3687
|
+
l1: text.l1Overview,
|
|
3688
|
+
failure: text.summaryFailed
|
|
2275
3689
|
}), [
|
|
2276
3690
|
text.l0Summary,
|
|
2277
3691
|
text.l1Overview,
|
|
@@ -2290,86 +3704,109 @@ function FileBrowser({ uri, defaultUri, rootUri = 'viking://', defaultExpandedUr
|
|
|
2290
3704
|
controlled: isControlled,
|
|
2291
3705
|
summaryLabels,
|
|
2292
3706
|
formatCharacterCount: text.formatCharacterCount,
|
|
3707
|
+
accessLabels: {
|
|
3708
|
+
accessDenied: text.accessDenied,
|
|
3709
|
+
traverseOnly: text.traverseOnly
|
|
3710
|
+
},
|
|
2293
3711
|
defaultExpandedUri,
|
|
2294
3712
|
currentUri,
|
|
2295
3713
|
requestOptions,
|
|
2296
3714
|
onUriChange: changeUri
|
|
2297
3715
|
});
|
|
2298
|
-
|
|
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, {
|
|
2299
3725
|
locale: locale,
|
|
2300
|
-
children:
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
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,
|
|
2324
3769
|
locale: locale,
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
copyPath: text.copyPath,
|
|
2352
|
-
copyContent: text.copyContent,
|
|
2353
|
-
download: text.download,
|
|
2354
|
-
viewSwitch: text.viewSwitch
|
|
2355
|
-
},
|
|
2356
|
-
onActiveKeyChange: browser.actions.setPreviewKey
|
|
2357
|
-
}) : browser.currentUri && browser.previewError ? /*#__PURE__*/ jsx("div", {
|
|
2358
|
-
className: "ov-file-browser__detail",
|
|
2359
|
-
children: /*#__PURE__*/ jsx(FileBrowserFailure, {
|
|
2360
|
-
message: text.error,
|
|
2361
|
-
detail: browser.previewError.message,
|
|
2362
|
-
retryLabel: text.retry,
|
|
2363
|
-
onRetry: ()=>void browser.actions.refresh()
|
|
2364
|
-
})
|
|
2365
|
-
}) : /*#__PURE__*/ jsx("div", {
|
|
2366
|
-
className: "ov-file-browser__detail",
|
|
2367
|
-
children: /*#__PURE__*/ jsx(FileBrowserEmpty, {
|
|
2368
|
-
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
|
+
})
|
|
2369
3796
|
})
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
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
|
+
]
|
|
2373
3810
|
});
|
|
2374
3811
|
}
|
|
2375
3812
|
export { FileBrowser };
|