@templatical/core 0.35.0 → 0.37.0

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.
@@ -108,14 +108,14 @@ var AuthManager = class AuthManager {
108
108
  const response = await fetch(this.url, fetchOptions);
109
109
  if (!response.ok) throw new SdkError(`Token refresh failed: ${response.status}`, response.status);
110
110
  const data = await response.json();
111
- if (!data.token || !data.expires_at || !data.project_id || !data.tenant) throw new Error("Invalid token response: missing token, expires_at, project_id, or tenant");
111
+ if (!data.token || !data.expiresAt || !data.projectId || !data.tenant) throw new Error("Invalid token response: missing token, expiresAt, projectId, or tenant");
112
112
  this.accessToken = data.token;
113
- this.expiresAt = /* @__PURE__ */ new Date(data.expires_at * 1e3);
114
- this._projectId = data.project_id;
113
+ this.expiresAt = /* @__PURE__ */ new Date(data.expiresAt * 1e3);
114
+ this._projectId = data.projectId;
115
115
  this._tenantSlug = data.tenant;
116
- if (data.test_email?.allowed_emails && data.test_email?.signature) this._testEmailConfig = {
117
- allowedEmails: data.test_email.allowed_emails,
118
- signature: data.test_email.signature
116
+ if (data.testEmail?.allowedEmails && data.testEmail?.signature) this._testEmailConfig = {
117
+ allowedEmails: data.testEmail.allowedEmails,
118
+ signature: data.testEmail.signature
119
119
  };
120
120
  else this._testEmailConfig = null;
121
121
  if (data.user?.id && data.user?.name && data.user?.signature) this._userConfig = {
@@ -440,12 +440,184 @@ var ApiClient = class {
440
440
  }
441
441
  };
442
442
  //#endregion
443
+ //#region src/cloud/media-api.ts
444
+ /**
445
+ * Cloud's HTTP client for media. Auth and project/tenant scoping come from
446
+ * {@link AuthManager}. The BYO contract lives on
447
+ * `createCloudMediaProvider`, which maps this client's rows.
448
+ */
449
+ var MediaApiClient = class {
450
+ authManager;
451
+ constructor(authManager) {
452
+ this.authManager = authManager;
453
+ }
454
+ get projectId() {
455
+ return this.authManager.projectId;
456
+ }
457
+ get tenantSlug() {
458
+ return this.authManager.tenantSlug;
459
+ }
460
+ get baseParams() {
461
+ return {
462
+ project: this.projectId,
463
+ tenant: this.tenantSlug
464
+ };
465
+ }
466
+ async request(path, options = {}) {
467
+ const response = await this.authManager.authenticatedFetch(path, {
468
+ ...options,
469
+ headers: {
470
+ "Content-Type": "application/json",
471
+ Accept: "application/json",
472
+ ...options.headers
473
+ }
474
+ });
475
+ if (!response.ok) {
476
+ const error = await response.json().catch(() => ({ message: `HTTP error ${response.status}` }));
477
+ throw new Error(error.message, { cause: error });
478
+ }
479
+ if (response.status === 204) return;
480
+ return (await response.json()).data;
481
+ }
482
+ async browseMedia(params) {
483
+ const query = new URLSearchParams();
484
+ if (params.folderId) query.set("folderId", params.folderId);
485
+ if (params.search) query.set("search", params.search);
486
+ if (params.category) query.set("category", params.category);
487
+ if (params.sort) query.set("sort", params.sort);
488
+ if (params.cursor) query.set("cursor", params.cursor);
489
+ const queryString = query.toString();
490
+ const url = `${buildUrl(API_ROUTES["media.browse"], this.baseParams)}${queryString ? `?${queryString}` : ""}`;
491
+ const response = await this.authManager.authenticatedFetch(url, { headers: { Accept: "application/json" } });
492
+ if (!response.ok) {
493
+ const error = await response.json().catch(() => ({ message: `HTTP error ${response.status}` }));
494
+ throw new Error(error.message, { cause: error });
495
+ }
496
+ return response.json();
497
+ }
498
+ async uploadMedia(file, folderId) {
499
+ const formData = new FormData();
500
+ formData.append("file", file);
501
+ if (folderId) formData.append("folderId", folderId);
502
+ const url = buildUrl(API_ROUTES["media.upload"], this.baseParams);
503
+ const response = await this.authManager.authenticatedFetch(url, {
504
+ method: "POST",
505
+ headers: { Accept: "application/json" },
506
+ body: formData
507
+ });
508
+ if (!response.ok) {
509
+ const error = await response.json().catch(() => ({ message: `HTTP error ${response.status}` }));
510
+ throw new Error(error.message, { cause: error });
511
+ }
512
+ return (await response.json()).data;
513
+ }
514
+ async updateMedia(mediaId, filename, alt) {
515
+ const body = {};
516
+ if (filename !== void 0) body.filename = filename;
517
+ if (alt !== void 0) body.alt = alt;
518
+ return this.request(buildUrl(API_ROUTES["media.update"], {
519
+ ...this.baseParams,
520
+ media: mediaId
521
+ }), {
522
+ method: "PUT",
523
+ body: JSON.stringify(body)
524
+ });
525
+ }
526
+ async deleteMedia(ids) {
527
+ return this.request(buildUrl(API_ROUTES["media.delete"], this.baseParams), {
528
+ method: "POST",
529
+ body: JSON.stringify({ ids })
530
+ });
531
+ }
532
+ async moveMedia(ids, folderId) {
533
+ return this.request(buildUrl(API_ROUTES["media.move"], this.baseParams), {
534
+ method: "POST",
535
+ body: JSON.stringify({
536
+ ids,
537
+ folderId
538
+ })
539
+ });
540
+ }
541
+ async getMediaFolders() {
542
+ return this.request(buildUrl(API_ROUTES["folders.index"], this.baseParams));
543
+ }
544
+ async createMediaFolder(name, parentId) {
545
+ return this.request(buildUrl(API_ROUTES["folders.store"], this.baseParams), {
546
+ method: "POST",
547
+ body: JSON.stringify({
548
+ name,
549
+ parentId: parentId ?? null
550
+ })
551
+ });
552
+ }
553
+ async renameMediaFolder(folderId, name) {
554
+ return this.request(buildUrl(API_ROUTES["folders.update"], {
555
+ ...this.baseParams,
556
+ mediaFolder: folderId
557
+ }), {
558
+ method: "PUT",
559
+ body: JSON.stringify({ name })
560
+ });
561
+ }
562
+ async deleteMediaFolder(folderId) {
563
+ return this.request(buildUrl(API_ROUTES["folders.destroy"], {
564
+ ...this.baseParams,
565
+ mediaFolder: folderId
566
+ }), { method: "DELETE" });
567
+ }
568
+ async checkMediaUsage(ids) {
569
+ const response = await this.authManager.authenticatedFetch(buildUrl(API_ROUTES["media.checkUsage"], this.baseParams), {
570
+ method: "POST",
571
+ headers: {
572
+ "Content-Type": "application/json",
573
+ Accept: "application/json"
574
+ },
575
+ body: JSON.stringify({ ids })
576
+ });
577
+ if (!response.ok) {
578
+ const error = await response.json().catch(() => ({ message: `HTTP error ${response.status}` }));
579
+ throw new Error(error.message, { cause: error });
580
+ }
581
+ return response.json();
582
+ }
583
+ async getFrequentlyUsed() {
584
+ return this.request(buildUrl(API_ROUTES["media.frequentlyUsed"], this.baseParams));
585
+ }
586
+ async importFromUrl(url, folderId) {
587
+ return this.request(buildUrl(API_ROUTES["media.importFromUrl"], this.baseParams), {
588
+ method: "POST",
589
+ body: JSON.stringify({
590
+ url,
591
+ folderId: folderId ?? null
592
+ })
593
+ });
594
+ }
595
+ async replaceMedia(mediaId, file) {
596
+ const formData = new FormData();
597
+ formData.append("file", file);
598
+ const url = buildUrl(API_ROUTES["media.replace"], {
599
+ ...this.baseParams,
600
+ media: mediaId
601
+ });
602
+ const response = await this.authManager.authenticatedFetch(url, {
603
+ method: "POST",
604
+ headers: { Accept: "application/json" },
605
+ body: formData
606
+ });
607
+ if (!response.ok) {
608
+ const error = await response.json().catch(() => ({ message: `HTTP error ${response.status}` }));
609
+ throw new Error(error.message, { cause: error });
610
+ }
611
+ return (await response.json()).data;
612
+ }
613
+ };
614
+ //#endregion
443
615
  //#region src/cloud/websocket-client.ts
444
616
  function resolveWebSocketConfig(serverConfig) {
445
617
  return {
446
618
  host: serverConfig.host,
447
619
  port: serverConfig.port,
448
- appKey: serverConfig.app_key
620
+ appKey: serverConfig.appKey
449
621
  };
450
622
  }
451
623
  var WebSocketClient = class {
@@ -487,9 +659,9 @@ var WebSocketClient = class {
487
659
  Accept: "application/json"
488
660
  },
489
661
  params: {
490
- user_id: this.authManager.userConfig?.id ?? "",
491
- user_name: this.authManager.userConfig?.name ?? "",
492
- user_signature: this.authManager.userConfig?.signature ?? ""
662
+ userId: this.authManager.userConfig?.id ?? "",
663
+ userName: this.authManager.userConfig?.name ?? "",
664
+ userSignature: this.authManager.userConfig?.signature ?? ""
493
665
  }
494
666
  }
495
667
  });
@@ -525,25 +697,25 @@ var WebSocketClient = class {
525
697
  function handleOperation(editor, payload) {
526
698
  const { operation, data } = payload;
527
699
  switch (operation) {
528
- case "add_block":
529
- editor.addBlock(data.block, data.section_id, data.column_index, data.index);
700
+ case "addBlock":
701
+ editor.addBlock(data.block, data.sectionId, data.columnIndex, data.index);
530
702
  break;
531
- case "update_block":
532
- editor.updateBlock(data.block_id, data.updates);
703
+ case "updateBlock":
704
+ editor.updateBlock(data.blockId, data.updates);
533
705
  break;
534
- case "delete_block":
535
- editor.removeBlock(data.block_id);
706
+ case "deleteBlock":
707
+ editor.removeBlock(data.blockId);
536
708
  break;
537
- case "move_block":
538
- editor.moveBlock(data.block_id, data.index, data.section_id, data.column_index);
709
+ case "moveBlock":
710
+ editor.moveBlock(data.blockId, data.index, data.sectionId, data.columnIndex);
539
711
  break;
540
- case "update_settings":
712
+ case "updateSettings":
541
713
  editor.updateSettings(data.updates);
542
714
  break;
543
- case "set_content":
715
+ case "setContent":
544
716
  editor.setContent(data.content);
545
717
  break;
546
- case "update_block_style": editor.updateBlock(data.block_id, { styles: data.styles });
718
+ case "updateBlockStyle": editor.updateBlock(data.blockId, { styles: data.styles });
547
719
  }
548
720
  }
549
721
  //#endregion
@@ -595,12 +767,12 @@ function useAiChat(options) {
595
767
  });
596
768
  if (!response.ok) return;
597
769
  const data = await response.json();
598
- if (data.conversation_id) conversationId.value = data.conversation_id;
770
+ if (data.conversationId) conversationId.value = data.conversationId;
599
771
  if (Array.isArray(data.data) && data.data.length > 0) messages.value = data.data.map((msg) => ({
600
772
  id: msg.id,
601
773
  role: msg.role,
602
774
  content: msg.content,
603
- timestamp: new Date(msg.created_at).getTime()
775
+ timestamp: new Date(msg.createdAt).getTime()
604
776
  }));
605
777
  } catch {} finally {
606
778
  isLoadingHistory.value = false;
@@ -623,8 +795,8 @@ function useAiChat(options) {
623
795
  Accept: "text/event-stream"
624
796
  },
625
797
  body: JSON.stringify({
626
- current_content: currentContent,
627
- merge_tags: mergeTags.map((p) => ({
798
+ currentContent,
799
+ mergeTags: mergeTags.map((p) => ({
628
800
  label: p.label,
629
801
  value: p.value
630
802
  }))
@@ -691,17 +863,17 @@ function useAiChat(options) {
691
863
  },
692
864
  body: JSON.stringify({
693
865
  prompt,
694
- current_content: currentContent,
695
- merge_tags: mergeTags.map((p) => ({
866
+ currentContent,
867
+ mergeTags: mergeTags.map((p) => ({
696
868
  label: p.label,
697
869
  value: p.value
698
870
  })),
699
- conversation_id: conversationId.value
871
+ conversationId: conversationId.value
700
872
  })
701
873
  });
702
874
  if (!response.ok) {
703
875
  const errorData = await response.json().catch(() => null);
704
- if (response.status === 403) throw new Error("ai_generation_not_available");
876
+ if (response.status === 403) throw new Error("aiGenerationNotAvailable");
705
877
  throw new Error(errorData?.message || "Failed to generate template");
706
878
  }
707
879
  const reader = response.body?.getReader();
@@ -728,7 +900,7 @@ function useAiChat(options) {
728
900
  if (event.type === "text") updateMessage(assistantMsgId, { content: (messages.value.find((m) => m.id === assistantMsgId)?.content ?? "") + event.text });
729
901
  else if (event.type === "error") throw new Error(event.message || "Failed to generate template");
730
902
  else if (event.type === "done") {
731
- if (event.conversation_id) conversationId.value = event.conversation_id;
903
+ if (event.conversationId) conversationId.value = event.conversationId;
732
904
  updateMessage(assistantMsgId, { content: event.text });
733
905
  result = event.content ?? null;
734
906
  if (result) {
@@ -822,14 +994,14 @@ function useAiRewrite(options) {
822
994
  body: JSON.stringify({
823
995
  content,
824
996
  instruction,
825
- merge_tags: mergeTags.map((p) => ({
997
+ mergeTags: mergeTags.map((p) => ({
826
998
  label: p.label,
827
999
  value: p.value
828
1000
  }))
829
1001
  })
830
1002
  });
831
1003
  if (!response.ok) {
832
- if (response.status === 403) throw new Error("ai_generation_not_available");
1004
+ if (response.status === 403) throw new Error("aiGenerationNotAvailable");
833
1005
  const errorData = await response.json().catch(() => null);
834
1006
  throw new Error(errorData?.message || "Failed to rewrite text");
835
1007
  }
@@ -944,15 +1116,15 @@ function useTemplateScoring(options) {
944
1116
  Accept: "text/event-stream"
945
1117
  },
946
1118
  body: JSON.stringify({
947
- current_content: content,
948
- merge_tags: mergeTags.map((p) => ({
1119
+ currentContent: content,
1120
+ mergeTags: mergeTags.map((p) => ({
949
1121
  label: p.label,
950
1122
  value: p.value
951
1123
  }))
952
1124
  })
953
1125
  });
954
1126
  if (!response.ok) {
955
- if (response.status === 403) throw new Error("ai_generation_not_available");
1127
+ if (response.status === 403) throw new Error("aiGenerationNotAvailable");
956
1128
  const errorData = await response.json().catch(() => null);
957
1129
  throw new Error(errorData?.message || "Failed to score template");
958
1130
  }
@@ -1015,14 +1187,14 @@ function useTemplateScoring(options) {
1015
1187
  suggestion: finding.suggestion,
1016
1188
  category: finding.category
1017
1189
  },
1018
- merge_tags: mergeTags.map((p) => ({
1190
+ mergeTags: mergeTags.map((p) => ({
1019
1191
  label: p.label,
1020
1192
  value: p.value
1021
1193
  }))
1022
1194
  })
1023
1195
  });
1024
1196
  if (!response.ok) {
1025
- if (response.status === 403) throw new Error("ai_generation_not_available");
1197
+ if (response.status === 403) throw new Error("aiGenerationNotAvailable");
1026
1198
  const errorData = await response.json().catch(() => null);
1027
1199
  throw new Error(errorData?.message || "Failed to fix finding");
1028
1200
  }
@@ -1113,7 +1285,7 @@ function useDesignReference(options) {
1113
1285
  });
1114
1286
  if (!response.ok) {
1115
1287
  const errorData = await response.json().catch(() => null);
1116
- if (response.status === 403) throw new Error("ai_generation_not_available");
1288
+ if (response.status === 403) throw new Error("aiGenerationNotAvailable");
1117
1289
  throw new Error(errorData?.message || "Failed to generate template from design");
1118
1290
  }
1119
1291
  const reader = response.body?.getReader();
@@ -1169,28 +1341,23 @@ function useDesignReference(options) {
1169
1341
  /** The Pusher event Cloud's backend fans comment writes out on. */
1170
1342
  const BROADCAST_EVENT = "comment-broadcast";
1171
1343
  /**
1172
- * Cloud's wire shape → the contract shape. The whole job of this adapter, plus
1173
- * auth: snake_case to camelCase, and the two flat `author_*` / `resolved_by_*`
1174
- * pairs into {@link CommentAuthor} objects.
1344
+ * Cloud's HTTP row → {@link Comment}. Omits `updatedAt` when it equals
1345
+ * `createdAt` (Cloud stamps both on insert) so the panel does not mark
1346
+ * every comment "(edited)", and omits `resolvedBy` / empty `replies`
1347
+ * when they are absent.
1175
1348
  */
1176
1349
  function toComment(record) {
1177
1350
  const comment = {
1178
1351
  id: record.id,
1179
1352
  body: record.body,
1180
- author: {
1181
- id: record.author_identifier,
1182
- name: record.author_name
1183
- },
1184
- createdAt: record.created_at,
1185
- blockId: record.block_id,
1186
- parentId: record.parent_id,
1187
- resolvedAt: record.resolved_at
1188
- };
1189
- if (record.updated_at !== record.created_at) comment.updatedAt = record.updated_at;
1190
- if (record.resolved_by_identifier) comment.resolvedBy = {
1191
- id: record.resolved_by_identifier,
1192
- name: record.resolved_by_name ?? ""
1353
+ author: record.author,
1354
+ createdAt: record.createdAt,
1355
+ blockId: record.blockId,
1356
+ parentId: record.parentId,
1357
+ resolvedAt: record.resolvedAt
1193
1358
  };
1359
+ if (record.updatedAt !== record.createdAt) comment.updatedAt = record.updatedAt;
1360
+ if (record.resolvedBy) comment.resolvedBy = record.resolvedBy;
1194
1361
  if (record.replies?.length) comment.replies = record.replies.map(toComment);
1195
1362
  return comment;
1196
1363
  }
@@ -1206,7 +1373,7 @@ function toComment(record) {
1206
1373
  * Two things about the write payloads are Cloud's alone and stay on this side of
1207
1374
  * the seam:
1208
1375
  *
1209
- * - **The author is signed.** `user_id` / `user_name` / `user_signature` come from
1376
+ * - **The author is signed.** `userId` / `userName` / `userSignature` come from
1210
1377
  * the JWT, not from the editor's `user` config, so a browser cannot attribute a
1211
1378
  * comment to someone else. The editor's `user` key still gates the feature and
1212
1379
  * drives "you wrote this" in the UI; the two agree because `initCloud()` fills
@@ -1223,9 +1390,9 @@ function createCloudCommentsProvider(options) {
1223
1390
  const user = authManager.userConfig;
1224
1391
  if (!user) throw new Error("[Templatical] Cloud comments need a signed user in the auth token. The project's token endpoint must include a `user` claim.");
1225
1392
  return {
1226
- user_id: user.id,
1227
- user_name: user.name,
1228
- user_signature: user.signature
1393
+ userId: user.id,
1394
+ userName: user.name,
1395
+ userSignature: user.signature
1229
1396
  };
1230
1397
  }
1231
1398
  function socketHeaders() {
@@ -1239,8 +1406,8 @@ function createCloudCommentsProvider(options) {
1239
1406
  async create(templateId, input) {
1240
1407
  return toComment(await api.createComment(templateId, {
1241
1408
  body: input.body,
1242
- block_id: input.blockId,
1243
- parent_id: input.parentId,
1409
+ blockId: input.blockId,
1410
+ parentId: input.parentId,
1244
1411
  ...userPayload()
1245
1412
  }, socketHeaders()));
1246
1413
  },
@@ -1261,22 +1428,22 @@ function createCloudCommentsProvider(options) {
1261
1428
  function handle(payload) {
1262
1429
  const comment = toComment(payload.comment);
1263
1430
  switch (payload.action) {
1264
- case "comment_created":
1431
+ case "commentCreated":
1265
1432
  onChange({
1266
1433
  type: "created",
1267
1434
  comment
1268
1435
  });
1269
1436
  break;
1270
- case "comment_deleted":
1437
+ case "commentDeleted":
1271
1438
  onChange({
1272
1439
  type: "deleted",
1273
1440
  commentId: comment.id,
1274
1441
  parentId: comment.parentId
1275
1442
  });
1276
1443
  break;
1277
- case "comment_updated":
1278
- case "comment_resolved":
1279
- case "comment_unresolved": onChange({
1444
+ case "commentUpdated":
1445
+ case "commentResolved":
1446
+ case "commentUnresolved": onChange({
1280
1447
  type: "updated",
1281
1448
  comment
1282
1449
  });
@@ -1298,8 +1465,8 @@ function createCloudCommentsProvider(options) {
1298
1465
  const COLLAB_EVENTS = [
1299
1466
  "pusher:member_added",
1300
1467
  "pusher:member_removed",
1301
- "client-block_locked",
1302
- "client-block_unlocked",
1468
+ "client-blockLocked",
1469
+ "client-blockUnlocked",
1303
1470
  "client-operation",
1304
1471
  "mcp-operation"
1305
1472
  ];
@@ -1388,14 +1555,14 @@ function useCollaboration(options) {
1388
1555
  }
1389
1556
  function broadcastBlockLocked(blockId) {
1390
1557
  if (!channel.value) return;
1391
- channel.value.trigger("client-block_locked", {
1558
+ channel.value.trigger("client-blockLocked", {
1392
1559
  blockId,
1393
1560
  userId: myUserId.value
1394
1561
  });
1395
1562
  }
1396
1563
  function broadcastBlockUnlocked(blockId) {
1397
1564
  if (!channel.value) return;
1398
- channel.value.trigger("client-block_unlocked", { blockId });
1565
+ channel.value.trigger("client-blockUnlocked", { blockId });
1399
1566
  }
1400
1567
  watch(() => editor.state.selectedBlockId, (newBlockId, oldBlockId) => {
1401
1568
  if (isProcessingRemoteOperation) return;
@@ -1423,7 +1590,7 @@ function useCollaboration(options) {
1423
1590
  removeCollaborator(member.id);
1424
1591
  if (collaborator) options.onCollaboratorLeft?.(collaborator);
1425
1592
  });
1426
- newChannel.bind("client-block_locked", (data) => {
1593
+ newChannel.bind("client-blockLocked", (data) => {
1427
1594
  handleBlockLocked(data);
1428
1595
  const collaborator = collaborators.value.find((c) => c.id === data.userId);
1429
1596
  if (collaborator) options.onBlockLocked?.({
@@ -1431,7 +1598,7 @@ function useCollaboration(options) {
1431
1598
  collaborator
1432
1599
  });
1433
1600
  });
1434
- newChannel.bind("client-block_unlocked", (data) => {
1601
+ newChannel.bind("client-blockUnlocked", (data) => {
1435
1602
  const holder = lockedBlocks.value.get(data.blockId);
1436
1603
  handleBlockUnlocked(data);
1437
1604
  if (holder) options.onBlockUnlocked?.({
@@ -1475,11 +1642,11 @@ function useCollaborationBroadcast(editor, collaboration) {
1475
1642
  editor.addBlock = (block, targetSectionId, columnIndex, index) => {
1476
1643
  originalAddBlock(block, targetSectionId, columnIndex, index);
1477
1644
  collaboration._broadcastOperation({
1478
- operation: "add_block",
1645
+ operation: "addBlock",
1479
1646
  data: {
1480
1647
  block,
1481
- section_id: targetSectionId,
1482
- column_index: columnIndex,
1648
+ sectionId: targetSectionId,
1649
+ columnIndex,
1483
1650
  index
1484
1651
  },
1485
1652
  timestamp: Date.now()
@@ -1488,9 +1655,9 @@ function useCollaborationBroadcast(editor, collaboration) {
1488
1655
  editor.updateBlock = (blockId, updates) => {
1489
1656
  originalUpdateBlock(blockId, updates);
1490
1657
  collaboration._broadcastOperation({
1491
- operation: "update_block",
1658
+ operation: "updateBlock",
1492
1659
  data: {
1493
- block_id: blockId,
1660
+ blockId,
1494
1661
  updates
1495
1662
  },
1496
1663
  timestamp: Date.now()
@@ -1499,20 +1666,20 @@ function useCollaborationBroadcast(editor, collaboration) {
1499
1666
  editor.removeBlock = (blockId) => {
1500
1667
  originalRemoveBlock(blockId);
1501
1668
  collaboration._broadcastOperation({
1502
- operation: "delete_block",
1503
- data: { block_id: blockId },
1669
+ operation: "deleteBlock",
1670
+ data: { blockId },
1504
1671
  timestamp: Date.now()
1505
1672
  });
1506
1673
  };
1507
1674
  editor.moveBlock = (blockId, newIndex, targetSectionId, columnIndex) => {
1508
1675
  originalMoveBlock(blockId, newIndex, targetSectionId, columnIndex);
1509
1676
  collaboration._broadcastOperation({
1510
- operation: "move_block",
1677
+ operation: "moveBlock",
1511
1678
  data: {
1512
- block_id: blockId,
1679
+ blockId,
1513
1680
  index: newIndex,
1514
- section_id: targetSectionId,
1515
- column_index: columnIndex
1681
+ sectionId: targetSectionId,
1682
+ columnIndex
1516
1683
  },
1517
1684
  timestamp: Date.now()
1518
1685
  });
@@ -1520,7 +1687,7 @@ function useCollaborationBroadcast(editor, collaboration) {
1520
1687
  editor.updateSettings = (updates) => {
1521
1688
  originalUpdateSettings(updates);
1522
1689
  collaboration._broadcastOperation({
1523
- operation: "update_settings",
1690
+ operation: "updateSettings",
1524
1691
  data: { updates },
1525
1692
  timestamp: Date.now()
1526
1693
  });
@@ -1528,7 +1695,7 @@ function useCollaborationBroadcast(editor, collaboration) {
1528
1695
  editor.setContent = (content, markDirty) => {
1529
1696
  originalSetContent(content, markDirty);
1530
1697
  collaboration._broadcastOperation({
1531
- operation: "set_content",
1698
+ operation: "setContent",
1532
1699
  data: { content },
1533
1700
  timestamp: Date.now()
1534
1701
  });
@@ -1621,6 +1788,152 @@ function createCloudSavedBlocksProvider(authManager) {
1621
1788
  };
1622
1789
  }
1623
1790
  //#endregion
1791
+ //#region src/cloud/media-provider.ts
1792
+ /**
1793
+ * Cloud's HTTP row → {@link MediaAsset}. Omits null optionals so
1794
+ * `thumbnailUrl` / `width` / `height` are absent rather than `null` —
1795
+ * the grid falls back to `url` when there is no thumbnail. Confirm
1796
+ * always inserts `url`.
1797
+ */
1798
+ function toAsset(item) {
1799
+ const asset = {
1800
+ id: item.id,
1801
+ url: item.url,
1802
+ filename: item.filename,
1803
+ alt: item.alt,
1804
+ mimeType: item.mimeType,
1805
+ folderId: item.folderId,
1806
+ createdAt: item.createdAt,
1807
+ updatedAt: item.updatedAt,
1808
+ size: item.size
1809
+ };
1810
+ if (item.width != null) asset.width = item.width;
1811
+ if (item.height != null) asset.height = item.height;
1812
+ if (item.thumbnailUrl != null) asset.thumbnailUrl = item.thumbnailUrl;
1813
+ return asset;
1814
+ }
1815
+ function toFolder(folder, inheritedParentId = null) {
1816
+ return {
1817
+ id: folder.id,
1818
+ name: folder.name,
1819
+ parentId: folder.parentId !== void 0 ? folder.parentId : inheritedParentId
1820
+ };
1821
+ }
1822
+ /**
1823
+ * Flatten a Cloud folder payload into the contract's flat list.
1824
+ *
1825
+ * The index endpoint may return a tree (`children`) or already-flat
1826
+ * `parentId` rows. Depth-first so a parent is always listed before its
1827
+ * descendants. Already-flat rows just copy `parentId`.
1828
+ */
1829
+ function flattenFolders(nodes, inheritedParentId = null) {
1830
+ const out = [];
1831
+ for (const node of nodes) {
1832
+ out.push(toFolder(node, inheritedParentId));
1833
+ if (node.children?.length) out.push(...flattenFolders(node.children, node.id));
1834
+ }
1835
+ return out;
1836
+ }
1837
+ function mimeTypesFromPlan(plan) {
1838
+ const categories = plan?.media?.categories;
1839
+ if (!categories) return;
1840
+ const mapped = {};
1841
+ for (const [key, value] of Object.entries(categories)) mapped[key] = value.mimeTypes;
1842
+ return mapped;
1843
+ }
1844
+ /**
1845
+ * Cloud-backed {@link MediaProvider} — the Templatical Cloud adapter for the
1846
+ * same DAM contract consumers implement themselves.
1847
+ *
1848
+ * Every method is a function: Cloud's media storage is what the plan pays
1849
+ * for, so there is no Cloud tier that can list but not upload. A consumer
1850
+ * who wants a read-only gallery supplies their own provider with the
1851
+ * mutations set to `false`.
1852
+ *
1853
+ * **Ignores `templateId`.** Cloud's library is project-scoped; a CMS that
1854
+ * scopes a gallery per template reads that param in its own `list`.
1855
+ *
1856
+ * **`storage` / `maxFileSize` / `mimeTypes` are live.** They read
1857
+ * `getPlanConfig()` at call time, because plan config arrives after
1858
+ * construction. Snapshotting at setup would pin `storage()` to `null` and
1859
+ * hide the quota ring for the whole session. A spread of this object
1860
+ * freezes the getters — `initCloud()` must `Object.assign`, never spread.
1861
+ */
1862
+ function createCloudMediaProvider(authManager, getPlanConfig) {
1863
+ const api = new MediaApiClient(authManager);
1864
+ return {
1865
+ async list(params) {
1866
+ const response = await api.browseMedia({
1867
+ search: params?.search,
1868
+ cursor: params?.cursor,
1869
+ folderId: params?.folderId,
1870
+ category: params?.category
1871
+ });
1872
+ const page = { items: response.data.map(toAsset) };
1873
+ if (response.meta.nextCursor) page.nextCursor = response.meta.nextCursor;
1874
+ return page;
1875
+ },
1876
+ async create(input) {
1877
+ return toAsset(await api.uploadMedia(input.file, input.folderId));
1878
+ },
1879
+ async update(id, patch) {
1880
+ return toAsset(await api.updateMedia(id, patch.filename, patch.alt));
1881
+ },
1882
+ delete(ids) {
1883
+ return api.deleteMedia(ids);
1884
+ },
1885
+ folders: {
1886
+ async list() {
1887
+ return flattenFolders(await api.getMediaFolders());
1888
+ },
1889
+ async create(input) {
1890
+ return toFolder(await api.createMediaFolder(input.name, input.parentId));
1891
+ },
1892
+ async update(id, patch) {
1893
+ return toFolder(await api.renameMediaFolder(id, patch.name));
1894
+ },
1895
+ delete(id) {
1896
+ return api.deleteMediaFolder(id);
1897
+ },
1898
+ async move(ids, folderId) {
1899
+ return (await api.moveMedia(ids, folderId)).map(toAsset);
1900
+ }
1901
+ },
1902
+ async replace(id, file) {
1903
+ return toAsset(await api.replaceMedia(id, file));
1904
+ },
1905
+ async importFromUrl(url, folderId) {
1906
+ return toAsset(await api.importFromUrl(url, folderId));
1907
+ },
1908
+ async checkUsage(ids) {
1909
+ const response = await api.checkMediaUsage(ids);
1910
+ const mapped = {};
1911
+ for (const [id, info] of Object.entries(response.data)) mapped[id] = {
1912
+ templateCount: info.templateCount,
1913
+ templateNames: info.templateNames
1914
+ };
1915
+ return mapped;
1916
+ },
1917
+ async frequentlyUsed() {
1918
+ return (await api.getFrequentlyUsed()).map(toAsset);
1919
+ },
1920
+ async storage() {
1921
+ return getPlanConfig?.()?.storage ?? null;
1922
+ },
1923
+ /**
1924
+ * A getter, not a snapshot: plan config arrives after construction.
1925
+ * The editor reads this inside a computed; capturing it at setup
1926
+ * would pin the client pre-check to `undefined`.
1927
+ */
1928
+ get maxFileSize() {
1929
+ return getPlanConfig?.()?.media?.maxFileSize;
1930
+ },
1931
+ get mimeTypes() {
1932
+ return mimeTypesFromPlan(getPlanConfig?.() ?? null);
1933
+ }
1934
+ };
1935
+ }
1936
+ //#endregion
1624
1937
  //#region src/cloud/templates-provider.ts
1625
1938
  /**
1626
1939
  * How often a save may also record an automatic version. Cloud's storage, Cloud's
@@ -1786,8 +2099,8 @@ function createCloudRenderProvider(options) {
1786
2099
  //#endregion
1787
2100
  //#region src/cloud/version-history-provider.ts
1788
2101
  /**
1789
- * Cloud's wire shape → the contract shape. The only real work this adapter does:
1790
- * snake_case to camelCase, and `is_autosave` to `isAutomatic`.
2102
+ * Cloud's HTTP row → {@link TemplateVersion}. Copies the contract fields
2103
+ * and drops `templateId`.
1791
2104
  *
1792
2105
  * `content` is carried through deliberately — Cloud's index endpoint already
1793
2106
  * returns it, so every listed version arrives hydrated and the editor never
@@ -1799,8 +2112,8 @@ function createCloudRenderProvider(options) {
1799
2112
  function toVersion(record) {
1800
2113
  return {
1801
2114
  id: record.id,
1802
- createdAt: record.created_at,
1803
- isAutomatic: record.is_autosave,
2115
+ createdAt: record.createdAt,
2116
+ isAutomatic: record.isAutomatic,
1804
2117
  content: record.content
1805
2118
  };
1806
2119
  }
@@ -1894,7 +2207,7 @@ function createCloudTestEmailProvider(options) {
1894
2207
  await api.sendTestEmail(templateId, {
1895
2208
  recipient,
1896
2209
  html,
1897
- allowed_emails: options.allowedEmails.value,
2210
+ allowedEmails: options.allowedEmails.value,
1898
2211
  signature
1899
2212
  });
1900
2213
  }
@@ -1991,7 +2304,7 @@ async function checkApiAndAuth(url, authManager) {
1991
2304
  }
1992
2305
  }
1993
2306
  async function checkWebSocket(wsConfig) {
1994
- if (!wsConfig?.host || !wsConfig?.app_key) return {
2307
+ if (!wsConfig?.host || !wsConfig?.appKey) return {
1995
2308
  ok: false,
1996
2309
  error: "WebSocket configuration not available"
1997
2310
  };
@@ -1999,7 +2312,7 @@ async function checkWebSocket(wsConfig) {
1999
2312
  ok: false,
2000
2313
  error: "WebSocket not supported in this environment"
2001
2314
  };
2002
- const url = `${wsConfig.port === 443 ? "wss" : "ws"}://${wsConfig.host}:${wsConfig.port}/app/${wsConfig.app_key}?protocol=7&client=js&version=8.4.0-rc2&flash=false`;
2315
+ const url = `${wsConfig.port === 443 ? "wss" : "ws"}://${wsConfig.host}:${wsConfig.port}/app/${wsConfig.appKey}?protocol=7&client=js&version=8.4.0-rc2&flash=false`;
2003
2316
  return new Promise((resolve) => {
2004
2317
  let ws = null;
2005
2318
  const timeout = setTimeout(() => {
@@ -2056,6 +2369,6 @@ function useMcpListener(options) {
2056
2369
  });
2057
2370
  }
2058
2371
  //#endregion
2059
- export { API_ROUTES, ApiClient, AuthManager, WebSocketClient, buildUrl, createCloudCommentsProvider, createCloudRenderProvider, createCloudSavedBlocksProvider, createCloudTemplatesProvider, createCloudTestEmailProvider, createCloudVersionHistoryProvider, createSdkAuthManager, handleOperation, isFatalAuthError, performHealthCheck, resolveExportFonts, resolveWebSocketConfig, useAiChat, useAiConfig, useAiRewrite, useCollaboration, useCollaborationBroadcast, useDesignReference, useExport, useMcpListener, usePlanConfig, useTemplateScoring, useTestEmail, useWebSocket };
2372
+ export { API_ROUTES, ApiClient, AuthManager, MediaApiClient, WebSocketClient, buildUrl, createCloudCommentsProvider, createCloudMediaProvider, createCloudRenderProvider, createCloudSavedBlocksProvider, createCloudTemplatesProvider, createCloudTestEmailProvider, createCloudVersionHistoryProvider, createSdkAuthManager, handleOperation, isFatalAuthError, performHealthCheck, resolveExportFonts, resolveWebSocketConfig, useAiChat, useAiConfig, useAiRewrite, useCollaboration, useCollaborationBroadcast, useDesignReference, useExport, useMcpListener, usePlanConfig, useTemplateScoring, useTestEmail, useWebSocket };
2060
2373
 
2061
2374
  //# sourceMappingURL=index.js.map