@powerhousedao/ph-clint-app 0.1.0-dev.89 → 0.1.0-dev.90

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 (106) hide show
  1. package/dist/browser/{editor-BuHwuzRa.js → editor-Bp2wUo8R.js} +748 -522
  2. package/dist/browser/editor-Bp2wUo8R.js.map +1 -0
  3. package/dist/browser/editors/ph-clint-project-editor/module.js +1 -1
  4. package/dist/connect/assets/{AddDriveModal-BoKLJyUY-DwlVwt2n.js → AddDriveModal-BoKLJyUY-cGJwd_2F.js} +2 -2
  5. package/dist/connect/assets/{AddDriveModal-BoKLJyUY-DwlVwt2n.js.map → AddDriveModal-BoKLJyUY-cGJwd_2F.js.map} +1 -1
  6. package/dist/connect/assets/{ClearStorageModal-CXoNWmRe-lBEW3sSH.js → ClearStorageModal-CXoNWmRe-D3ifKRm-.js} +2 -2
  7. package/dist/connect/assets/{ClearStorageModal-CXoNWmRe-lBEW3sSH.js.map → ClearStorageModal-CXoNWmRe-D3ifKRm-.js.map} +1 -1
  8. package/dist/connect/assets/{CookiesPolicyModal-1nOh6oN3-BYSDp-5J.js → CookiesPolicyModal-1nOh6oN3-kK_sx5mz.js} +2 -2
  9. package/dist/connect/assets/{CookiesPolicyModal-1nOh6oN3-BYSDp-5J.js.map → CookiesPolicyModal-1nOh6oN3-kK_sx5mz.js.map} +1 -1
  10. package/dist/connect/assets/{CreateDocumentModal-nQbLVDq3-DJbJhlYp.js → CreateDocumentModal-nQbLVDq3-DFAye2CX.js} +2 -2
  11. package/dist/connect/assets/{CreateDocumentModal-nQbLVDq3-DJbJhlYp.js.map → CreateDocumentModal-nQbLVDq3-DFAye2CX.js.map} +1 -1
  12. package/dist/connect/assets/{DebugSettingsModal-5SDhy5gt-Du3PKhbh.js → DebugSettingsModal-5SDhy5gt-DLMPdDQB.js} +2 -2
  13. package/dist/connect/assets/{DebugSettingsModal-5SDhy5gt-Du3PKhbh.js.map → DebugSettingsModal-5SDhy5gt-DLMPdDQB.js.map} +1 -1
  14. package/dist/connect/assets/{DeleteDriveModal-6P0T1QgD-CaEKmseP.js → DeleteDriveModal-6P0T1QgD-CK6cPXbT.js} +2 -2
  15. package/dist/connect/assets/{DeleteDriveModal-6P0T1QgD-CaEKmseP.js.map → DeleteDriveModal-6P0T1QgD-CK6cPXbT.js.map} +1 -1
  16. package/dist/connect/assets/{DeleteItemModal-BmV1aQyZ-B01dssV-.js → DeleteItemModal-BmV1aQyZ-Ld9lHNhl.js} +2 -2
  17. package/dist/connect/assets/{DeleteItemModal-BmV1aQyZ-B01dssV-.js.map → DeleteItemModal-BmV1aQyZ-Ld9lHNhl.js.map} +1 -1
  18. package/dist/connect/assets/{DisclaimerModal-CVTFfN0_-CfPGYzP1.js → DisclaimerModal-CVTFfN0_-D7N9TDI4.js} +2 -2
  19. package/dist/connect/assets/{DisclaimerModal-CVTFfN0_-CfPGYzP1.js.map → DisclaimerModal-CVTFfN0_-D7N9TDI4.js.map} +1 -1
  20. package/dist/connect/assets/{DownloadDocumentWithErrorsModal-CXeFUtaz-UexzRqQT.js → DownloadDocumentWithErrorsModal-CXeFUtaz-CYIIl42M.js} +2 -2
  21. package/dist/connect/assets/{DownloadDocumentWithErrorsModal-CXeFUtaz-UexzRqQT.js.map → DownloadDocumentWithErrorsModal-CXeFUtaz-CYIIl42M.js.map} +1 -1
  22. package/dist/connect/assets/{DriveAuthRequiredModal-B3VdLwSH-C5FZCMC5.js → DriveAuthRequiredModal-B3VdLwSH-fqmI_4sF.js} +2 -2
  23. package/dist/connect/assets/{DriveAuthRequiredModal-B3VdLwSH-C5FZCMC5.js.map → DriveAuthRequiredModal-B3VdLwSH-fqmI_4sF.js.map} +1 -1
  24. package/dist/connect/assets/{DriveSettingsModal-CsNFEJ9E-BoXSbW1c.js → DriveSettingsModal-CsNFEJ9E-BR2vyeU8.js} +2 -2
  25. package/dist/connect/assets/{DriveSettingsModal-CsNFEJ9E-BoXSbW1c.js.map → DriveSettingsModal-CsNFEJ9E-BR2vyeU8.js.map} +1 -1
  26. package/dist/connect/assets/{InspectorModal-BsSihzuc-hukmcBoP.js → InspectorModal-BsSihzuc-B0n5gbGB.js} +2 -2
  27. package/dist/connect/assets/{InspectorModal-BsSihzuc-hukmcBoP.js.map → InspectorModal-BsSihzuc-B0n5gbGB.js.map} +1 -1
  28. package/dist/connect/assets/{MissingPackageModal-Bn036mWQ-BOXHWmFa.js → MissingPackageModal-Bn036mWQ-C021G1nV.js} +2 -2
  29. package/dist/connect/assets/{MissingPackageModal-Bn036mWQ-BOXHWmFa.js.map → MissingPackageModal-Bn036mWQ-C021G1nV.js.map} +1 -1
  30. package/dist/connect/assets/{SettingsModal-S_A5qf95-Fz0f2CXX.js → SettingsModal-S_A5qf95-uzigz0nk.js} +2 -2
  31. package/dist/connect/assets/{SettingsModal-S_A5qf95-Fz0f2CXX.js.map → SettingsModal-S_A5qf95-uzigz0nk.js.map} +1 -1
  32. package/dist/connect/assets/{UpgradeDriveModal-C7lV4rAX-BkeBWpDi.js → UpgradeDriveModal-C7lV4rAX-CTIf4duw.js} +2 -2
  33. package/dist/connect/assets/{UpgradeDriveModal-C7lV4rAX-BkeBWpDi.js.map → UpgradeDriveModal-C7lV4rAX-CTIf4duw.js.map} +1 -1
  34. package/dist/connect/assets/build-info-cc1PjSvF-DtAGzh0W.js.map +1 -1
  35. package/dist/connect/assets/chunk-pbuEa-1d-KEQyMDN_.js.map +1 -1
  36. package/dist/connect/assets/{components-ByEUkO4U-Mc23VVA-.js → components-ByEUkO4U-xPZUVkzr.js} +4 -4
  37. package/dist/connect/assets/{components-ByEUkO4U-Mc23VVA-.js.map → components-ByEUkO4U-xPZUVkzr.js.map} +1 -1
  38. package/dist/connect/assets/{components-HCKyLh9m.js → components-Djrujufg.js} +2 -2
  39. package/dist/connect/assets/{components-HCKyLh9m.js.map → components-Djrujufg.js.map} +1 -1
  40. package/dist/connect/assets/{connect-pRAhId57.js → connect-CGvtDtZh.js} +2 -2
  41. package/dist/connect/assets/{connect-pRAhId57.js.map → connect-CGvtDtZh.js.map} +1 -1
  42. package/dist/connect/assets/{connect.config-Bt4pvvWV-CAyer5pZ.js → connect.config-Bt4pvvWV-Dj3FkjaZ.js} +2 -2
  43. package/dist/connect/assets/{connect.config-Bt4pvvWV-CAyer5pZ.js.map → connect.config-Bt4pvvWV-Dj3FkjaZ.js.map} +1 -1
  44. package/dist/connect/assets/{dist-DmhUSVtT.js → dist-t-ezfpxv.js} +3 -3
  45. package/dist/connect/assets/{dist-DmhUSVtT.js.map → dist-t-ezfpxv.js.map} +1 -1
  46. package/dist/connect/assets/{editor-B560eld_-CIE3_3II.js → editor-B560eld_-DLpKHubk.js} +2 -2
  47. package/dist/connect/assets/{editor-B560eld_-CIE3_3II.js.map → editor-B560eld_-DLpKHubk.js.map} +1 -1
  48. package/dist/connect/assets/editor-BaQ62r3i.js +2 -0
  49. package/dist/connect/assets/editor-BaQ62r3i.js.map +1 -0
  50. package/dist/connect/assets/{editor-CAVHtR-v-ClzvSfK5.js → editor-CAVHtR-v-D33T9vW0.js} +2 -2
  51. package/dist/connect/assets/{editor-CAVHtR-v-ClzvSfK5.js.map → editor-CAVHtR-v-D33T9vW0.js.map} +1 -1
  52. package/dist/connect/assets/{editor-CVCLxUhe-C6nP7EPN.js → editor-CVCLxUhe-C9Ftgu1S.js} +2 -2
  53. package/dist/connect/assets/{editor-CVCLxUhe-C6nP7EPN.js.map → editor-CVCLxUhe-C9Ftgu1S.js.map} +1 -1
  54. package/dist/connect/assets/{editor-DnG2Zzet-CRhirYbH.js → editor-DnG2Zzet-CQ38uSti.js} +2 -2
  55. package/dist/connect/assets/{editor-DnG2Zzet-CRhirYbH.js.map → editor-DnG2Zzet-CQ38uSti.js.map} +1 -1
  56. package/dist/connect/assets/{editor-mor7dV6n-DDigdbLM.js → editor-mor7dV6n-NTU9k1uj.js} +2 -2
  57. package/dist/connect/assets/{editor-mor7dV6n-DDigdbLM.js.map → editor-mor7dV6n-NTU9k1uj.js.map} +1 -1
  58. package/dist/connect/assets/{editor-xCnDas6c-CyHuSL7M.js → editor-xCnDas6c-DdGhx--5.js} +2 -2
  59. package/dist/connect/assets/{editor-xCnDas6c-CyHuSL7M.js.map → editor-xCnDas6c-DdGhx--5.js.map} +1 -1
  60. package/dist/connect/assets/esm-dD38MHAT.js +10 -0
  61. package/dist/connect/assets/esm-dD38MHAT.js.map +1 -0
  62. package/dist/connect/assets/{hooks-Btogj1f0-CMXQZV6d.js → hooks-Btogj1f0-BKAAmyt-.js} +2 -2
  63. package/dist/connect/assets/{hooks-Btogj1f0-CMXQZV6d.js.map → hooks-Btogj1f0-BKAAmyt-.js.map} +1 -1
  64. package/dist/connect/assets/index-NB86TKkT.js +3 -0
  65. package/dist/connect/assets/{index-DS7vbdaR.js.map → index-NB86TKkT.js.map} +1 -1
  66. package/dist/connect/assets/{load-BWibkoFV-BVilFVpD.js → load-BWibkoFV-CDRjWwYQ.js} +2 -2
  67. package/dist/connect/assets/{load-BWibkoFV-BVilFVpD.js.map → load-BWibkoFV-CDRjWwYQ.js.map} +1 -1
  68. package/dist/connect/assets/module-1hr_ne9w-CWAVsMzZ.js.map +1 -1
  69. package/dist/connect/assets/{module-DP3LMNU7.js → module-7zCQE1MC.js} +2 -2
  70. package/dist/connect/assets/{module-DP3LMNU7.js.map → module-7zCQE1MC.js.map} +1 -1
  71. package/dist/connect/assets/module-CRq_lUkG-D61oeRlC.js.map +1 -1
  72. package/dist/connect/assets/module-DP3bVbak-VbUPIBZP.js.map +1 -1
  73. package/dist/connect/assets/package-BZLLR0Ma-B7vswMvI.js.map +1 -1
  74. package/dist/connect/assets/{pglite-seed-BWk7yOwr-T-NGWlGh.js → pglite-seed-BWk7yOwr-b2F0oJiP.js} +2 -2
  75. package/dist/connect/assets/{pglite-seed-BWk7yOwr-T-NGWlGh.js.map → pglite-seed-BWk7yOwr-b2F0oJiP.js.map} +1 -1
  76. package/dist/connect/assets/pglite.worker-BuyZ0d7u.js.map +1 -1
  77. package/dist/connect/assets/pglite.worker.legacy-D5InEf0i.js.map +1 -1
  78. package/dist/connect/assets/{ph-clint-project-BZ5X7dVQ.js → ph-clint-project-p1zY6e17.js} +2 -2
  79. package/dist/connect/assets/{ph-clint-project-BZ5X7dVQ.js.map → ph-clint-project-p1zY6e17.js.map} +1 -1
  80. package/dist/connect/assets/{reactor-BxNvHOKC-DmVBqCt_.js → reactor-BxNvHOKC-CeMF1M2u.js} +4 -4
  81. package/dist/connect/assets/{reactor-BxNvHOKC-DmVBqCt_.js.map → reactor-BxNvHOKC-CeMF1M2u.js.map} +1 -1
  82. package/dist/connect/assets/{registerServiceWorker-D9l1kja7-oC4nicB2.js → registerServiceWorker-D9l1kja7-DG3DqQna.js} +2 -2
  83. package/dist/connect/assets/{registerServiceWorker-D9l1kja7-oC4nicB2.js.map → registerServiceWorker-D9l1kja7-DG3DqQna.js.map} +1 -1
  84. package/dist/connect/assets/reload-connect-toast-CwvSc6F--BQcqGaN_.js.map +1 -1
  85. package/dist/connect/assets/runtime-config-DL3QjcfM-D9g4VFtY.js.map +1 -1
  86. package/dist/connect/assets/toast-BEy42aYx-Dg_jZf6v.js.map +1 -1
  87. package/dist/connect/assets/{useRegistryPackages-B-gFECmd-CXOBKziY.js → useRegistryPackages-B-gFECmd-C8h2M5qM.js} +2 -2
  88. package/dist/connect/assets/{useRegistryPackages-B-gFECmd-CXOBKziY.js.map → useRegistryPackages-B-gFECmd-C8h2M5qM.js.map} +1 -1
  89. package/dist/connect/assets/{useVetraDocument-tpjMF571-DT5T1mNT.js → useVetraDocument-tpjMF571-DxT9GgCx.js} +2 -2
  90. package/dist/connect/assets/{useVetraDocument-tpjMF571-DT5T1mNT.js.map → useVetraDocument-tpjMF571-DxT9GgCx.js.map} +1 -1
  91. package/dist/connect/assets/utils-BiV3cs9Y-B7i9WJ1b.js.map +1 -1
  92. package/dist/connect/assets/{utils-CkWB3DRk-CxqNKFWP.js → utils-CkWB3DRk-BURJYofr.js} +2 -2
  93. package/dist/connect/assets/{utils-CkWB3DRk-CxqNKFWP.js.map → utils-CkWB3DRk-BURJYofr.js.map} +1 -1
  94. package/dist/connect/index.html +3 -3
  95. package/dist/connect/powerhouse.config.json +1 -1
  96. package/dist/node/{editor-Bev0bZuJ.mjs → editor-BC9t7D3T.mjs} +748 -517
  97. package/dist/node/editor-BC9t7D3T.mjs.map +1 -0
  98. package/dist/node/editors/ph-clint-project-editor/module.mjs +1 -1
  99. package/package.json +2 -2
  100. package/dist/browser/editor-BuHwuzRa.js.map +0 -1
  101. package/dist/connect/assets/editor-BrPMvNFl.js +0 -2
  102. package/dist/connect/assets/editor-BrPMvNFl.js.map +0 -1
  103. package/dist/connect/assets/esm-mbrYcWED.js +0 -10
  104. package/dist/connect/assets/esm-mbrYcWED.js.map +0 -1
  105. package/dist/connect/assets/index-DS7vbdaR.js +0 -3
  106. package/dist/node/editor-Bev0bZuJ.mjs.map +0 -1
@@ -2,11 +2,6 @@ import { Ar as generateId, _r as undo, g as actions, jr as __commonJSMin, mr as
2
2
  import { C as useDocumentById, S as t$7, _ as usePHToast, b as e$5, c as DEFAULT_SWITCHBOARD_URL, d as showRevisionHistory, f as useDownloadDocument, g as useNodeParentFolderById, h as useNodeById, l as getSwitchboardGatewayUrlFromDriveUrl, m as useNodeActions, p as useGetSwitchboardLink, s as useSelectedPhClintProjectDocument, u as setSelectedNode, v as useSelectedDocumentSafe, x as t$8, y as useSelectedDrive } from "./ph-clint-project-CHq59BKL.js";
3
3
  import { Fragment, Suspense, createContext, createElement, forwardRef, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
4
4
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
5
- import "node:crypto";
6
- import "node:path";
7
- import "node:fs/promises";
8
- import "node:fs";
9
- import "node:stream";
10
5
  //#region ../../node_modules/.pnpm/remeda@2.33.7/node_modules/remeda/dist/defaultTo.js
11
6
  function t$6(...t) {
12
7
  return t$7(n$4, t);
@@ -128,6 +123,521 @@ function t(...t) {
128
123
  return t$7(Object.values, t);
129
124
  }
130
125
  //#endregion
126
+ //#region ../../node_modules/.pnpm/@powerhousedao+reactor-attachments@6.2.0-dev.23_@sentry+node-core@10.58.0_@opentelemetr_52094b0a5e5faf97617f2bc1ad7b3eb6/node_modules/@powerhousedao/reactor-attachments/dist/null-attachment-transport-Drx03s02.js
127
+ /**
128
+ * Thrown when an attachment ref or hash is not known to the store.
129
+ */
130
+ var AttachmentNotFound = class extends Error {
131
+ constructor(identifier) {
132
+ super(`Attachment not found: ${identifier}`);
133
+ this.name = "AttachmentNotFound";
134
+ }
135
+ };
136
+ /**
137
+ * Thrown when a reservation ID is not found in the reservation store.
138
+ */
139
+ var ReservationNotFound = class extends Error {
140
+ constructor(reservationId) {
141
+ super(`Reservation not found: ${reservationId}`);
142
+ this.name = "ReservationNotFound";
143
+ }
144
+ };
145
+ /**
146
+ * Thrown when an attachment ref string does not match the expected format.
147
+ */
148
+ var InvalidAttachmentRef = class extends Error {
149
+ constructor(ref) {
150
+ super(`Invalid attachment ref: ${ref}`);
151
+ this.name = "InvalidAttachmentRef";
152
+ }
153
+ };
154
+ /**
155
+ * Thrown by reserve() when the claimed hash is already available in the store.
156
+ * The caller should use err.ref directly and upload nothing -- this is the
157
+ * dedup fast path: duplicate content never leaves the client.
158
+ */
159
+ var AttachmentAlreadyExists = class extends Error {
160
+ hash;
161
+ ref;
162
+ constructor(hash, ref) {
163
+ super(`Attachment already exists for hash: ${hash}`);
164
+ this.name = "AttachmentAlreadyExists";
165
+ this.hash = hash;
166
+ this.ref = ref;
167
+ }
168
+ };
169
+ /**
170
+ * Thrown by send() when the server-computed hash of the uploaded bytes
171
+ * does not match the hash claimed at reservation time. Nothing is committed;
172
+ * the reservation is retained so the client can retry with correct bytes.
173
+ */
174
+ var HashMismatch = class extends Error {
175
+ claimed;
176
+ actual;
177
+ constructor(claimed, actual) {
178
+ super(`Hash mismatch: claimed ${claimed} but computed ${actual}`);
179
+ this.name = "HashMismatch";
180
+ this.claimed = claimed;
181
+ this.actual = actual;
182
+ }
183
+ };
184
+ /**
185
+ * Thrown by send() when the uploaded byte count does not equal the
186
+ * sizeBytes declared at reservation time. The handle may reject
187
+ * mid-stream as soon as the count exceeds the declaration.
188
+ * Nothing is committed; the reservation is retained for retry.
189
+ *
190
+ * "actual" is the byte count received from the stream before aborting --
191
+ * it includes the chunk that crossed the declaration and can exceed bytes
192
+ * persisted. On mid-stream aborts the true total is unknown; at least
193
+ * "actual" bytes were sent.
194
+ */
195
+ var SizeMismatch = class extends Error {
196
+ declared;
197
+ actual;
198
+ constructor(declared, actual) {
199
+ super(`Size mismatch: declared ${declared} bytes but received ${actual}`);
200
+ this.name = "SizeMismatch";
201
+ this.declared = declared;
202
+ this.actual = actual;
203
+ }
204
+ };
205
+ /**
206
+ * Thrown by get() when the hash is reserved by an in-flight upload and
207
+ * bytes are not yet available anywhere. Deliberately NOT a subclass of
208
+ * AttachmentNotFound -- callers must distinguish "retry later" from "unknown".
209
+ * After expiresAtUtc has passed the hash reads as not found.
210
+ *
211
+ * metadata is populated when the reservation is local and its fields are
212
+ * known (mimeType, fileName, sizeBytes). It is undefined when the pending
213
+ * state is learned from a remote transport that did not supply the full
214
+ * Attachment-Pending header (transport-pending / degraded wire case).
215
+ */
216
+ var AttachmentPending = class extends Error {
217
+ hash;
218
+ expiresAtUtc;
219
+ metadata;
220
+ constructor(hash, expiresAtUtc, meta) {
221
+ super(`Attachment pending upload for hash: ${hash}, expires: ${expiresAtUtc}`);
222
+ this.name = "AttachmentPending";
223
+ this.hash = hash;
224
+ this.expiresAtUtc = expiresAtUtc;
225
+ this.metadata = meta;
226
+ }
227
+ };
228
+ const REF_PATTERN = /^attachment:\/\/v(\d+):(.+)$/;
229
+ const DEFAULT_VERSION = 1;
230
+ function parseRef(ref) {
231
+ const match = REF_PATTERN.exec(ref);
232
+ if (!match) throw new InvalidAttachmentRef(ref);
233
+ return {
234
+ version: Number(match[1]),
235
+ hash: match[2]
236
+ };
237
+ }
238
+ function createRef(hash, version = DEFAULT_VERSION) {
239
+ return `attachment://v${version}:${hash}`;
240
+ }
241
+ const CLIENT_HASH_PATTERN = /^[a-f0-9]{64}$/;
242
+ var AttachmentService = class {
243
+ constructor(store, reservations, uploadFactory) {
244
+ this.store = store;
245
+ this.reservations = reservations;
246
+ this.uploadFactory = uploadFactory;
247
+ }
248
+ async reserve(options) {
249
+ if (options.clientHash !== void 0) return this.reserveHashFirst(options);
250
+ const reservation = await this.reservations.create(options);
251
+ return this.uploadFactory.createUpload(reservation);
252
+ }
253
+ async stat(ref) {
254
+ const { hash } = parseRef(ref);
255
+ return this.store.stat(hash);
256
+ }
257
+ async get(ref, signal) {
258
+ const { hash } = parseRef(ref);
259
+ return this.store.get(hash, signal);
260
+ }
261
+ async reserveHashFirst(options) {
262
+ const normalized = options.clientHash.toLowerCase();
263
+ if (!CLIENT_HASH_PATTERN.test(normalized)) throw new Error(`clientHash must be a 64-character lowercase hex string, got: ${options.clientHash}`);
264
+ if (options.sizeBytes === void 0 || !Number.isInteger(options.sizeBytes) || options.sizeBytes <= 0 || !Number.isSafeInteger(options.sizeBytes)) throw new Error("sizeBytes must be a positive safe integer when clientHash is provided");
265
+ const normalizedOptions = {
266
+ ...options,
267
+ clientHash: normalized
268
+ };
269
+ let existingHeader = null;
270
+ try {
271
+ existingHeader = await this.store.stat(normalized);
272
+ } catch (err) {
273
+ if (!(err instanceof AttachmentNotFound) && !(err instanceof AttachmentPending)) throw err;
274
+ }
275
+ if (existingHeader !== null && existingHeader.status === "available") throw new AttachmentAlreadyExists(normalized, createRef(normalized));
276
+ const reservation = await this.reservations.create(normalizedOptions);
277
+ return this.uploadFactory.createUpload(reservation);
278
+ }
279
+ };
280
+ async function buildAuthHeaders(url, jwtHandler) {
281
+ const headers = {};
282
+ if (jwtHandler) {
283
+ const token = await jwtHandler(url);
284
+ if (token) headers["Authorization"] = `Bearer ${token}`;
285
+ }
286
+ return headers;
287
+ }
288
+ function isRecord$2(value) {
289
+ return typeof value === "object" && value !== null && !Array.isArray(value);
290
+ }
291
+ function deriveExtension(fileName) {
292
+ const idx = fileName.lastIndexOf(".");
293
+ if (idx <= 0 || idx === fileName.length - 1) return null;
294
+ return fileName.slice(idx + 1).toLowerCase();
295
+ }
296
+ function isReservationBase(value) {
297
+ if (!isRecord$2(value)) return false;
298
+ if (typeof value.reservationId !== "string") return false;
299
+ if (typeof value.mimeType !== "string") return false;
300
+ if (typeof value.fileName !== "string") return false;
301
+ if (value.extension !== null && typeof value.extension !== "string") return false;
302
+ if (typeof value.createdAtUtc !== "string") return false;
303
+ if (typeof value.expiresAtUtc !== "string") return false;
304
+ return true;
305
+ }
306
+ function isReservation(value) {
307
+ if (!isReservationBase(value)) return false;
308
+ if (value.clientHash !== void 0 && value.clientHash !== null && typeof value.clientHash !== "string") return false;
309
+ if (value.sizeBytes !== void 0 && value.sizeBytes !== null && typeof value.sizeBytes !== "number") return false;
310
+ return true;
311
+ }
312
+ var RemoteReservationStore = class {
313
+ remoteUrl;
314
+ jwtHandler;
315
+ fetchFn;
316
+ constructor(config) {
317
+ this.remoteUrl = config.remoteUrl;
318
+ this.jwtHandler = config.jwtHandler;
319
+ this.fetchFn = (config.fetchFn ?? globalThis.fetch).bind(globalThis);
320
+ }
321
+ async create(options) {
322
+ const url = `${this.remoteUrl}/attachments/reservations`;
323
+ const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
324
+ const extension = options.extension ?? deriveExtension(options.fileName);
325
+ const bodyObj = {
326
+ mimeType: options.mimeType,
327
+ fileName: options.fileName,
328
+ extension
329
+ };
330
+ if (options.clientHash !== void 0) bodyObj.clientHash = options.clientHash;
331
+ if (options.sizeBytes !== void 0) bodyObj.sizeBytes = options.sizeBytes;
332
+ const response = await this.fetchFn(url, {
333
+ method: "POST",
334
+ headers: {
335
+ ...authHeaders,
336
+ "Content-Type": "application/json"
337
+ },
338
+ body: JSON.stringify(bodyObj)
339
+ });
340
+ if (response.status === 409) {
341
+ let body;
342
+ try {
343
+ body = await response.json();
344
+ } catch {
345
+ throw new Error(`Reservation create failed: ${response.status} ${response.statusText}`);
346
+ }
347
+ if (isRecord$2(body) && body.error === "already_exists" && options.clientHash !== void 0) {
348
+ let ref;
349
+ if (typeof body.ref === "string") try {
350
+ parseRef(body.ref);
351
+ ref = body.ref;
352
+ } catch {
353
+ ref = createRef(options.clientHash);
354
+ }
355
+ else ref = createRef(options.clientHash);
356
+ throw new AttachmentAlreadyExists(options.clientHash, ref);
357
+ }
358
+ throw new Error(`Reservation create failed: ${response.status} ${response.statusText}`);
359
+ }
360
+ if (!response.ok) throw new Error(`Reservation create failed: ${response.status} ${response.statusText}`);
361
+ let json;
362
+ try {
363
+ json = await response.json();
364
+ } catch {
365
+ throw new Error("Reservation create returned non-JSON response");
366
+ }
367
+ if (typeof json !== "object" || json === null || typeof json.reservationId !== "string" || json.reservationId.length === 0) throw new Error("Reservation create returned a payload missing a non-empty reservationId string");
368
+ const body = json;
369
+ const now = /* @__PURE__ */ new Date();
370
+ return {
371
+ reservationId: body.reservationId,
372
+ mimeType: options.mimeType,
373
+ fileName: options.fileName,
374
+ extension,
375
+ createdAtUtc: body.createdAtUtc ?? now.toISOString(),
376
+ expiresAtUtc: body.expiresAtUtc ?? new Date(now.getTime() + 1440 * 60 * 1e3).toISOString(),
377
+ clientHash: options.clientHash ?? null,
378
+ sizeBytes: options.sizeBytes ?? null
379
+ };
380
+ }
381
+ async get(reservationId) {
382
+ const url = `${this.remoteUrl}/attachments/reservations/${encodeURIComponent(reservationId)}`;
383
+ const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
384
+ const response = await this.fetchFn(url, { headers: authHeaders });
385
+ if (response.status === 404) throw new ReservationNotFound(reservationId);
386
+ if (!response.ok) throw new Error(`Reservation get failed: ${response.status} ${response.statusText}`);
387
+ let parsed;
388
+ try {
389
+ parsed = await response.json();
390
+ } catch {
391
+ throw new Error("Reservation get returned non-JSON response");
392
+ }
393
+ if (!isReservation(parsed)) throw new Error("Reservation get returned a payload that does not match the Reservation shape");
394
+ return {
395
+ reservationId: parsed.reservationId,
396
+ mimeType: parsed.mimeType,
397
+ fileName: parsed.fileName,
398
+ extension: parsed.extension,
399
+ createdAtUtc: parsed.createdAtUtc,
400
+ expiresAtUtc: parsed.expiresAtUtc,
401
+ clientHash: parsed.clientHash ?? null,
402
+ sizeBytes: parsed.sizeBytes ?? null
403
+ };
404
+ }
405
+ async delete(reservationId) {
406
+ const url = `${this.remoteUrl}/attachments/reservations/${encodeURIComponent(reservationId)}`;
407
+ const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
408
+ const response = await this.fetchFn(url, {
409
+ method: "DELETE",
410
+ headers: authHeaders
411
+ });
412
+ if (!response.ok && response.status !== 404 && response.status !== 410) throw new Error(`Reservation delete failed: ${response.status} ${response.statusText}`);
413
+ }
414
+ deleteExpired() {
415
+ return Promise.reject(/* @__PURE__ */ new Error("RemoteReservationStore.deleteExpired is not supported"));
416
+ }
417
+ };
418
+ function isRecord$1(value) {
419
+ return typeof value === "object" && value !== null && !Array.isArray(value);
420
+ }
421
+ var RemoteAttachmentUpload = class {
422
+ reservationId;
423
+ ref;
424
+ expiresAtUtc;
425
+ remoteUrl;
426
+ jwtHandler;
427
+ fetchFn;
428
+ constructor(reservation, config) {
429
+ this.reservationId = reservation.reservationId;
430
+ this.ref = reservation.clientHash !== null ? createRef(reservation.clientHash) : null;
431
+ this.expiresAtUtc = reservation.expiresAtUtc;
432
+ this.remoteUrl = config.remoteUrl;
433
+ this.jwtHandler = config.jwtHandler;
434
+ this.fetchFn = (config.fetchFn ?? globalThis.fetch).bind(globalThis);
435
+ }
436
+ async send(data) {
437
+ const url = `${this.remoteUrl}/attachments/reservations/${this.reservationId}`;
438
+ const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
439
+ const body = await new Response(data).blob();
440
+ const response = await this.fetchFn(url, {
441
+ method: "PUT",
442
+ headers: {
443
+ ...authHeaders,
444
+ "Content-Type": "application/octet-stream"
445
+ },
446
+ body
447
+ });
448
+ if (response.status === 422) {
449
+ let errorBody;
450
+ try {
451
+ errorBody = await response.json();
452
+ } catch {
453
+ throw new Error(`Attachment upload failed: ${response.status} ${response.statusText}`);
454
+ }
455
+ if (isRecord$1(errorBody)) {
456
+ if (errorBody.error === "hash_mismatch" && typeof errorBody.claimed === "string" && typeof errorBody.actual === "string") throw new HashMismatch(errorBody.claimed, errorBody.actual);
457
+ if (errorBody.error === "size_mismatch" && typeof errorBody.declared === "number" && typeof errorBody.actual === "number") throw new SizeMismatch(errorBody.declared, errorBody.actual);
458
+ }
459
+ throw new Error(`Attachment upload failed: ${response.status} ${response.statusText}`);
460
+ }
461
+ if (!response.ok) throw new Error(`Attachment upload failed: ${response.status} ${response.statusText}`);
462
+ return await response.json();
463
+ }
464
+ };
465
+ var RemoteAttachmentUploadFactory = class {
466
+ constructor(config) {
467
+ this.config = config;
468
+ }
469
+ createUpload(reservation) {
470
+ return new RemoteAttachmentUpload(reservation, this.config);
471
+ }
472
+ };
473
+ function isRecord(value) {
474
+ return typeof value === "object" && value !== null && !Array.isArray(value);
475
+ }
476
+ function isAttachmentMetadata(value) {
477
+ if (!isRecord(value)) return false;
478
+ if (typeof value.mimeType !== "string") return false;
479
+ if (typeof value.fileName !== "string") return false;
480
+ if (typeof value.sizeBytes !== "number" || !Number.isFinite(value.sizeBytes) || value.sizeBytes < 0) return false;
481
+ if (value.extension !== null && typeof value.extension !== "string") return false;
482
+ if (typeof value.createdAtUtc !== "string") return false;
483
+ if (value.lastAccessedAtUtc !== void 0 && typeof value.lastAccessedAtUtc !== "string") return false;
484
+ return true;
485
+ }
486
+ function contentTypeFallback(response) {
487
+ const contentLength = response.headers.get("Content-Length");
488
+ if (contentLength === null) throw new Error("Switchboard response missing both Attachment-Metadata and Content-Length headers");
489
+ const sizeBytes = Number(contentLength);
490
+ if (!Number.isInteger(sizeBytes) || sizeBytes < 0) throw new Error(`Switchboard response has invalid Content-Length header: ${JSON.stringify(contentLength)}`);
491
+ const lastModified = response.headers.get("Last-Modified");
492
+ const dateHeader = response.headers.get("Date");
493
+ const createdAtUtc = lastModified ? new Date(lastModified).toISOString() : dateHeader ? new Date(dateHeader).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
494
+ return {
495
+ mimeType: response.headers.get("Content-Type") ?? "application/octet-stream",
496
+ fileName: "unknown",
497
+ sizeBytes,
498
+ extension: null,
499
+ createdAtUtc,
500
+ lastAccessedAtUtc: createdAtUtc
501
+ };
502
+ }
503
+ function parseMetadata(response) {
504
+ let fallbackCache;
505
+ const fallback = () => {
506
+ if (fallbackCache === void 0) fallbackCache = contentTypeFallback(response);
507
+ return fallbackCache;
508
+ };
509
+ const metaHeader = response.headers.get("Attachment-Metadata");
510
+ if (metaHeader) try {
511
+ const parsed = JSON.parse(metaHeader);
512
+ if (isRecord(parsed)) {
513
+ if (parsed.extension === void 0) parsed.extension = null;
514
+ if (parsed.createdAtUtc === void 0) parsed.createdAtUtc = fallback().createdAtUtc;
515
+ if (parsed.lastAccessedAtUtc === void 0) parsed.lastAccessedAtUtc = fallback().lastAccessedAtUtc;
516
+ }
517
+ if (isAttachmentMetadata(parsed)) return parsed;
518
+ } catch {}
519
+ return fallback();
520
+ }
521
+ function parsePendingExpiry(response) {
522
+ const header = response.headers.get("Attachment-Pending");
523
+ if (!header) return null;
524
+ try {
525
+ const parsed = JSON.parse(header);
526
+ if (!isRecord(parsed)) return null;
527
+ if (typeof parsed.expiresAtUtc !== "string") return null;
528
+ const result = { expiresAtUtc: parsed.expiresAtUtc };
529
+ if (typeof parsed.mimeType === "string") result.mimeType = parsed.mimeType;
530
+ if (typeof parsed.fileName === "string") result.fileName = parsed.fileName;
531
+ if (typeof parsed.sizeBytes === "number" && Number.isFinite(parsed.sizeBytes) && parsed.sizeBytes >= 0) result.sizeBytes = parsed.sizeBytes;
532
+ return result;
533
+ } catch {
534
+ return null;
535
+ }
536
+ }
537
+ function parsePendingHeader(response) {
538
+ const partial = parsePendingExpiry(response);
539
+ if (!partial) return null;
540
+ if (typeof partial.mimeType !== "string" || typeof partial.fileName !== "string" || partial.sizeBytes === void 0) return null;
541
+ return {
542
+ expiresAtUtc: partial.expiresAtUtc,
543
+ mimeType: partial.mimeType,
544
+ fileName: partial.fileName,
545
+ sizeBytes: partial.sizeBytes
546
+ };
547
+ }
548
+ var RemoteAttachmentStore = class {
549
+ remoteUrl;
550
+ jwtHandler;
551
+ fetchFn;
552
+ constructor(config) {
553
+ this.remoteUrl = config.remoteUrl;
554
+ this.jwtHandler = config.jwtHandler;
555
+ this.fetchFn = (config.fetchFn ?? globalThis.fetch).bind(globalThis);
556
+ }
557
+ /**
558
+ * Get attachment metadata. Normally returns a pending AttachmentHeader
559
+ * (status: 'pending') when the server responds 202 with a full
560
+ * Attachment-Pending header. When only expiresAtUtc is present in the
561
+ * header (degraded wire), throws AttachmentPending instead -- the
562
+ * AttachmentPending throw is the degraded-wire case.
563
+ */
564
+ async stat(hash) {
565
+ const url = `${this.remoteUrl}/attachments/${hash}`;
566
+ const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
567
+ const response = await this.fetchFn(url, {
568
+ method: "HEAD",
569
+ headers: authHeaders
570
+ });
571
+ if (response.status === 202) {
572
+ const fullPending = parsePendingHeader(response);
573
+ if (fullPending) return buildPendingHeader(hash, fullPending);
574
+ const partial = parsePendingExpiry(response);
575
+ if (partial) throw new AttachmentPending(hash, partial.expiresAtUtc);
576
+ throw new Error("Attachment stat returned 202 with missing or malformed Attachment-Pending header");
577
+ }
578
+ if (response.status === 404) throw new AttachmentNotFound(hash);
579
+ if (!response.ok) throw new Error(`Attachment stat failed: ${response.status} ${response.statusText}`);
580
+ return buildHeader(hash, parseMetadata(response));
581
+ }
582
+ async get(hash, signal) {
583
+ return this.fetchAttachment(hash, signal);
584
+ }
585
+ async fetchAttachment(hash, signal) {
586
+ const url = `${this.remoteUrl}/attachments/${hash}`;
587
+ const headers = await buildAuthHeaders(url, this.jwtHandler);
588
+ const response = await this.fetchFn(url, {
589
+ signal,
590
+ headers
591
+ });
592
+ if (response.status === 202) {
593
+ const pending = parsePendingExpiry(response);
594
+ if (!pending) throw new Error("Attachment fetch returned 202 with missing or malformed Attachment-Pending header");
595
+ throw new AttachmentPending(hash, pending.expiresAtUtc);
596
+ }
597
+ if (response.status === 404) throw new AttachmentNotFound(hash);
598
+ if (!response.ok) throw new Error(`Attachment fetch failed: ${response.status} ${response.statusText}`);
599
+ if (!response.body) throw new Error("Response body is null");
600
+ return {
601
+ header: buildHeader(hash, parseMetadata(response)),
602
+ body: response.body
603
+ };
604
+ }
605
+ };
606
+ function buildHeader(hash, metadata) {
607
+ return {
608
+ hash,
609
+ mimeType: metadata.mimeType,
610
+ fileName: metadata.fileName,
611
+ sizeBytes: metadata.sizeBytes,
612
+ extension: metadata.extension,
613
+ status: "available",
614
+ source: "sync",
615
+ createdAtUtc: metadata.createdAtUtc,
616
+ lastAccessedAtUtc: metadata.lastAccessedAtUtc ?? metadata.createdAtUtc,
617
+ expiresAtUtc: null
618
+ };
619
+ }
620
+ function buildPendingHeader(hash, pending) {
621
+ const now = (/* @__PURE__ */ new Date()).toISOString();
622
+ return {
623
+ hash,
624
+ mimeType: pending.mimeType,
625
+ fileName: pending.fileName,
626
+ sizeBytes: pending.sizeBytes,
627
+ extension: null,
628
+ status: "pending",
629
+ source: "sync",
630
+ createdAtUtc: now,
631
+ lastAccessedAtUtc: now,
632
+ expiresAtUtc: pending.expiresAtUtc
633
+ };
634
+ }
635
+ function createRemoteAttachmentService(config) {
636
+ const reservations = new RemoteReservationStore(config);
637
+ const uploadFactory = new RemoteAttachmentUploadFactory(config);
638
+ return new AttachmentService(new RemoteAttachmentStore(config), reservations, uploadFactory);
639
+ }
640
+ //#endregion
131
641
  //#region ../../node_modules/.pnpm/@powerhousedao+design-system@6.2.0-dev.23_@babel+runtime@7.29.2_@electric-sql+pglite@0._b451e295ed096f1dc466b71d0cbd40f8/node_modules/@powerhousedao/design-system/dist/Removed-CO_5C1ce.js
132
642
  function Created(props) {
133
643
  return /* @__PURE__ */ jsxs("svg", {
@@ -7838,542 +8348,258 @@ function ToolbarCloseButton(props) {
7838
8348
  });
7839
8349
  }
7840
8350
  /**
7841
- * Creates a toolbar button click handler.
7842
- *
7843
- * If an override is provided, it is called with the current document. Otherwise,
7844
- * the built-in handler is called with the current document.
7845
- */
7846
- function makeOnClick(document, onClickOverride, defaultOnClick) {
7847
- if (e$5(onClickOverride)) return () => onClickOverride(document);
7848
- return () => defaultOnClick(document);
7849
- }
7850
- /**
7851
- * Text input styled for use inside a toolbar.
7852
- *
7853
- * This wraps `NodeInput` with toolbar-specific text styling. Use it for inline
7854
- * toolbar editing flows where the user can submit a value or cancel editing.
7855
- */
7856
- function ToolbarInput(props) {
7857
- const { defaultValue, className, onSubmit, onCancel, "aria-label": ariaLabel } = props;
7858
- return /* @__PURE__ */ jsx(NodeInput, {
7859
- defaultValue,
7860
- className: twMerge("text-center text-sm font-medium text-muted-foreground", className),
7861
- "aria-label": ariaLabel,
7862
- onCancel,
7863
- onSubmit
7864
- });
7865
- }
7866
- /**
7867
- * Toolbar control for displaying and renaming the current document.
7868
- *
7869
- * By default, the component renders the document name as a clickable heading.
7870
- * When clicked, it switches to an inline input. Submitting the input renames
7871
- * both the node and the corresponding drive node entry.
7872
- */
7873
- function ToolbarName(props) {
7874
- const { document, inputClassName, titleClassName } = props;
7875
- const [isEditing, setIsEditing] = useState(false);
7876
- const node = useNodeById(document?.header.id);
7877
- const { onRenameNode, onRenameDriveNodes } = useNodeActions();
7878
- const documentName = document?.header.name;
7879
- const documentId = document?.header.id;
7880
- const activateEditing = () => setIsEditing(true);
7881
- const cancelEditing = () => setIsEditing(false);
7882
- const onSubmit = (newName) => {
7883
- cancelEditing();
7884
- if (!documentId || !node) return;
7885
- Promise.all([onRenameNode(newName, node), onRenameDriveNodes(newName, documentId)]).catch(console.error);
7886
- };
7887
- if (!documentName) return null;
7888
- if (isEditing) return /* @__PURE__ */ jsx(ToolbarInput, {
7889
- className: inputClassName,
7890
- onSubmit,
7891
- onCancel: cancelEditing,
7892
- defaultValue: documentName,
7893
- "aria-label": "Document name"
7894
- });
7895
- return /* @__PURE__ */ jsx("h1", {
7896
- className: twMerge("cursor-pointer text-sm font-medium text-muted-foreground hover:hover-effect", titleClassName),
7897
- onClick: activateEditing,
7898
- title: "Click to edit",
7899
- children: documentName
7900
- });
7901
- }
7902
- /**
7903
- * Default slot layout for the built-in document toolbar controls.
7904
- *
7905
- * The toolbar is divided into three control groups:
7906
- *
7907
- * - `first`: primary document actions.
7908
- * - `second`: document identity/display controls.
7909
- * - `third`: secondary document actions.
7910
- */
7911
- const defaultControlSlots = {
7912
- first: [
7913
- "undo",
7914
- "redo",
7915
- "download"
7916
- ],
7917
- second: ["name"],
7918
- third: [
7919
- "history",
7920
- "switchboard",
7921
- "close"
7922
- ]
7923
- };
7924
- /**
7925
- * Ordered list of toolbar slot names.
7926
- */
7927
- const controlSlots = t$3(defaultControlSlots);
7928
- /**
7929
- * Ordered list of all built-in document toolbar control names.
7930
- *
7931
- * The order is derived from `defaultControlSlots`.
7932
- */
7933
- const documentToolbarControls = [
7934
- ...defaultControlSlots.first,
7935
- ...defaultControlSlots.second,
7936
- ...defaultControlSlots.third
7937
- ];
7938
- /**
7939
- * Default component implementation for each built-in toolbar control.
7940
- *
7941
- * These components are used unless a matching entry is provided through
7942
- * `componentOverrides`.
7943
- */
7944
- const defaultControlComponents = {
7945
- undo: ToolbarUndoButton,
7946
- redo: ToolbarRedoButton,
7947
- download: ToolbarDownloadButton,
7948
- name: ToolbarName,
7949
- switchboard: ToolbarSwitchboardButton,
7950
- history: ToolbarHistoryButton,
7951
- close: ToolbarCloseButton
7952
- };
7953
- /**
7954
- * Creates a predicate for checking whether a built-in toolbar control should render.
7955
- *
7956
- * A control renders when it is included in `enabledControls` and absent from
7957
- * `disabledControls`. When `enabledControls` is omitted, all built-in controls
7958
- * are considered enabled. When a control appears in both lists,
7959
- * `disabledControls` takes precedence.
7960
- */
7961
- function makeIsEnabledChecker(args) {
7962
- const { enabledControls = documentToolbarControls, disabledControls = [] } = args;
7963
- return (control) => e$2(control, enabledControls) && !e$2(control, disabledControls);
7964
- }
7965
- /**
7966
- * Creates a getter for rendering the built-in toolbar controls in a slot.
7967
- *
7968
- * The returned function resolves the controls assigned to a slot, filters them
7969
- * through the enabled/disabled control lists, applies any component overrides,
7970
- * and renders each control with the current document.
7971
- */
7972
- function makeToolbarControlsRenderer(args) {
7973
- const { document, enabledControls, disabledControls, componentOverrides } = args;
7974
- const checkIsEnabled = makeIsEnabledChecker({
7975
- enabledControls,
7976
- disabledControls
7977
- });
7978
- const renderComponent = (control) => t$4(e(componentOverrides, control), t$6(e(defaultControlComponents, control)), (Component) => /* @__PURE__ */ jsx(Component, { document }, control));
7979
- return (slot) => t$4(e(defaultControlSlots, slot), n$1(checkIsEnabled), t$2(renderComponent));
7980
- }
7981
- /**
7982
- * Checks whether a custom control should render in the requested position.
7983
- *
7984
- * Controls without an explicit position are treated as `"start"`.
7985
- */
7986
- function isControlInPosition(control, position) {
7987
- return t$6(control.position, "start") === position;
7988
- }
7989
- /**
7990
- * Creates a getter for rendering custom controls in a slot and position.
7991
- *
7992
- * The returned function resolves the custom control or controls assigned to a
7993
- * slot, then renders only the controls that belong in the requested position.
7994
- */
7995
- function makeCustomControlsRenderer(args) {
7996
- const { document, customControls = {} } = args;
7997
- return (slot, pos) => {
7998
- const controlOrControlList = e(customControls, slot);
7999
- if (!e$5(controlOrControlList)) return null;
8000
- if (e$3(controlOrControlList)) return renderCustomControlList(controlOrControlList, pos, document);
8001
- return renderCustomControl(controlOrControlList, pos, document);
8002
- };
8003
- }
8004
- /**
8005
- * Renders a single custom control when it belongs in the requested position.
8006
- */
8007
- function renderCustomControl(control, pos, document) {
8008
- if (!isControlInPosition(control, pos)) return null;
8009
- const Component = control.component;
8010
- return /* @__PURE__ */ jsx(Component, { document });
8011
- }
8012
- /**
8013
- * Renders a list of custom controls for the requested position.
8014
- *
8015
- * Returns `null` when no controls in the list belong in that position.
8016
- */
8017
- function renderCustomControlList(controls, pos, document) {
8018
- const controlsInPosition = n$1(controls, (control) => isControlInPosition(control, pos));
8019
- if (!t$8(controlsInPosition, 1)) return null;
8020
- return /* @__PURE__ */ jsx(Fragment$1, { children: t$2(controlsInPosition, ({ component: Component, key }) => /* @__PURE__ */ jsx(Component, { document }, key)) });
8021
- }
8022
- /**
8023
- * Renders a document toolbar.
8024
- *
8025
- * By default, the toolbar renders the built-in document controls grouped into
8026
- * toolbar slots. The controls operate on the provided `document`, or on the
8027
- * currently selected document when no document is provided.
8028
- *
8029
- * Use `enabledControls` and `disabledControls` to control which built-in
8030
- * controls are shown. Use `componentOverrides` to replace individual built-in
8031
- * controls while keeping the default toolbar layout. Use `customControls` to
8032
- * insert additional controls before or after the built-in controls in a slot.
8033
- *
8034
- * To take over the toolbar contents completely, pass `children`.
8035
- */
8036
- function DocumentToolbar(props) {
8037
- const [selectedDocument] = useSelectedDocumentSafe();
8038
- const { toolbarClassName, document = selectedDocument, toolbarContainer: Container = ToolbarContainer } = props;
8039
- if ("children" in props) return /* @__PURE__ */ jsx(Container, {
8040
- className: toolbarClassName,
8041
- children: props.children
8042
- });
8043
- return /* @__PURE__ */ jsx(Container, {
8044
- className: toolbarClassName,
8045
- children: t$2(controlSlots, (slot) => /* @__PURE__ */ createElement(ControlsContainerSlot, {
8046
- ...props,
8047
- document,
8048
- slot,
8049
- key: slot
8050
- }))
8051
- });
8351
+ * Creates a toolbar button click handler.
8352
+ *
8353
+ * If an override is provided, it is called with the current document. Otherwise,
8354
+ * the built-in handler is called with the current document.
8355
+ */
8356
+ function makeOnClick(document, onClickOverride, defaultOnClick) {
8357
+ if (e$5(onClickOverride)) return () => onClickOverride(document);
8358
+ return () => defaultOnClick(document);
8052
8359
  }
8053
8360
  /**
8054
- * Renders one toolbar controls slot.
8361
+ * Text input styled for use inside a toolbar.
8055
8362
  *
8056
- * Custom controls with position `"start"` are rendered before the built-in
8057
- * controls for the slot. Custom controls with position `"end"` are rendered
8058
- * after them.
8363
+ * This wraps `NodeInput` with toolbar-specific text styling. Use it for inline
8364
+ * toolbar editing flows where the user can submit a value or cancel editing.
8059
8365
  */
8060
- function ControlsContainerSlot(props) {
8061
- const { slot, document, controlsContainerClassName, enabledControls, disabledControls, componentOverrides, customControls, controlsContainer: ControlsContainer = ToolbarControlsContainer } = props;
8062
- const renderToolbarControls = makeToolbarControlsRenderer({
8063
- document,
8064
- enabledControls,
8065
- disabledControls,
8066
- componentOverrides
8067
- });
8068
- const renderCustomControls = makeCustomControlsRenderer({
8069
- document,
8070
- customControls
8071
- });
8072
- return /* @__PURE__ */ jsxs(ControlsContainer, {
8073
- className: controlsContainerClassName,
8074
- children: [
8075
- renderCustomControls(slot, "start"),
8076
- renderToolbarControls(slot),
8077
- renderCustomControls(slot, "end")
8078
- ]
8366
+ function ToolbarInput(props) {
8367
+ const { defaultValue, className, onSubmit, onCancel, "aria-label": ariaLabel } = props;
8368
+ return /* @__PURE__ */ jsx(NodeInput, {
8369
+ defaultValue,
8370
+ className: twMerge("text-center text-sm font-medium text-muted-foreground", className),
8371
+ "aria-label": ariaLabel,
8372
+ onCancel,
8373
+ onSubmit
8079
8374
  });
8080
8375
  }
8081
- const normalNodeOptions = [
8082
- "DUPLICATE",
8083
- "RENAME",
8084
- "DELETE",
8085
- "SETTINGS"
8086
- ];
8087
- const debugNodeOptions = [
8088
- "ADD_TRIGGER",
8089
- "REMOVE_TRIGGER",
8090
- "ADD_INVALID_TRIGGER"
8091
- ];
8092
- [...normalNodeOptions, ...debugNodeOptions];
8093
- //#endregion
8094
- //#region ../../node_modules/.pnpm/@powerhousedao+reactor-attachments@6.0.0-dev.257_@emnapi+core@1.10.0_@emnapi+runtime@1._ac8ae047337833ca71dd21474a1e3e5f/node_modules/@powerhousedao/reactor-attachments/dist/index.js
8095
8376
  /**
8096
- * Thrown when an attachment ref or hash is not known to the store.
8377
+ * Toolbar control for displaying and renaming the current document.
8378
+ *
8379
+ * By default, the component renders the document name as a clickable heading.
8380
+ * When clicked, it switches to an inline input. Submitting the input renames
8381
+ * both the node and the corresponding drive node entry.
8097
8382
  */
8098
- var AttachmentNotFound = class extends Error {
8099
- constructor(identifier) {
8100
- super(`Attachment not found: ${identifier}`);
8101
- this.name = "AttachmentNotFound";
8102
- }
8103
- };
8383
+ function ToolbarName(props) {
8384
+ const { document, inputClassName, titleClassName } = props;
8385
+ const [isEditing, setIsEditing] = useState(false);
8386
+ const node = useNodeById(document?.header.id);
8387
+ const { onRenameNode, onRenameDriveNodes } = useNodeActions();
8388
+ const documentName = document?.header.name;
8389
+ const documentId = document?.header.id;
8390
+ const activateEditing = () => setIsEditing(true);
8391
+ const cancelEditing = () => setIsEditing(false);
8392
+ const onSubmit = (newName) => {
8393
+ cancelEditing();
8394
+ if (!documentId || !node) return;
8395
+ Promise.all([onRenameNode(newName, node), onRenameDriveNodes(newName, documentId)]).catch(console.error);
8396
+ };
8397
+ if (!documentName) return null;
8398
+ if (isEditing) return /* @__PURE__ */ jsx(ToolbarInput, {
8399
+ className: inputClassName,
8400
+ onSubmit,
8401
+ onCancel: cancelEditing,
8402
+ defaultValue: documentName,
8403
+ "aria-label": "Document name"
8404
+ });
8405
+ return /* @__PURE__ */ jsx("h1", {
8406
+ className: twMerge("cursor-pointer text-sm font-medium text-muted-foreground hover:hover-effect", titleClassName),
8407
+ onClick: activateEditing,
8408
+ title: "Click to edit",
8409
+ children: documentName
8410
+ });
8411
+ }
8104
8412
  /**
8105
- * Thrown when a reservation ID is not found in the reservation store.
8413
+ * Default slot layout for the built-in document toolbar controls.
8414
+ *
8415
+ * The toolbar is divided into three control groups:
8416
+ *
8417
+ * - `first`: primary document actions.
8418
+ * - `second`: document identity/display controls.
8419
+ * - `third`: secondary document actions.
8106
8420
  */
8107
- var ReservationNotFound = class extends Error {
8108
- constructor(reservationId) {
8109
- super(`Reservation not found: ${reservationId}`);
8110
- this.name = "ReservationNotFound";
8111
- }
8421
+ const defaultControlSlots = {
8422
+ first: [
8423
+ "undo",
8424
+ "redo",
8425
+ "download"
8426
+ ],
8427
+ second: ["name"],
8428
+ third: [
8429
+ "history",
8430
+ "switchboard",
8431
+ "close"
8432
+ ]
8112
8433
  };
8113
8434
  /**
8114
- * Thrown when an attachment ref string does not match the expected format.
8115
- */
8116
- var InvalidAttachmentRef = class extends Error {
8117
- constructor(ref) {
8118
- super(`Invalid attachment ref: ${ref}`);
8119
- this.name = "InvalidAttachmentRef";
8120
- }
8121
- };
8122
- const REF_PATTERN = /^attachment:\/\/v(\d+):(.+)$/;
8123
- function parseRef(ref) {
8124
- const match = REF_PATTERN.exec(ref);
8125
- if (!match) throw new InvalidAttachmentRef(ref);
8126
- return {
8127
- version: Number(match[1]),
8128
- hash: match[2]
8129
- };
8130
- }
8131
- var AttachmentService = class {
8132
- constructor(store, reservations, uploadFactory) {
8133
- this.store = store;
8134
- this.reservations = reservations;
8135
- this.uploadFactory = uploadFactory;
8136
- }
8137
- async reserve(options) {
8138
- const reservation = await this.reservations.create(options);
8139
- return this.uploadFactory.createUpload(reservation.reservationId, options);
8140
- }
8141
- async stat(ref) {
8142
- const { hash } = parseRef(ref);
8143
- return this.store.stat(hash);
8144
- }
8145
- async get(ref, signal) {
8146
- const { hash } = parseRef(ref);
8147
- return this.store.get(hash, signal);
8148
- }
8149
- };
8150
- async function buildAuthHeaders(url, jwtHandler) {
8151
- const headers = {};
8152
- if (jwtHandler) {
8153
- const token = await jwtHandler(url);
8154
- if (token) headers["Authorization"] = `Bearer ${token}`;
8155
- }
8156
- return headers;
8157
- }
8158
- function isRecord$1(value) {
8159
- return typeof value === "object" && value !== null && !Array.isArray(value);
8160
- }
8161
- function isReservation(value) {
8162
- if (!isRecord$1(value)) return false;
8163
- if (typeof value.reservationId !== "string") return false;
8164
- if (typeof value.mimeType !== "string") return false;
8165
- if (typeof value.fileName !== "string") return false;
8166
- if (value.extension !== null && typeof value.extension !== "string") return false;
8167
- if (typeof value.createdAtUtc !== "string") return false;
8168
- if (typeof value.expiresAtUtc !== "string") return false;
8169
- return true;
8170
- }
8171
- var RemoteReservationStore = class {
8172
- remoteUrl;
8173
- jwtHandler;
8174
- fetchFn;
8175
- constructor(config) {
8176
- this.remoteUrl = config.remoteUrl;
8177
- this.jwtHandler = config.jwtHandler;
8178
- this.fetchFn = config.fetchFn ?? globalThis.fetch;
8179
- }
8180
- async create(options) {
8181
- const url = `${this.remoteUrl}/attachments/reservations`;
8182
- const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
8183
- const response = await this.fetchFn(url, {
8184
- method: "POST",
8185
- headers: {
8186
- ...authHeaders,
8187
- "Content-Type": "application/json"
8188
- },
8189
- body: JSON.stringify({
8190
- mimeType: options.mimeType,
8191
- fileName: options.fileName,
8192
- extension: options.extension ?? null
8193
- })
8194
- });
8195
- if (!response.ok) throw new Error(`Reservation create failed: ${response.status} ${response.statusText}`);
8196
- const json = await response.json();
8197
- const now = /* @__PURE__ */ new Date();
8198
- return {
8199
- reservationId: json.reservationId,
8200
- mimeType: options.mimeType,
8201
- fileName: options.fileName,
8202
- extension: options.extension ?? null,
8203
- createdAtUtc: json.createdAtUtc ?? now.toISOString(),
8204
- expiresAtUtc: json.expiresAtUtc ?? new Date(now.getTime() + 1440 * 60 * 1e3).toISOString()
8205
- };
8206
- }
8207
- async get(reservationId) {
8208
- const url = `${this.remoteUrl}/attachments/reservations/${encodeURIComponent(reservationId)}`;
8209
- const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
8210
- const response = await this.fetchFn(url, { headers: authHeaders });
8211
- if (response.status === 404) throw new ReservationNotFound(reservationId);
8212
- if (!response.ok) throw new Error(`Reservation get failed: ${response.status} ${response.statusText}`);
8213
- let parsed;
8214
- try {
8215
- parsed = await response.json();
8216
- } catch {
8217
- throw new Error("Reservation get returned non-JSON response");
8218
- }
8219
- if (!isReservation(parsed)) throw new Error("Reservation get returned a payload that does not match the Reservation shape");
8220
- return parsed;
8221
- }
8222
- async delete(reservationId) {
8223
- const url = `${this.remoteUrl}/attachments/reservations/${encodeURIComponent(reservationId)}`;
8224
- const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
8225
- const response = await this.fetchFn(url, {
8226
- method: "DELETE",
8227
- headers: authHeaders
8228
- });
8229
- if (!response.ok && response.status !== 404 && response.status !== 410) throw new Error(`Reservation delete failed: ${response.status} ${response.statusText}`);
8230
- }
8231
- deleteExpired() {
8232
- return Promise.reject(/* @__PURE__ */ new Error("RemoteReservationStore.deleteExpired is not supported"));
8233
- }
8234
- };
8235
- var RemoteAttachmentUpload = class {
8236
- reservationId;
8237
- remoteUrl;
8238
- jwtHandler;
8239
- fetchFn;
8240
- options;
8241
- constructor(reservationId, options, config) {
8242
- this.reservationId = reservationId;
8243
- this.options = options;
8244
- this.remoteUrl = config.remoteUrl;
8245
- this.jwtHandler = config.jwtHandler;
8246
- this.fetchFn = config.fetchFn ?? globalThis.fetch;
8247
- }
8248
- async send(data) {
8249
- const url = `${this.remoteUrl}/attachments/reservations/${this.reservationId}`;
8250
- const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
8251
- const response = await this.fetchFn(url, {
8252
- method: "PUT",
8253
- headers: {
8254
- ...authHeaders,
8255
- "Content-Type": "application/octet-stream"
8256
- },
8257
- body: data,
8258
- duplex: "half"
8259
- });
8260
- if (!response.ok) throw new Error(`Attachment upload failed: ${response.status} ${response.statusText}`);
8261
- return await response.json();
8262
- }
8263
- };
8264
- var RemoteAttachmentUploadFactory = class {
8265
- constructor(config) {
8266
- this.config = config;
8267
- }
8268
- createUpload(reservationId, options) {
8269
- return new RemoteAttachmentUpload(reservationId, options, this.config);
8270
- }
8435
+ * Ordered list of toolbar slot names.
8436
+ */
8437
+ const controlSlots = t$3(defaultControlSlots);
8438
+ /**
8439
+ * Ordered list of all built-in document toolbar control names.
8440
+ *
8441
+ * The order is derived from `defaultControlSlots`.
8442
+ */
8443
+ const documentToolbarControls = [
8444
+ ...defaultControlSlots.first,
8445
+ ...defaultControlSlots.second,
8446
+ ...defaultControlSlots.third
8447
+ ];
8448
+ /**
8449
+ * Default component implementation for each built-in toolbar control.
8450
+ *
8451
+ * These components are used unless a matching entry is provided through
8452
+ * `componentOverrides`.
8453
+ */
8454
+ const defaultControlComponents = {
8455
+ undo: ToolbarUndoButton,
8456
+ redo: ToolbarRedoButton,
8457
+ download: ToolbarDownloadButton,
8458
+ name: ToolbarName,
8459
+ switchboard: ToolbarSwitchboardButton,
8460
+ history: ToolbarHistoryButton,
8461
+ close: ToolbarCloseButton
8271
8462
  };
8272
- function isRecord(value) {
8273
- return typeof value === "object" && value !== null && !Array.isArray(value);
8463
+ /**
8464
+ * Creates a predicate for checking whether a built-in toolbar control should render.
8465
+ *
8466
+ * A control renders when it is included in `enabledControls` and absent from
8467
+ * `disabledControls`. When `enabledControls` is omitted, all built-in controls
8468
+ * are considered enabled. When a control appears in both lists,
8469
+ * `disabledControls` takes precedence.
8470
+ */
8471
+ function makeIsEnabledChecker(args) {
8472
+ const { enabledControls = documentToolbarControls, disabledControls = [] } = args;
8473
+ return (control) => e$2(control, enabledControls) && !e$2(control, disabledControls);
8274
8474
  }
8275
- function isAttachmentMetadata(value) {
8276
- if (!isRecord(value)) return false;
8277
- if (typeof value.mimeType !== "string") return false;
8278
- if (typeof value.fileName !== "string") return false;
8279
- if (typeof value.sizeBytes !== "number" || !Number.isFinite(value.sizeBytes) || value.sizeBytes < 0) return false;
8280
- if (value.extension !== null && typeof value.extension !== "string") return false;
8281
- if (typeof value.createdAtUtc !== "string") return false;
8282
- if (value.lastAccessedAtUtc !== void 0 && typeof value.lastAccessedAtUtc !== "string") return false;
8283
- return true;
8475
+ /**
8476
+ * Creates a getter for rendering the built-in toolbar controls in a slot.
8477
+ *
8478
+ * The returned function resolves the controls assigned to a slot, filters them
8479
+ * through the enabled/disabled control lists, applies any component overrides,
8480
+ * and renders each control with the current document.
8481
+ */
8482
+ function makeToolbarControlsRenderer(args) {
8483
+ const { document, enabledControls, disabledControls, componentOverrides } = args;
8484
+ const checkIsEnabled = makeIsEnabledChecker({
8485
+ enabledControls,
8486
+ disabledControls
8487
+ });
8488
+ const renderComponent = (control) => t$4(e(componentOverrides, control), t$6(e(defaultControlComponents, control)), (Component) => /* @__PURE__ */ jsx(Component, { document }, control));
8489
+ return (slot) => t$4(e(defaultControlSlots, slot), n$1(checkIsEnabled), t$2(renderComponent));
8284
8490
  }
8285
- function contentTypeFallback(response) {
8286
- const contentLength = response.headers.get("Content-Length");
8287
- if (contentLength === null) throw new Error("Switchboard response missing both Attachment-Metadata and Content-Length headers");
8288
- const sizeBytes = Number(contentLength);
8289
- if (!Number.isInteger(sizeBytes) || sizeBytes < 0) throw new Error(`Switchboard response has invalid Content-Length header: ${JSON.stringify(contentLength)}`);
8290
- const lastModified = response.headers.get("Last-Modified");
8291
- const dateHeader = response.headers.get("Date");
8292
- const createdAtUtc = lastModified ? new Date(lastModified).toISOString() : dateHeader ? new Date(dateHeader).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
8293
- return {
8294
- mimeType: response.headers.get("Content-Type") ?? "application/octet-stream",
8295
- fileName: "unknown",
8296
- sizeBytes,
8297
- extension: null,
8298
- createdAtUtc,
8299
- lastAccessedAtUtc: createdAtUtc
8300
- };
8491
+ /**
8492
+ * Checks whether a custom control should render in the requested position.
8493
+ *
8494
+ * Controls without an explicit position are treated as `"start"`.
8495
+ */
8496
+ function isControlInPosition(control, position) {
8497
+ return t$6(control.position, "start") === position;
8301
8498
  }
8302
- function parseMetadata(response) {
8303
- let fallbackCache;
8304
- const fallback = () => {
8305
- if (fallbackCache === void 0) fallbackCache = contentTypeFallback(response);
8306
- return fallbackCache;
8499
+ /**
8500
+ * Creates a getter for rendering custom controls in a slot and position.
8501
+ *
8502
+ * The returned function resolves the custom control or controls assigned to a
8503
+ * slot, then renders only the controls that belong in the requested position.
8504
+ */
8505
+ function makeCustomControlsRenderer(args) {
8506
+ const { document, customControls = {} } = args;
8507
+ return (slot, pos) => {
8508
+ const controlOrControlList = e(customControls, slot);
8509
+ if (!e$5(controlOrControlList)) return null;
8510
+ if (e$3(controlOrControlList)) return renderCustomControlList(controlOrControlList, pos, document);
8511
+ return renderCustomControl(controlOrControlList, pos, document);
8307
8512
  };
8308
- const metaHeader = response.headers.get("Attachment-Metadata");
8309
- if (metaHeader) try {
8310
- const parsed = JSON.parse(metaHeader);
8311
- if (isRecord(parsed)) {
8312
- if (parsed.extension === void 0) parsed.extension = null;
8313
- if (parsed.createdAtUtc === void 0) parsed.createdAtUtc = fallback().createdAtUtc;
8314
- if (parsed.lastAccessedAtUtc === void 0) parsed.lastAccessedAtUtc = fallback().lastAccessedAtUtc;
8315
- }
8316
- if (isAttachmentMetadata(parsed)) return parsed;
8317
- } catch {}
8318
- return fallback();
8319
8513
  }
8320
- var RemoteAttachmentStore = class {
8321
- remoteUrl;
8322
- jwtHandler;
8323
- fetchFn;
8324
- constructor(config) {
8325
- this.remoteUrl = config.remoteUrl;
8326
- this.jwtHandler = config.jwtHandler;
8327
- this.fetchFn = config.fetchFn ?? globalThis.fetch;
8328
- }
8329
- async stat(hash) {
8330
- const url = `${this.remoteUrl}/attachments/${hash}`;
8331
- const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
8332
- const response = await this.fetchFn(url, {
8333
- method: "HEAD",
8334
- headers: authHeaders
8335
- });
8336
- if (response.status === 404) throw new AttachmentNotFound(hash);
8337
- if (!response.ok) throw new Error(`Attachment stat failed: ${response.status} ${response.statusText}`);
8338
- return buildHeader(hash, parseMetadata(response));
8339
- }
8340
- async get(hash, signal) {
8341
- return this.fetchAttachment(hash, signal);
8342
- }
8343
- async fetchAttachment(hash, signal) {
8344
- const url = `${this.remoteUrl}/attachments/${hash}`;
8345
- const headers = await buildAuthHeaders(url, this.jwtHandler);
8346
- const response = await this.fetchFn(url, {
8347
- signal,
8348
- headers
8349
- });
8350
- if (response.status === 404) throw new AttachmentNotFound(hash);
8351
- if (!response.ok) throw new Error(`Attachment fetch failed: ${response.status} ${response.statusText}`);
8352
- if (!response.body) throw new Error("Response body is null");
8353
- return {
8354
- header: buildHeader(hash, parseMetadata(response)),
8355
- body: response.body
8356
- };
8357
- }
8358
- };
8359
- function buildHeader(hash, metadata) {
8360
- return {
8361
- hash,
8362
- mimeType: metadata.mimeType,
8363
- fileName: metadata.fileName,
8364
- sizeBytes: metadata.sizeBytes,
8365
- extension: metadata.extension,
8366
- status: "available",
8367
- source: "sync",
8368
- createdAtUtc: metadata.createdAtUtc,
8369
- lastAccessedAtUtc: metadata.lastAccessedAtUtc ?? metadata.createdAtUtc
8370
- };
8514
+ /**
8515
+ * Renders a single custom control when it belongs in the requested position.
8516
+ */
8517
+ function renderCustomControl(control, pos, document) {
8518
+ if (!isControlInPosition(control, pos)) return null;
8519
+ const Component = control.component;
8520
+ return /* @__PURE__ */ jsx(Component, { document });
8371
8521
  }
8372
- function createRemoteAttachmentService(config) {
8373
- const reservations = new RemoteReservationStore(config);
8374
- const uploadFactory = new RemoteAttachmentUploadFactory(config);
8375
- return new AttachmentService(new RemoteAttachmentStore(config), reservations, uploadFactory);
8522
+ /**
8523
+ * Renders a list of custom controls for the requested position.
8524
+ *
8525
+ * Returns `null` when no controls in the list belong in that position.
8526
+ */
8527
+ function renderCustomControlList(controls, pos, document) {
8528
+ const controlsInPosition = n$1(controls, (control) => isControlInPosition(control, pos));
8529
+ if (!t$8(controlsInPosition, 1)) return null;
8530
+ return /* @__PURE__ */ jsx(Fragment$1, { children: t$2(controlsInPosition, ({ component: Component, key }) => /* @__PURE__ */ jsx(Component, { document }, key)) });
8531
+ }
8532
+ /**
8533
+ * Renders a document toolbar.
8534
+ *
8535
+ * By default, the toolbar renders the built-in document controls grouped into
8536
+ * toolbar slots. The controls operate on the provided `document`, or on the
8537
+ * currently selected document when no document is provided.
8538
+ *
8539
+ * Use `enabledControls` and `disabledControls` to control which built-in
8540
+ * controls are shown. Use `componentOverrides` to replace individual built-in
8541
+ * controls while keeping the default toolbar layout. Use `customControls` to
8542
+ * insert additional controls before or after the built-in controls in a slot.
8543
+ *
8544
+ * To take over the toolbar contents completely, pass `children`.
8545
+ */
8546
+ function DocumentToolbar(props) {
8547
+ const [selectedDocument] = useSelectedDocumentSafe();
8548
+ const { toolbarClassName, document = selectedDocument, toolbarContainer: Container = ToolbarContainer } = props;
8549
+ if ("children" in props) return /* @__PURE__ */ jsx(Container, {
8550
+ className: toolbarClassName,
8551
+ children: props.children
8552
+ });
8553
+ return /* @__PURE__ */ jsx(Container, {
8554
+ className: toolbarClassName,
8555
+ children: t$2(controlSlots, (slot) => /* @__PURE__ */ createElement(ControlsContainerSlot, {
8556
+ ...props,
8557
+ document,
8558
+ slot,
8559
+ key: slot
8560
+ }))
8561
+ });
8562
+ }
8563
+ /**
8564
+ * Renders one toolbar controls slot.
8565
+ *
8566
+ * Custom controls with position `"start"` are rendered before the built-in
8567
+ * controls for the slot. Custom controls with position `"end"` are rendered
8568
+ * after them.
8569
+ */
8570
+ function ControlsContainerSlot(props) {
8571
+ const { slot, document, controlsContainerClassName, enabledControls, disabledControls, componentOverrides, customControls, controlsContainer: ControlsContainer = ToolbarControlsContainer } = props;
8572
+ const renderToolbarControls = makeToolbarControlsRenderer({
8573
+ document,
8574
+ enabledControls,
8575
+ disabledControls,
8576
+ componentOverrides
8577
+ });
8578
+ const renderCustomControls = makeCustomControlsRenderer({
8579
+ document,
8580
+ customControls
8581
+ });
8582
+ return /* @__PURE__ */ jsxs(ControlsContainer, {
8583
+ className: controlsContainerClassName,
8584
+ children: [
8585
+ renderCustomControls(slot, "start"),
8586
+ renderToolbarControls(slot),
8587
+ renderCustomControls(slot, "end")
8588
+ ]
8589
+ });
8376
8590
  }
8591
+ const normalNodeOptions = [
8592
+ "DUPLICATE",
8593
+ "RENAME",
8594
+ "DELETE",
8595
+ "SETTINGS"
8596
+ ];
8597
+ const debugNodeOptions = [
8598
+ "ADD_TRIGGER",
8599
+ "REMOVE_TRIGGER",
8600
+ "ADD_INVALID_TRIGGER"
8601
+ ];
8602
+ [...normalNodeOptions, ...debugNodeOptions];
8377
8603
  //#endregion
8378
8604
  //#region editors/ph-clint-project-editor/editor.tsx
8379
8605
  const POWERHOUSE_LEVELS = [
@@ -10198,4 +10424,4 @@ function StatusBadge(props) {
10198
10424
  //#endregion
10199
10425
  export { Editor as default };
10200
10426
 
10201
- //# sourceMappingURL=editor-BuHwuzRa.js.map
10427
+ //# sourceMappingURL=editor-Bp2wUo8R.js.map