@openviking/react 0.1.6 → 0.1.8-beta.10.1.g8e5299efeb0a

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