@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
@@ -123,6 +123,521 @@ function t(...t) {
123
123
  return t$7(Object.values, t);
124
124
  }
125
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
126
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
127
642
  function Created(props) {
128
643
  return /* @__PURE__ */ jsxs("svg", {
@@ -7839,542 +8354,258 @@ function ToolbarCloseButton(props) {
7839
8354
  });
7840
8355
  }
7841
8356
  /**
7842
- * Creates a toolbar button click handler.
7843
- *
7844
- * If an override is provided, it is called with the current document. Otherwise,
7845
- * the built-in handler is called with the current document.
7846
- */
7847
- function makeOnClick(document, onClickOverride, defaultOnClick) {
7848
- if (e$5(onClickOverride)) return () => onClickOverride(document);
7849
- return () => defaultOnClick(document);
7850
- }
7851
- /**
7852
- * Text input styled for use inside a toolbar.
7853
- *
7854
- * This wraps `NodeInput` with toolbar-specific text styling. Use it for inline
7855
- * toolbar editing flows where the user can submit a value or cancel editing.
7856
- */
7857
- function ToolbarInput(props) {
7858
- const { defaultValue, className, onSubmit, onCancel, "aria-label": ariaLabel } = props;
7859
- return /* @__PURE__ */ jsx(NodeInput, {
7860
- defaultValue,
7861
- className: twMerge("text-center text-sm font-medium text-muted-foreground", className),
7862
- "aria-label": ariaLabel,
7863
- onCancel,
7864
- onSubmit
7865
- });
7866
- }
7867
- /**
7868
- * Toolbar control for displaying and renaming the current document.
7869
- *
7870
- * By default, the component renders the document name as a clickable heading.
7871
- * When clicked, it switches to an inline input. Submitting the input renames
7872
- * both the node and the corresponding drive node entry.
7873
- */
7874
- function ToolbarName(props) {
7875
- const { document, inputClassName, titleClassName } = props;
7876
- const [isEditing, setIsEditing] = useState(false);
7877
- const node = useNodeById(document?.header.id);
7878
- const { onRenameNode, onRenameDriveNodes } = useNodeActions();
7879
- const documentName = document?.header.name;
7880
- const documentId = document?.header.id;
7881
- const activateEditing = () => setIsEditing(true);
7882
- const cancelEditing = () => setIsEditing(false);
7883
- const onSubmit = (newName) => {
7884
- cancelEditing();
7885
- if (!documentId || !node) return;
7886
- Promise.all([onRenameNode(newName, node), onRenameDriveNodes(newName, documentId)]).catch(console.error);
7887
- };
7888
- if (!documentName) return null;
7889
- if (isEditing) return /* @__PURE__ */ jsx(ToolbarInput, {
7890
- className: inputClassName,
7891
- onSubmit,
7892
- onCancel: cancelEditing,
7893
- defaultValue: documentName,
7894
- "aria-label": "Document name"
7895
- });
7896
- return /* @__PURE__ */ jsx("h1", {
7897
- className: twMerge("cursor-pointer text-sm font-medium text-muted-foreground hover:hover-effect", titleClassName),
7898
- onClick: activateEditing,
7899
- title: "Click to edit",
7900
- children: documentName
7901
- });
7902
- }
7903
- /**
7904
- * Default slot layout for the built-in document toolbar controls.
7905
- *
7906
- * The toolbar is divided into three control groups:
7907
- *
7908
- * - `first`: primary document actions.
7909
- * - `second`: document identity/display controls.
7910
- * - `third`: secondary document actions.
7911
- */
7912
- const defaultControlSlots = {
7913
- first: [
7914
- "undo",
7915
- "redo",
7916
- "download"
7917
- ],
7918
- second: ["name"],
7919
- third: [
7920
- "history",
7921
- "switchboard",
7922
- "close"
7923
- ]
7924
- };
7925
- /**
7926
- * Ordered list of toolbar slot names.
7927
- */
7928
- const controlSlots = t$3(defaultControlSlots);
7929
- /**
7930
- * Ordered list of all built-in document toolbar control names.
7931
- *
7932
- * The order is derived from `defaultControlSlots`.
7933
- */
7934
- const documentToolbarControls = [
7935
- ...defaultControlSlots.first,
7936
- ...defaultControlSlots.second,
7937
- ...defaultControlSlots.third
7938
- ];
7939
- /**
7940
- * Default component implementation for each built-in toolbar control.
7941
- *
7942
- * These components are used unless a matching entry is provided through
7943
- * `componentOverrides`.
7944
- */
7945
- const defaultControlComponents = {
7946
- undo: ToolbarUndoButton,
7947
- redo: ToolbarRedoButton,
7948
- download: ToolbarDownloadButton,
7949
- name: ToolbarName,
7950
- switchboard: ToolbarSwitchboardButton,
7951
- history: ToolbarHistoryButton,
7952
- close: ToolbarCloseButton
7953
- };
7954
- /**
7955
- * Creates a predicate for checking whether a built-in toolbar control should render.
7956
- *
7957
- * A control renders when it is included in `enabledControls` and absent from
7958
- * `disabledControls`. When `enabledControls` is omitted, all built-in controls
7959
- * are considered enabled. When a control appears in both lists,
7960
- * `disabledControls` takes precedence.
7961
- */
7962
- function makeIsEnabledChecker(args) {
7963
- const { enabledControls = documentToolbarControls, disabledControls = [] } = args;
7964
- return (control) => e$2(control, enabledControls) && !e$2(control, disabledControls);
7965
- }
7966
- /**
7967
- * Creates a getter for rendering the built-in toolbar controls in a slot.
7968
- *
7969
- * The returned function resolves the controls assigned to a slot, filters them
7970
- * through the enabled/disabled control lists, applies any component overrides,
7971
- * and renders each control with the current document.
7972
- */
7973
- function makeToolbarControlsRenderer(args) {
7974
- const { document, enabledControls, disabledControls, componentOverrides } = args;
7975
- const checkIsEnabled = makeIsEnabledChecker({
7976
- enabledControls,
7977
- disabledControls
7978
- });
7979
- const renderComponent = (control) => t$4(e(componentOverrides, control), t$6(e(defaultControlComponents, control)), (Component) => /* @__PURE__ */ jsx(Component, { document }, control));
7980
- return (slot) => t$4(e(defaultControlSlots, slot), n$1(checkIsEnabled), t$2(renderComponent));
7981
- }
7982
- /**
7983
- * Checks whether a custom control should render in the requested position.
7984
- *
7985
- * Controls without an explicit position are treated as `"start"`.
7986
- */
7987
- function isControlInPosition(control, position) {
7988
- return t$6(control.position, "start") === position;
7989
- }
7990
- /**
7991
- * Creates a getter for rendering custom controls in a slot and position.
7992
- *
7993
- * The returned function resolves the custom control or controls assigned to a
7994
- * slot, then renders only the controls that belong in the requested position.
7995
- */
7996
- function makeCustomControlsRenderer(args) {
7997
- const { document, customControls = {} } = args;
7998
- return (slot, pos) => {
7999
- const controlOrControlList = e(customControls, slot);
8000
- if (!e$5(controlOrControlList)) return null;
8001
- if (e$3(controlOrControlList)) return renderCustomControlList(controlOrControlList, pos, document);
8002
- return renderCustomControl(controlOrControlList, pos, document);
8003
- };
8004
- }
8005
- /**
8006
- * Renders a single custom control when it belongs in the requested position.
8007
- */
8008
- function renderCustomControl(control, pos, document) {
8009
- if (!isControlInPosition(control, pos)) return null;
8010
- const Component = control.component;
8011
- return /* @__PURE__ */ jsx(Component, { document });
8012
- }
8013
- /**
8014
- * Renders a list of custom controls for the requested position.
8015
- *
8016
- * Returns `null` when no controls in the list belong in that position.
8017
- */
8018
- function renderCustomControlList(controls, pos, document) {
8019
- const controlsInPosition = n$1(controls, (control) => isControlInPosition(control, pos));
8020
- if (!t$8(controlsInPosition, 1)) return null;
8021
- return /* @__PURE__ */ jsx(Fragment$1, { children: t$2(controlsInPosition, ({ component: Component, key }) => /* @__PURE__ */ jsx(Component, { document }, key)) });
8022
- }
8023
- /**
8024
- * Renders a document toolbar.
8025
- *
8026
- * By default, the toolbar renders the built-in document controls grouped into
8027
- * toolbar slots. The controls operate on the provided `document`, or on the
8028
- * currently selected document when no document is provided.
8029
- *
8030
- * Use `enabledControls` and `disabledControls` to control which built-in
8031
- * controls are shown. Use `componentOverrides` to replace individual built-in
8032
- * controls while keeping the default toolbar layout. Use `customControls` to
8033
- * insert additional controls before or after the built-in controls in a slot.
8034
- *
8035
- * To take over the toolbar contents completely, pass `children`.
8036
- */
8037
- function DocumentToolbar(props) {
8038
- const [selectedDocument] = useSelectedDocumentSafe();
8039
- const { toolbarClassName, document = selectedDocument, toolbarContainer: Container = ToolbarContainer } = props;
8040
- if ("children" in props) return /* @__PURE__ */ jsx(Container, {
8041
- className: toolbarClassName,
8042
- children: props.children
8043
- });
8044
- return /* @__PURE__ */ jsx(Container, {
8045
- className: toolbarClassName,
8046
- children: t$2(controlSlots, (slot) => /* @__PURE__ */ createElement(ControlsContainerSlot, {
8047
- ...props,
8048
- document,
8049
- slot,
8050
- key: slot
8051
- }))
8052
- });
8357
+ * Creates a toolbar button click handler.
8358
+ *
8359
+ * If an override is provided, it is called with the current document. Otherwise,
8360
+ * the built-in handler is called with the current document.
8361
+ */
8362
+ function makeOnClick(document, onClickOverride, defaultOnClick) {
8363
+ if (e$5(onClickOverride)) return () => onClickOverride(document);
8364
+ return () => defaultOnClick(document);
8053
8365
  }
8054
8366
  /**
8055
- * Renders one toolbar controls slot.
8367
+ * Text input styled for use inside a toolbar.
8056
8368
  *
8057
- * Custom controls with position `"start"` are rendered before the built-in
8058
- * controls for the slot. Custom controls with position `"end"` are rendered
8059
- * after them.
8369
+ * This wraps `NodeInput` with toolbar-specific text styling. Use it for inline
8370
+ * toolbar editing flows where the user can submit a value or cancel editing.
8060
8371
  */
8061
- function ControlsContainerSlot(props) {
8062
- const { slot, document, controlsContainerClassName, enabledControls, disabledControls, componentOverrides, customControls, controlsContainer: ControlsContainer = ToolbarControlsContainer } = props;
8063
- const renderToolbarControls = makeToolbarControlsRenderer({
8064
- document,
8065
- enabledControls,
8066
- disabledControls,
8067
- componentOverrides
8068
- });
8069
- const renderCustomControls = makeCustomControlsRenderer({
8070
- document,
8071
- customControls
8072
- });
8073
- return /* @__PURE__ */ jsxs(ControlsContainer, {
8074
- className: controlsContainerClassName,
8075
- children: [
8076
- renderCustomControls(slot, "start"),
8077
- renderToolbarControls(slot),
8078
- renderCustomControls(slot, "end")
8079
- ]
8372
+ function ToolbarInput(props) {
8373
+ const { defaultValue, className, onSubmit, onCancel, "aria-label": ariaLabel } = props;
8374
+ return /* @__PURE__ */ jsx(NodeInput, {
8375
+ defaultValue,
8376
+ className: twMerge("text-center text-sm font-medium text-muted-foreground", className),
8377
+ "aria-label": ariaLabel,
8378
+ onCancel,
8379
+ onSubmit
8080
8380
  });
8081
8381
  }
8082
- const normalNodeOptions = [
8083
- "DUPLICATE",
8084
- "RENAME",
8085
- "DELETE",
8086
- "SETTINGS"
8087
- ];
8088
- const debugNodeOptions = [
8089
- "ADD_TRIGGER",
8090
- "REMOVE_TRIGGER",
8091
- "ADD_INVALID_TRIGGER"
8092
- ];
8093
- [...normalNodeOptions, ...debugNodeOptions];
8094
- //#endregion
8095
- //#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
8096
8382
  /**
8097
- * Thrown when an attachment ref or hash is not known to the store.
8383
+ * Toolbar control for displaying and renaming the current document.
8384
+ *
8385
+ * By default, the component renders the document name as a clickable heading.
8386
+ * When clicked, it switches to an inline input. Submitting the input renames
8387
+ * both the node and the corresponding drive node entry.
8098
8388
  */
8099
- var AttachmentNotFound = class extends Error {
8100
- constructor(identifier) {
8101
- super(`Attachment not found: ${identifier}`);
8102
- this.name = "AttachmentNotFound";
8103
- }
8104
- };
8389
+ function ToolbarName(props) {
8390
+ const { document, inputClassName, titleClassName } = props;
8391
+ const [isEditing, setIsEditing] = useState(false);
8392
+ const node = useNodeById(document?.header.id);
8393
+ const { onRenameNode, onRenameDriveNodes } = useNodeActions();
8394
+ const documentName = document?.header.name;
8395
+ const documentId = document?.header.id;
8396
+ const activateEditing = () => setIsEditing(true);
8397
+ const cancelEditing = () => setIsEditing(false);
8398
+ const onSubmit = (newName) => {
8399
+ cancelEditing();
8400
+ if (!documentId || !node) return;
8401
+ Promise.all([onRenameNode(newName, node), onRenameDriveNodes(newName, documentId)]).catch(console.error);
8402
+ };
8403
+ if (!documentName) return null;
8404
+ if (isEditing) return /* @__PURE__ */ jsx(ToolbarInput, {
8405
+ className: inputClassName,
8406
+ onSubmit,
8407
+ onCancel: cancelEditing,
8408
+ defaultValue: documentName,
8409
+ "aria-label": "Document name"
8410
+ });
8411
+ return /* @__PURE__ */ jsx("h1", {
8412
+ className: twMerge("cursor-pointer text-sm font-medium text-muted-foreground hover:hover-effect", titleClassName),
8413
+ onClick: activateEditing,
8414
+ title: "Click to edit",
8415
+ children: documentName
8416
+ });
8417
+ }
8105
8418
  /**
8106
- * Thrown when a reservation ID is not found in the reservation store.
8419
+ * Default slot layout for the built-in document toolbar controls.
8420
+ *
8421
+ * The toolbar is divided into three control groups:
8422
+ *
8423
+ * - `first`: primary document actions.
8424
+ * - `second`: document identity/display controls.
8425
+ * - `third`: secondary document actions.
8107
8426
  */
8108
- var ReservationNotFound = class extends Error {
8109
- constructor(reservationId) {
8110
- super(`Reservation not found: ${reservationId}`);
8111
- this.name = "ReservationNotFound";
8112
- }
8427
+ const defaultControlSlots = {
8428
+ first: [
8429
+ "undo",
8430
+ "redo",
8431
+ "download"
8432
+ ],
8433
+ second: ["name"],
8434
+ third: [
8435
+ "history",
8436
+ "switchboard",
8437
+ "close"
8438
+ ]
8113
8439
  };
8114
8440
  /**
8115
- * Thrown when an attachment ref string does not match the expected format.
8116
- */
8117
- var InvalidAttachmentRef = class extends Error {
8118
- constructor(ref) {
8119
- super(`Invalid attachment ref: ${ref}`);
8120
- this.name = "InvalidAttachmentRef";
8121
- }
8122
- };
8123
- const REF_PATTERN = /^attachment:\/\/v(\d+):(.+)$/;
8124
- function parseRef(ref) {
8125
- const match = REF_PATTERN.exec(ref);
8126
- if (!match) throw new InvalidAttachmentRef(ref);
8127
- return {
8128
- version: Number(match[1]),
8129
- hash: match[2]
8130
- };
8131
- }
8132
- var AttachmentService = class {
8133
- constructor(store, reservations, uploadFactory) {
8134
- this.store = store;
8135
- this.reservations = reservations;
8136
- this.uploadFactory = uploadFactory;
8137
- }
8138
- async reserve(options) {
8139
- const reservation = await this.reservations.create(options);
8140
- return this.uploadFactory.createUpload(reservation.reservationId, options);
8141
- }
8142
- async stat(ref) {
8143
- const { hash } = parseRef(ref);
8144
- return this.store.stat(hash);
8145
- }
8146
- async get(ref, signal) {
8147
- const { hash } = parseRef(ref);
8148
- return this.store.get(hash, signal);
8149
- }
8150
- };
8151
- async function buildAuthHeaders(url, jwtHandler) {
8152
- const headers = {};
8153
- if (jwtHandler) {
8154
- const token = await jwtHandler(url);
8155
- if (token) headers["Authorization"] = `Bearer ${token}`;
8156
- }
8157
- return headers;
8158
- }
8159
- function isRecord$1(value) {
8160
- return typeof value === "object" && value !== null && !Array.isArray(value);
8161
- }
8162
- function isReservation(value) {
8163
- if (!isRecord$1(value)) return false;
8164
- if (typeof value.reservationId !== "string") return false;
8165
- if (typeof value.mimeType !== "string") return false;
8166
- if (typeof value.fileName !== "string") return false;
8167
- if (value.extension !== null && typeof value.extension !== "string") return false;
8168
- if (typeof value.createdAtUtc !== "string") return false;
8169
- if (typeof value.expiresAtUtc !== "string") return false;
8170
- return true;
8171
- }
8172
- var RemoteReservationStore = class {
8173
- remoteUrl;
8174
- jwtHandler;
8175
- fetchFn;
8176
- constructor(config) {
8177
- this.remoteUrl = config.remoteUrl;
8178
- this.jwtHandler = config.jwtHandler;
8179
- this.fetchFn = config.fetchFn ?? globalThis.fetch;
8180
- }
8181
- async create(options) {
8182
- const url = `${this.remoteUrl}/attachments/reservations`;
8183
- const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
8184
- const response = await this.fetchFn(url, {
8185
- method: "POST",
8186
- headers: {
8187
- ...authHeaders,
8188
- "Content-Type": "application/json"
8189
- },
8190
- body: JSON.stringify({
8191
- mimeType: options.mimeType,
8192
- fileName: options.fileName,
8193
- extension: options.extension ?? null
8194
- })
8195
- });
8196
- if (!response.ok) throw new Error(`Reservation create failed: ${response.status} ${response.statusText}`);
8197
- const json = await response.json();
8198
- const now = /* @__PURE__ */ new Date();
8199
- return {
8200
- reservationId: json.reservationId,
8201
- mimeType: options.mimeType,
8202
- fileName: options.fileName,
8203
- extension: options.extension ?? null,
8204
- createdAtUtc: json.createdAtUtc ?? now.toISOString(),
8205
- expiresAtUtc: json.expiresAtUtc ?? new Date(now.getTime() + 1440 * 60 * 1e3).toISOString()
8206
- };
8207
- }
8208
- async get(reservationId) {
8209
- const url = `${this.remoteUrl}/attachments/reservations/${encodeURIComponent(reservationId)}`;
8210
- const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
8211
- const response = await this.fetchFn(url, { headers: authHeaders });
8212
- if (response.status === 404) throw new ReservationNotFound(reservationId);
8213
- if (!response.ok) throw new Error(`Reservation get failed: ${response.status} ${response.statusText}`);
8214
- let parsed;
8215
- try {
8216
- parsed = await response.json();
8217
- } catch {
8218
- throw new Error("Reservation get returned non-JSON response");
8219
- }
8220
- if (!isReservation(parsed)) throw new Error("Reservation get returned a payload that does not match the Reservation shape");
8221
- return parsed;
8222
- }
8223
- async delete(reservationId) {
8224
- const url = `${this.remoteUrl}/attachments/reservations/${encodeURIComponent(reservationId)}`;
8225
- const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
8226
- const response = await this.fetchFn(url, {
8227
- method: "DELETE",
8228
- headers: authHeaders
8229
- });
8230
- if (!response.ok && response.status !== 404 && response.status !== 410) throw new Error(`Reservation delete failed: ${response.status} ${response.statusText}`);
8231
- }
8232
- deleteExpired() {
8233
- return Promise.reject(/* @__PURE__ */ new Error("RemoteReservationStore.deleteExpired is not supported"));
8234
- }
8235
- };
8236
- var RemoteAttachmentUpload = class {
8237
- reservationId;
8238
- remoteUrl;
8239
- jwtHandler;
8240
- fetchFn;
8241
- options;
8242
- constructor(reservationId, options, config) {
8243
- this.reservationId = reservationId;
8244
- this.options = options;
8245
- this.remoteUrl = config.remoteUrl;
8246
- this.jwtHandler = config.jwtHandler;
8247
- this.fetchFn = config.fetchFn ?? globalThis.fetch;
8248
- }
8249
- async send(data) {
8250
- const url = `${this.remoteUrl}/attachments/reservations/${this.reservationId}`;
8251
- const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
8252
- const response = await this.fetchFn(url, {
8253
- method: "PUT",
8254
- headers: {
8255
- ...authHeaders,
8256
- "Content-Type": "application/octet-stream"
8257
- },
8258
- body: data,
8259
- duplex: "half"
8260
- });
8261
- if (!response.ok) throw new Error(`Attachment upload failed: ${response.status} ${response.statusText}`);
8262
- return await response.json();
8263
- }
8264
- };
8265
- var RemoteAttachmentUploadFactory = class {
8266
- constructor(config) {
8267
- this.config = config;
8268
- }
8269
- createUpload(reservationId, options) {
8270
- return new RemoteAttachmentUpload(reservationId, options, this.config);
8271
- }
8441
+ * Ordered list of toolbar slot names.
8442
+ */
8443
+ const controlSlots = t$3(defaultControlSlots);
8444
+ /**
8445
+ * Ordered list of all built-in document toolbar control names.
8446
+ *
8447
+ * The order is derived from `defaultControlSlots`.
8448
+ */
8449
+ const documentToolbarControls = [
8450
+ ...defaultControlSlots.first,
8451
+ ...defaultControlSlots.second,
8452
+ ...defaultControlSlots.third
8453
+ ];
8454
+ /**
8455
+ * Default component implementation for each built-in toolbar control.
8456
+ *
8457
+ * These components are used unless a matching entry is provided through
8458
+ * `componentOverrides`.
8459
+ */
8460
+ const defaultControlComponents = {
8461
+ undo: ToolbarUndoButton,
8462
+ redo: ToolbarRedoButton,
8463
+ download: ToolbarDownloadButton,
8464
+ name: ToolbarName,
8465
+ switchboard: ToolbarSwitchboardButton,
8466
+ history: ToolbarHistoryButton,
8467
+ close: ToolbarCloseButton
8272
8468
  };
8273
- function isRecord(value) {
8274
- return typeof value === "object" && value !== null && !Array.isArray(value);
8469
+ /**
8470
+ * Creates a predicate for checking whether a built-in toolbar control should render.
8471
+ *
8472
+ * A control renders when it is included in `enabledControls` and absent from
8473
+ * `disabledControls`. When `enabledControls` is omitted, all built-in controls
8474
+ * are considered enabled. When a control appears in both lists,
8475
+ * `disabledControls` takes precedence.
8476
+ */
8477
+ function makeIsEnabledChecker(args) {
8478
+ const { enabledControls = documentToolbarControls, disabledControls = [] } = args;
8479
+ return (control) => e$2(control, enabledControls) && !e$2(control, disabledControls);
8275
8480
  }
8276
- function isAttachmentMetadata(value) {
8277
- if (!isRecord(value)) return false;
8278
- if (typeof value.mimeType !== "string") return false;
8279
- if (typeof value.fileName !== "string") return false;
8280
- if (typeof value.sizeBytes !== "number" || !Number.isFinite(value.sizeBytes) || value.sizeBytes < 0) return false;
8281
- if (value.extension !== null && typeof value.extension !== "string") return false;
8282
- if (typeof value.createdAtUtc !== "string") return false;
8283
- if (value.lastAccessedAtUtc !== void 0 && typeof value.lastAccessedAtUtc !== "string") return false;
8284
- return true;
8481
+ /**
8482
+ * Creates a getter for rendering the built-in toolbar controls in a slot.
8483
+ *
8484
+ * The returned function resolves the controls assigned to a slot, filters them
8485
+ * through the enabled/disabled control lists, applies any component overrides,
8486
+ * and renders each control with the current document.
8487
+ */
8488
+ function makeToolbarControlsRenderer(args) {
8489
+ const { document, enabledControls, disabledControls, componentOverrides } = args;
8490
+ const checkIsEnabled = makeIsEnabledChecker({
8491
+ enabledControls,
8492
+ disabledControls
8493
+ });
8494
+ const renderComponent = (control) => t$4(e(componentOverrides, control), t$6(e(defaultControlComponents, control)), (Component) => /* @__PURE__ */ jsx(Component, { document }, control));
8495
+ return (slot) => t$4(e(defaultControlSlots, slot), n$1(checkIsEnabled), t$2(renderComponent));
8285
8496
  }
8286
- function contentTypeFallback(response) {
8287
- const contentLength = response.headers.get("Content-Length");
8288
- if (contentLength === null) throw new Error("Switchboard response missing both Attachment-Metadata and Content-Length headers");
8289
- const sizeBytes = Number(contentLength);
8290
- if (!Number.isInteger(sizeBytes) || sizeBytes < 0) throw new Error(`Switchboard response has invalid Content-Length header: ${JSON.stringify(contentLength)}`);
8291
- const lastModified = response.headers.get("Last-Modified");
8292
- const dateHeader = response.headers.get("Date");
8293
- const createdAtUtc = lastModified ? new Date(lastModified).toISOString() : dateHeader ? new Date(dateHeader).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
8294
- return {
8295
- mimeType: response.headers.get("Content-Type") ?? "application/octet-stream",
8296
- fileName: "unknown",
8297
- sizeBytes,
8298
- extension: null,
8299
- createdAtUtc,
8300
- lastAccessedAtUtc: createdAtUtc
8301
- };
8497
+ /**
8498
+ * Checks whether a custom control should render in the requested position.
8499
+ *
8500
+ * Controls without an explicit position are treated as `"start"`.
8501
+ */
8502
+ function isControlInPosition(control, position) {
8503
+ return t$6(control.position, "start") === position;
8302
8504
  }
8303
- function parseMetadata(response) {
8304
- let fallbackCache;
8305
- const fallback = () => {
8306
- if (fallbackCache === void 0) fallbackCache = contentTypeFallback(response);
8307
- return fallbackCache;
8505
+ /**
8506
+ * Creates a getter for rendering custom controls in a slot and position.
8507
+ *
8508
+ * The returned function resolves the custom control or controls assigned to a
8509
+ * slot, then renders only the controls that belong in the requested position.
8510
+ */
8511
+ function makeCustomControlsRenderer(args) {
8512
+ const { document, customControls = {} } = args;
8513
+ return (slot, pos) => {
8514
+ const controlOrControlList = e(customControls, slot);
8515
+ if (!e$5(controlOrControlList)) return null;
8516
+ if (e$3(controlOrControlList)) return renderCustomControlList(controlOrControlList, pos, document);
8517
+ return renderCustomControl(controlOrControlList, pos, document);
8308
8518
  };
8309
- const metaHeader = response.headers.get("Attachment-Metadata");
8310
- if (metaHeader) try {
8311
- const parsed = JSON.parse(metaHeader);
8312
- if (isRecord(parsed)) {
8313
- if (parsed.extension === void 0) parsed.extension = null;
8314
- if (parsed.createdAtUtc === void 0) parsed.createdAtUtc = fallback().createdAtUtc;
8315
- if (parsed.lastAccessedAtUtc === void 0) parsed.lastAccessedAtUtc = fallback().lastAccessedAtUtc;
8316
- }
8317
- if (isAttachmentMetadata(parsed)) return parsed;
8318
- } catch {}
8319
- return fallback();
8320
8519
  }
8321
- var RemoteAttachmentStore = class {
8322
- remoteUrl;
8323
- jwtHandler;
8324
- fetchFn;
8325
- constructor(config) {
8326
- this.remoteUrl = config.remoteUrl;
8327
- this.jwtHandler = config.jwtHandler;
8328
- this.fetchFn = config.fetchFn ?? globalThis.fetch;
8329
- }
8330
- async stat(hash) {
8331
- const url = `${this.remoteUrl}/attachments/${hash}`;
8332
- const authHeaders = await buildAuthHeaders(url, this.jwtHandler);
8333
- const response = await this.fetchFn(url, {
8334
- method: "HEAD",
8335
- headers: authHeaders
8336
- });
8337
- if (response.status === 404) throw new AttachmentNotFound(hash);
8338
- if (!response.ok) throw new Error(`Attachment stat failed: ${response.status} ${response.statusText}`);
8339
- return buildHeader(hash, parseMetadata(response));
8340
- }
8341
- async get(hash, signal) {
8342
- return this.fetchAttachment(hash, signal);
8343
- }
8344
- async fetchAttachment(hash, signal) {
8345
- const url = `${this.remoteUrl}/attachments/${hash}`;
8346
- const headers = await buildAuthHeaders(url, this.jwtHandler);
8347
- const response = await this.fetchFn(url, {
8348
- signal,
8349
- headers
8350
- });
8351
- if (response.status === 404) throw new AttachmentNotFound(hash);
8352
- if (!response.ok) throw new Error(`Attachment fetch failed: ${response.status} ${response.statusText}`);
8353
- if (!response.body) throw new Error("Response body is null");
8354
- return {
8355
- header: buildHeader(hash, parseMetadata(response)),
8356
- body: response.body
8357
- };
8358
- }
8359
- };
8360
- function buildHeader(hash, metadata) {
8361
- return {
8362
- hash,
8363
- mimeType: metadata.mimeType,
8364
- fileName: metadata.fileName,
8365
- sizeBytes: metadata.sizeBytes,
8366
- extension: metadata.extension,
8367
- status: "available",
8368
- source: "sync",
8369
- createdAtUtc: metadata.createdAtUtc,
8370
- lastAccessedAtUtc: metadata.lastAccessedAtUtc ?? metadata.createdAtUtc
8371
- };
8520
+ /**
8521
+ * Renders a single custom control when it belongs in the requested position.
8522
+ */
8523
+ function renderCustomControl(control, pos, document) {
8524
+ if (!isControlInPosition(control, pos)) return null;
8525
+ const Component = control.component;
8526
+ return /* @__PURE__ */ jsx(Component, { document });
8372
8527
  }
8373
- function createRemoteAttachmentService(config) {
8374
- const reservations = new RemoteReservationStore(config);
8375
- const uploadFactory = new RemoteAttachmentUploadFactory(config);
8376
- return new AttachmentService(new RemoteAttachmentStore(config), reservations, uploadFactory);
8528
+ /**
8529
+ * Renders a list of custom controls for the requested position.
8530
+ *
8531
+ * Returns `null` when no controls in the list belong in that position.
8532
+ */
8533
+ function renderCustomControlList(controls, pos, document) {
8534
+ const controlsInPosition = n$1(controls, (control) => isControlInPosition(control, pos));
8535
+ if (!t$8(controlsInPosition, 1)) return null;
8536
+ return /* @__PURE__ */ jsx(Fragment$1, { children: t$2(controlsInPosition, ({ component: Component, key }) => /* @__PURE__ */ jsx(Component, { document }, key)) });
8537
+ }
8538
+ /**
8539
+ * Renders a document toolbar.
8540
+ *
8541
+ * By default, the toolbar renders the built-in document controls grouped into
8542
+ * toolbar slots. The controls operate on the provided `document`, or on the
8543
+ * currently selected document when no document is provided.
8544
+ *
8545
+ * Use `enabledControls` and `disabledControls` to control which built-in
8546
+ * controls are shown. Use `componentOverrides` to replace individual built-in
8547
+ * controls while keeping the default toolbar layout. Use `customControls` to
8548
+ * insert additional controls before or after the built-in controls in a slot.
8549
+ *
8550
+ * To take over the toolbar contents completely, pass `children`.
8551
+ */
8552
+ function DocumentToolbar(props) {
8553
+ const [selectedDocument] = useSelectedDocumentSafe();
8554
+ const { toolbarClassName, document = selectedDocument, toolbarContainer: Container = ToolbarContainer } = props;
8555
+ if ("children" in props) return /* @__PURE__ */ jsx(Container, {
8556
+ className: toolbarClassName,
8557
+ children: props.children
8558
+ });
8559
+ return /* @__PURE__ */ jsx(Container, {
8560
+ className: toolbarClassName,
8561
+ children: t$2(controlSlots, (slot) => /* @__PURE__ */ createElement(ControlsContainerSlot, {
8562
+ ...props,
8563
+ document,
8564
+ slot,
8565
+ key: slot
8566
+ }))
8567
+ });
8568
+ }
8569
+ /**
8570
+ * Renders one toolbar controls slot.
8571
+ *
8572
+ * Custom controls with position `"start"` are rendered before the built-in
8573
+ * controls for the slot. Custom controls with position `"end"` are rendered
8574
+ * after them.
8575
+ */
8576
+ function ControlsContainerSlot(props) {
8577
+ const { slot, document, controlsContainerClassName, enabledControls, disabledControls, componentOverrides, customControls, controlsContainer: ControlsContainer = ToolbarControlsContainer } = props;
8578
+ const renderToolbarControls = makeToolbarControlsRenderer({
8579
+ document,
8580
+ enabledControls,
8581
+ disabledControls,
8582
+ componentOverrides
8583
+ });
8584
+ const renderCustomControls = makeCustomControlsRenderer({
8585
+ document,
8586
+ customControls
8587
+ });
8588
+ return /* @__PURE__ */ jsxs(ControlsContainer, {
8589
+ className: controlsContainerClassName,
8590
+ children: [
8591
+ renderCustomControls(slot, "start"),
8592
+ renderToolbarControls(slot),
8593
+ renderCustomControls(slot, "end")
8594
+ ]
8595
+ });
8377
8596
  }
8597
+ const normalNodeOptions = [
8598
+ "DUPLICATE",
8599
+ "RENAME",
8600
+ "DELETE",
8601
+ "SETTINGS"
8602
+ ];
8603
+ const debugNodeOptions = [
8604
+ "ADD_TRIGGER",
8605
+ "REMOVE_TRIGGER",
8606
+ "ADD_INVALID_TRIGGER"
8607
+ ];
8608
+ [...normalNodeOptions, ...debugNodeOptions];
8378
8609
  //#endregion
8379
8610
  //#region editors/ph-clint-project-editor/editor.tsx
8380
8611
  const POWERHOUSE_LEVELS = [
@@ -10199,4 +10430,4 @@ function StatusBadge(props) {
10199
10430
  //#endregion
10200
10431
  export { Editor as default };
10201
10432
 
10202
- //# sourceMappingURL=editor-Bev0bZuJ.mjs.map
10433
+ //# sourceMappingURL=editor-BC9t7D3T.mjs.map