@templatical/core 0.48.0 → 0.50.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.
@@ -1,5 +1,5 @@
1
1
  import { SdkError } from "@templatical/types";
2
- import { computed, onScopeDispose, ref, watch } from "vue";
2
+ import { computed, ref, watch } from "vue";
3
3
  //#region src/cloud/auth.ts
4
4
  /**
5
5
  * Whether a token-refresh failure is worth interrupting the user over.
@@ -178,7 +178,6 @@ function buildUrl(template, params) {
178
178
  }
179
179
  const BASE = "/api/v1/projects/{project}/tenants/{tenant}";
180
180
  const TEMPLATE = `${BASE}/templates/{template}`;
181
- const AI = `${TEMPLATE}/ai`;
182
181
  const MEDIA = `${BASE}/media`;
183
182
  const FOLDERS = `${MEDIA}/folders`;
184
183
  const MODULES = `${BASE}/saved-modules`;
@@ -202,13 +201,6 @@ const API_ROUTES = {
202
201
  "comments.update": `${TEMPLATE}/comments/{comment}`,
203
202
  "comments.destroy": `${TEMPLATE}/comments/{comment}`,
204
203
  "comments.resolve": `${TEMPLATE}/comments/{comment}/resolve`,
205
- "ai.generate": `${AI}/generate`,
206
- "ai.conversationMessages": `${AI}/conversation-messages`,
207
- "ai.suggestions": `${AI}/suggestions`,
208
- "ai.rewriteText": `${AI}/rewrite-text`,
209
- "ai.score": `${AI}/score`,
210
- "ai.fixFinding": `${AI}/fix-finding`,
211
- "ai.generateFromDesign": `${AI}/generate-from-design`,
212
204
  "media.upload": `${MEDIA}/upload`,
213
205
  "media.browse": `${MEDIA}/browse`,
214
206
  "media.delete": `${MEDIA}/delete`,
@@ -693,650 +685,6 @@ var WebSocketClient = class {
693
685
  }
694
686
  };
695
687
  //#endregion
696
- //#region src/cloud/mcp-operation-handler.ts
697
- function handleOperation(editor, payload) {
698
- const { operation, data } = payload;
699
- switch (operation) {
700
- case "addBlock":
701
- editor.addBlock(data.block, data.sectionId, data.columnIndex, data.index);
702
- break;
703
- case "updateBlock":
704
- editor.updateBlock(data.blockId, data.updates);
705
- break;
706
- case "deleteBlock":
707
- editor.removeBlock(data.blockId);
708
- break;
709
- case "moveBlock":
710
- editor.moveBlock(data.blockId, data.index, data.sectionId, data.columnIndex);
711
- break;
712
- case "updateSettings":
713
- editor.updateSettings(data.updates);
714
- break;
715
- case "setContent":
716
- editor.setContent(data.content);
717
- break;
718
- case "updateBlockStyle": editor.updateBlock(data.blockId, { styles: data.styles });
719
- }
720
- }
721
- //#endregion
722
- //#region src/cloud/ai-chat.ts
723
- let messageIdCounter = 0;
724
- function generateMessageId() {
725
- return `msg_${Date.now()}_${++messageIdCounter}`;
726
- }
727
- function useAiChat(options) {
728
- const { authManager, getTemplateId, onApply, onError } = options;
729
- const messages = ref([]);
730
- const isGenerating = ref(false);
731
- const isLoadingHistory = ref(false);
732
- const error = ref(null);
733
- const failedPrompt = ref(null);
734
- const conversationId = ref(null);
735
- const lastApplyMessageId = ref(null);
736
- const lastPreviousContent = ref(null);
737
- const lastAppliedContent = ref(null);
738
- const isLastChangeReverted = ref(false);
739
- const suggestions = ref([]);
740
- const isLoadingSuggestions = ref(false);
741
- function updateMessage(msgId, updates) {
742
- const idx = messages.value.findIndex((m) => m.id === msgId);
743
- if (idx === -1) return;
744
- const updated = {
745
- ...messages.value[idx],
746
- ...updates
747
- };
748
- messages.value = [
749
- ...messages.value.slice(0, idx),
750
- updated,
751
- ...messages.value.slice(idx + 1)
752
- ];
753
- }
754
- async function loadConversation() {
755
- const templateId = getTemplateId();
756
- if (!templateId) return;
757
- isLoadingHistory.value = true;
758
- try {
759
- const url = buildUrl(API_ROUTES["ai.conversationMessages"], {
760
- project: authManager.projectId,
761
- tenant: authManager.tenantSlug,
762
- template: templateId
763
- });
764
- const response = await authManager.authenticatedFetch(url, {
765
- method: "GET",
766
- headers: { Accept: "application/json" }
767
- });
768
- if (!response.ok) return;
769
- const data = await response.json();
770
- if (data.conversationId) conversationId.value = data.conversationId;
771
- if (Array.isArray(data.data) && data.data.length > 0) messages.value = data.data.map((msg) => ({
772
- id: msg.id,
773
- role: msg.role,
774
- content: msg.content,
775
- timestamp: new Date(msg.createdAt).getTime()
776
- }));
777
- } catch {} finally {
778
- isLoadingHistory.value = false;
779
- }
780
- }
781
- async function loadSuggestions(currentContent, mergeTags) {
782
- const templateId = getTemplateId();
783
- if (!templateId) return;
784
- isLoadingSuggestions.value = true;
785
- try {
786
- const url = buildUrl(API_ROUTES["ai.suggestions"], {
787
- project: authManager.projectId,
788
- tenant: authManager.tenantSlug,
789
- template: templateId
790
- });
791
- const response = await authManager.authenticatedFetch(url, {
792
- method: "POST",
793
- headers: {
794
- "Content-Type": "application/json",
795
- Accept: "text/event-stream"
796
- },
797
- body: JSON.stringify({
798
- currentContent,
799
- mergeTags: mergeTags.map((p) => ({
800
- label: p.label,
801
- value: p.value
802
- }))
803
- })
804
- });
805
- if (!response.ok) return;
806
- const reader = response.body?.getReader();
807
- if (!reader) return;
808
- const decoder = new TextDecoder();
809
- let buffer = "";
810
- while (true) {
811
- const { done, value } = await reader.read();
812
- if (done) break;
813
- buffer += decoder.decode(value, { stream: true });
814
- const lines = buffer.split("\n");
815
- buffer = lines.pop() ?? "";
816
- for (const line of lines) {
817
- if (!line.startsWith("data: ")) continue;
818
- let event;
819
- try {
820
- event = JSON.parse(line.slice(6));
821
- } catch {
822
- continue;
823
- }
824
- if (event.type === "done" && Array.isArray(event.suggestions)) suggestions.value = event.suggestions.slice(0, 3);
825
- }
826
- }
827
- } catch {} finally {
828
- isLoadingSuggestions.value = false;
829
- }
830
- }
831
- async function sendPrompt(prompt, currentContent, mergeTags) {
832
- const templateId = getTemplateId();
833
- if (!templateId) throw new Error("Template must be saved before using AI generation");
834
- isGenerating.value = true;
835
- error.value = null;
836
- failedPrompt.value = null;
837
- suggestions.value = [];
838
- const userMsgId = generateMessageId();
839
- messages.value = [...messages.value, {
840
- id: userMsgId,
841
- role: "user",
842
- content: prompt,
843
- timestamp: Date.now()
844
- }];
845
- const assistantMsgId = generateMessageId();
846
- messages.value = [...messages.value, {
847
- id: assistantMsgId,
848
- role: "assistant",
849
- content: "",
850
- timestamp: Date.now()
851
- }];
852
- try {
853
- const url = buildUrl(API_ROUTES["ai.generate"], {
854
- project: authManager.projectId,
855
- tenant: authManager.tenantSlug,
856
- template: templateId
857
- });
858
- const response = await authManager.authenticatedFetch(url, {
859
- method: "POST",
860
- headers: {
861
- "Content-Type": "application/json",
862
- Accept: "text/event-stream"
863
- },
864
- body: JSON.stringify({
865
- prompt,
866
- currentContent,
867
- mergeTags: mergeTags.map((p) => ({
868
- label: p.label,
869
- value: p.value
870
- })),
871
- conversationId: conversationId.value
872
- })
873
- });
874
- if (!response.ok) {
875
- const errorData = await response.json().catch(() => null);
876
- if (response.status === 403) throw new Error("aiGenerationNotAvailable");
877
- throw new Error(errorData?.message || "Failed to generate template");
878
- }
879
- const reader = response.body?.getReader();
880
- if (!reader) throw new Error("Failed to read stream");
881
- const decoder = new TextDecoder();
882
- let buffer = "";
883
- let result = null;
884
- try {
885
- while (true) {
886
- const { done, value } = await reader.read();
887
- if (done) break;
888
- buffer += decoder.decode(value, { stream: true });
889
- const lines = buffer.split("\n");
890
- buffer = lines.pop() ?? "";
891
- for (const line of lines) {
892
- if (!line.startsWith("data: ")) continue;
893
- const jsonStr = line.slice(6);
894
- let event;
895
- try {
896
- event = JSON.parse(jsonStr);
897
- } catch {
898
- continue;
899
- }
900
- if (event.type === "text") updateMessage(assistantMsgId, { content: (messages.value.find((m) => m.id === assistantMsgId)?.content ?? "") + event.text });
901
- else if (event.type === "error") throw new Error(event.message || "Failed to generate template");
902
- else if (event.type === "done") {
903
- if (event.conversationId) conversationId.value = event.conversationId;
904
- updateMessage(assistantMsgId, { content: event.text });
905
- result = event.content ?? null;
906
- if (result) {
907
- lastPreviousContent.value = currentContent;
908
- lastAppliedContent.value = result;
909
- lastApplyMessageId.value = assistantMsgId;
910
- isLastChangeReverted.value = false;
911
- onApply?.(result);
912
- } else error.value = "ai_apply_failed";
913
- }
914
- }
915
- }
916
- } finally {
917
- reader.cancel().catch(() => {});
918
- }
919
- return result;
920
- } catch (err) {
921
- const wrappedError = err instanceof Error ? err : new Error("Failed to generate template", { cause: err });
922
- error.value = wrappedError.message;
923
- failedPrompt.value = prompt;
924
- onError?.(wrappedError);
925
- messages.value = messages.value.filter((m) => m.id !== userMsgId && m.id !== assistantMsgId);
926
- return null;
927
- } finally {
928
- isGenerating.value = false;
929
- }
930
- }
931
- function toggleLastRevert() {
932
- if (isLastChangeReverted.value) {
933
- if (lastAppliedContent.value) onApply?.(lastAppliedContent.value);
934
- isLastChangeReverted.value = false;
935
- } else {
936
- if (lastPreviousContent.value) onApply?.(lastPreviousContent.value);
937
- isLastChangeReverted.value = true;
938
- }
939
- }
940
- function clearChat() {
941
- messages.value = [];
942
- conversationId.value = null;
943
- error.value = null;
944
- lastApplyMessageId.value = null;
945
- lastPreviousContent.value = null;
946
- lastAppliedContent.value = null;
947
- isLastChangeReverted.value = false;
948
- }
949
- return {
950
- messages,
951
- isGenerating,
952
- isLoadingHistory,
953
- isLastChangeReverted,
954
- lastApplyMessageId,
955
- error,
956
- failedPrompt,
957
- suggestions,
958
- isLoadingSuggestions,
959
- sendPrompt,
960
- toggleLastRevert,
961
- loadConversation,
962
- loadSuggestions,
963
- clearChat
964
- };
965
- }
966
- //#endregion
967
- //#region src/cloud/ai-rewrite.ts
968
- function useAiRewrite(options) {
969
- const { authManager, getTemplateId } = options;
970
- const isRewriting = ref(false);
971
- const streamingText = ref("");
972
- const previousContent = ref(null);
973
- const rewrittenContent = ref(null);
974
- const isReverted = ref(false);
975
- const error = ref(null);
976
- async function rewrite(content, instruction, mergeTags) {
977
- const templateId = getTemplateId();
978
- if (!templateId) return null;
979
- isRewriting.value = true;
980
- streamingText.value = "";
981
- error.value = null;
982
- try {
983
- const url = buildUrl(API_ROUTES["ai.rewriteText"], {
984
- project: authManager.projectId,
985
- tenant: authManager.tenantSlug,
986
- template: templateId
987
- });
988
- const response = await authManager.authenticatedFetch(url, {
989
- method: "POST",
990
- headers: {
991
- "Content-Type": "application/json",
992
- Accept: "text/event-stream"
993
- },
994
- body: JSON.stringify({
995
- content,
996
- instruction,
997
- mergeTags: mergeTags.map((p) => ({
998
- label: p.label,
999
- value: p.value
1000
- }))
1001
- })
1002
- });
1003
- if (!response.ok) {
1004
- if (response.status === 403) throw new Error("aiGenerationNotAvailable");
1005
- const errorData = await response.json().catch(() => null);
1006
- throw new Error(errorData?.message || "Failed to rewrite text");
1007
- }
1008
- const reader = response.body?.getReader();
1009
- if (!reader) throw new Error("Failed to read stream");
1010
- const decoder = new TextDecoder();
1011
- let buffer = "";
1012
- let result = null;
1013
- while (true) {
1014
- const { done, value } = await reader.read();
1015
- if (done) break;
1016
- buffer += decoder.decode(value, { stream: true });
1017
- const lines = buffer.split("\n");
1018
- buffer = lines.pop() ?? "";
1019
- for (const line of lines) {
1020
- if (!line.startsWith("data: ")) continue;
1021
- let event;
1022
- try {
1023
- event = JSON.parse(line.slice(6));
1024
- } catch {
1025
- continue;
1026
- }
1027
- if (event.type === "text") streamingText.value += event.text;
1028
- else if (event.type === "error") throw new Error(event.message || "Failed to rewrite text");
1029
- else if (event.type === "done") {
1030
- result = event.content ?? null;
1031
- if (result) {
1032
- previousContent.value = content;
1033
- rewrittenContent.value = result;
1034
- isReverted.value = false;
1035
- }
1036
- }
1037
- }
1038
- }
1039
- return result;
1040
- } catch (err) {
1041
- error.value = err instanceof Error ? err.message : "Failed to rewrite text";
1042
- return null;
1043
- } finally {
1044
- isRewriting.value = false;
1045
- }
1046
- }
1047
- function undo() {
1048
- if (!previousContent.value) return null;
1049
- isReverted.value = true;
1050
- return previousContent.value;
1051
- }
1052
- function redo() {
1053
- if (!rewrittenContent.value) return null;
1054
- isReverted.value = false;
1055
- return rewrittenContent.value;
1056
- }
1057
- function reset() {
1058
- isRewriting.value = false;
1059
- streamingText.value = "";
1060
- previousContent.value = null;
1061
- rewrittenContent.value = null;
1062
- isReverted.value = false;
1063
- error.value = null;
1064
- }
1065
- return {
1066
- isRewriting,
1067
- streamingText,
1068
- previousContent,
1069
- rewrittenContent,
1070
- isReverted,
1071
- error,
1072
- rewrite,
1073
- undo,
1074
- redo,
1075
- reset
1076
- };
1077
- }
1078
- //#endregion
1079
- //#region src/cloud/ai-config.ts
1080
- function useAiConfig(config) {
1081
- function isFeatureEnabled(feature) {
1082
- if (config === false) return false;
1083
- return config?.[feature] !== false;
1084
- }
1085
- return {
1086
- isFeatureEnabled,
1087
- hasAnyMenuFeature: computed(() => isFeatureEnabled("chat") || isFeatureEnabled("scoring") || isFeatureEnabled("designToTemplate"))
1088
- };
1089
- }
1090
- //#endregion
1091
- //#region src/cloud/template-scoring.ts
1092
- function useTemplateScoring(options) {
1093
- const { authManager, getTemplateId } = options;
1094
- const isScoring = ref(false);
1095
- const scoringResult = ref(null);
1096
- const error = ref(null);
1097
- const fixingFindingId = ref(null);
1098
- const fixStreamingText = ref("");
1099
- const fixError = ref(null);
1100
- async function score(content, mergeTags) {
1101
- const templateId = getTemplateId();
1102
- if (!templateId) return null;
1103
- isScoring.value = true;
1104
- error.value = null;
1105
- scoringResult.value = null;
1106
- try {
1107
- const url = buildUrl(API_ROUTES["ai.score"], {
1108
- project: authManager.projectId,
1109
- tenant: authManager.tenantSlug,
1110
- template: templateId
1111
- });
1112
- const response = await authManager.authenticatedFetch(url, {
1113
- method: "POST",
1114
- headers: {
1115
- "Content-Type": "application/json",
1116
- Accept: "text/event-stream"
1117
- },
1118
- body: JSON.stringify({
1119
- currentContent: content,
1120
- mergeTags: mergeTags.map((p) => ({
1121
- label: p.label,
1122
- value: p.value
1123
- }))
1124
- })
1125
- });
1126
- if (!response.ok) {
1127
- if (response.status === 403) throw new Error("aiGenerationNotAvailable");
1128
- const errorData = await response.json().catch(() => null);
1129
- throw new Error(errorData?.message || "Failed to score template");
1130
- }
1131
- const reader = response.body?.getReader();
1132
- if (!reader) throw new Error("Failed to read stream");
1133
- const decoder = new TextDecoder();
1134
- let buffer = "";
1135
- let result = null;
1136
- while (true) {
1137
- const { done, value } = await reader.read();
1138
- if (done) break;
1139
- buffer += decoder.decode(value, { stream: true });
1140
- const lines = buffer.split("\n");
1141
- buffer = lines.pop() ?? "";
1142
- for (const line of lines) {
1143
- if (!line.startsWith("data: ")) continue;
1144
- let event;
1145
- try {
1146
- event = JSON.parse(line.slice(6));
1147
- } catch {
1148
- continue;
1149
- }
1150
- if (event.type === "error") throw new Error(event.message || "Failed to score template");
1151
- if (event.type === "done") result = event.result ?? null;
1152
- }
1153
- }
1154
- if (result) for (const [category, categoryData] of Object.entries(result.categories)) for (const finding of categoryData.findings) finding.category = category;
1155
- scoringResult.value = result;
1156
- return result;
1157
- } catch (err) {
1158
- error.value = err instanceof Error ? err.message : "Failed to score template";
1159
- return null;
1160
- } finally {
1161
- isScoring.value = false;
1162
- }
1163
- }
1164
- async function fixFinding(blockContent, finding, mergeTags) {
1165
- const templateId = getTemplateId();
1166
- if (!templateId) return null;
1167
- fixingFindingId.value = finding.id;
1168
- fixStreamingText.value = "";
1169
- fixError.value = null;
1170
- try {
1171
- const url = buildUrl(API_ROUTES["ai.fixFinding"], {
1172
- project: authManager.projectId,
1173
- tenant: authManager.tenantSlug,
1174
- template: templateId
1175
- });
1176
- const response = await authManager.authenticatedFetch(url, {
1177
- method: "POST",
1178
- headers: {
1179
- "Content-Type": "application/json",
1180
- Accept: "text/event-stream"
1181
- },
1182
- body: JSON.stringify({
1183
- content: blockContent,
1184
- finding: {
1185
- id: finding.id,
1186
- message: finding.message,
1187
- suggestion: finding.suggestion,
1188
- category: finding.category
1189
- },
1190
- mergeTags: mergeTags.map((p) => ({
1191
- label: p.label,
1192
- value: p.value
1193
- }))
1194
- })
1195
- });
1196
- if (!response.ok) {
1197
- if (response.status === 403) throw new Error("aiGenerationNotAvailable");
1198
- const errorData = await response.json().catch(() => null);
1199
- throw new Error(errorData?.message || "Failed to fix finding");
1200
- }
1201
- const reader = response.body?.getReader();
1202
- if (!reader) throw new Error("Failed to read stream");
1203
- const decoder = new TextDecoder();
1204
- let buffer = "";
1205
- let result = null;
1206
- while (true) {
1207
- const { done, value } = await reader.read();
1208
- if (done) break;
1209
- buffer += decoder.decode(value, { stream: true });
1210
- const lines = buffer.split("\n");
1211
- buffer = lines.pop() ?? "";
1212
- for (const line of lines) {
1213
- if (!line.startsWith("data: ")) continue;
1214
- let event;
1215
- try {
1216
- event = JSON.parse(line.slice(6));
1217
- } catch {
1218
- continue;
1219
- }
1220
- if (event.type === "text") fixStreamingText.value += event.text;
1221
- else if (event.type === "error") throw new Error(event.message || "Failed to fix finding");
1222
- else if (event.type === "done") result = event.content ?? null;
1223
- }
1224
- }
1225
- return result;
1226
- } catch (err) {
1227
- fixError.value = err instanceof Error ? err.message : "Failed to fix finding";
1228
- return null;
1229
- } finally {
1230
- fixingFindingId.value = null;
1231
- }
1232
- }
1233
- function removeFinding(category, findingId) {
1234
- if (!scoringResult.value) return;
1235
- const cat = scoringResult.value.categories[category];
1236
- if (!cat) return;
1237
- cat.findings = cat.findings.filter((f) => f.id !== findingId);
1238
- }
1239
- function reset() {
1240
- isScoring.value = false;
1241
- scoringResult.value = null;
1242
- error.value = null;
1243
- fixingFindingId.value = null;
1244
- fixStreamingText.value = "";
1245
- fixError.value = null;
1246
- }
1247
- return {
1248
- isScoring,
1249
- scoringResult,
1250
- error,
1251
- fixingFindingId,
1252
- fixStreamingText,
1253
- fixError,
1254
- score,
1255
- fixFinding,
1256
- removeFinding,
1257
- reset
1258
- };
1259
- }
1260
- //#endregion
1261
- //#region src/cloud/design-reference.ts
1262
- function useDesignReference(options) {
1263
- const { authManager, getTemplateId, onApply, onError } = options;
1264
- const isGenerating = ref(false);
1265
- const error = ref(null);
1266
- async function generate(input) {
1267
- const templateId = getTemplateId();
1268
- if (!templateId) throw new Error("Template must be saved before using design reference");
1269
- isGenerating.value = true;
1270
- error.value = null;
1271
- try {
1272
- const formData = new FormData();
1273
- if (input.prompt) formData.append("prompt", input.prompt);
1274
- if (input.imageUpload) formData.append("image_upload", input.imageUpload);
1275
- if (input.pdfUpload) formData.append("pdf_upload", input.pdfUpload);
1276
- const url = buildUrl(API_ROUTES["ai.generateFromDesign"], {
1277
- project: authManager.projectId,
1278
- tenant: authManager.tenantSlug,
1279
- template: templateId
1280
- });
1281
- const response = await authManager.authenticatedFetch(url, {
1282
- method: "POST",
1283
- headers: { Accept: "text/event-stream" },
1284
- body: formData
1285
- });
1286
- if (!response.ok) {
1287
- const errorData = await response.json().catch(() => null);
1288
- if (response.status === 403) throw new Error("aiGenerationNotAvailable");
1289
- throw new Error(errorData?.message || "Failed to generate template from design");
1290
- }
1291
- const reader = response.body?.getReader();
1292
- if (!reader) throw new Error("Failed to read stream");
1293
- const decoder = new TextDecoder();
1294
- let buffer = "";
1295
- let result = null;
1296
- while (true) {
1297
- const { done, value } = await reader.read();
1298
- if (done) break;
1299
- buffer += decoder.decode(value, { stream: true });
1300
- const lines = buffer.split("\n");
1301
- buffer = lines.pop() ?? "";
1302
- for (const line of lines) {
1303
- if (!line.startsWith("data: ")) continue;
1304
- const jsonStr = line.slice(6);
1305
- let event;
1306
- try {
1307
- event = JSON.parse(jsonStr);
1308
- } catch {
1309
- continue;
1310
- }
1311
- if (event.type === "error") throw new Error(event.message || "Failed to generate template from design");
1312
- if (event.type === "done") {
1313
- result = event.content ?? null;
1314
- if (result) onApply?.(result);
1315
- }
1316
- }
1317
- }
1318
- return result;
1319
- } catch (err) {
1320
- const wrappedError = err instanceof Error ? err : new Error("Failed to generate template from design", { cause: err });
1321
- error.value = wrappedError.message;
1322
- onError?.(wrappedError);
1323
- return null;
1324
- } finally {
1325
- isGenerating.value = false;
1326
- }
1327
- }
1328
- function reset() {
1329
- isGenerating.value = false;
1330
- error.value = null;
1331
- }
1332
- return {
1333
- isGenerating,
1334
- error,
1335
- generate,
1336
- reset
1337
- };
1338
- }
1339
- //#endregion
1340
688
  //#region src/cloud/comments-provider.ts
1341
689
  /** The Pusher event Cloud's backend fans comment writes out on. */
1342
690
  const BROADCAST_EVENT = "comment-broadcast";
@@ -1461,247 +809,6 @@ function createCloudCommentsProvider(options) {
1461
809
  };
1462
810
  }
1463
811
  //#endregion
1464
- //#region src/cloud/collaboration.ts
1465
- const COLLAB_EVENTS = [
1466
- "pusher:member_added",
1467
- "pusher:member_removed",
1468
- "client-blockLocked",
1469
- "client-blockUnlocked",
1470
- "client-operation",
1471
- "mcp-operation"
1472
- ];
1473
- function unbindCollabEvents(channel) {
1474
- for (const event of COLLAB_EVENTS) channel.unbind(event);
1475
- }
1476
- const COLLABORATOR_COLORS = [
1477
- "#3b82f6",
1478
- "#ef4444",
1479
- "#10b981",
1480
- "#f59e0b",
1481
- "#8b5cf6",
1482
- "#ec4899",
1483
- "#06b6d4",
1484
- "#f97316",
1485
- "#6366f1",
1486
- "#14b8a6"
1487
- ];
1488
- function useCollaboration(options) {
1489
- const { authManager, editor, channel } = options;
1490
- const collaborators = ref([]);
1491
- const lockedBlocks = ref(/* @__PURE__ */ new Map());
1492
- let colorIndex = 0;
1493
- let isProcessingRemoteOperation = false;
1494
- const myUserId = computed(() => authManager.userConfig?.id ?? "");
1495
- function assignColor() {
1496
- const color = COLLABORATOR_COLORS[colorIndex % COLLABORATOR_COLORS.length];
1497
- colorIndex++;
1498
- return color;
1499
- }
1500
- function addCollaborator(member) {
1501
- if (member.id === myUserId.value) return;
1502
- if (collaborators.value.some((c) => c.id === member.id)) return;
1503
- const collaborator = {
1504
- id: member.id,
1505
- name: member.name,
1506
- color: assignColor(),
1507
- selectedBlockId: null
1508
- };
1509
- collaborators.value = [...collaborators.value, collaborator];
1510
- return collaborator;
1511
- }
1512
- function removeCollaborator(memberId) {
1513
- const newLockedBlocks = new Map(lockedBlocks.value);
1514
- for (const [blockId, collaborator] of newLockedBlocks) if (collaborator.id === memberId) newLockedBlocks.delete(blockId);
1515
- lockedBlocks.value = newLockedBlocks;
1516
- collaborators.value = collaborators.value.filter((c) => c.id !== memberId);
1517
- }
1518
- function handleBlockLocked(data) {
1519
- const collaborator = collaborators.value.find((c) => c.id === data.userId);
1520
- if (!collaborator) return;
1521
- collaborators.value = collaborators.value.map((c) => c.id === data.userId ? {
1522
- ...c,
1523
- selectedBlockId: data.blockId
1524
- } : c);
1525
- const newLockedBlocks = new Map(lockedBlocks.value);
1526
- for (const [blockId, holder] of newLockedBlocks) if (holder.id === data.userId) newLockedBlocks.delete(blockId);
1527
- newLockedBlocks.set(data.blockId, {
1528
- ...collaborator,
1529
- selectedBlockId: data.blockId
1530
- });
1531
- lockedBlocks.value = newLockedBlocks;
1532
- if (editor.state.selectedBlockId === data.blockId) editor.selectBlock(null);
1533
- }
1534
- function handleBlockUnlocked(data) {
1535
- const newLockedBlocks = new Map(lockedBlocks.value);
1536
- const holder = newLockedBlocks.get(data.blockId);
1537
- newLockedBlocks.delete(data.blockId);
1538
- lockedBlocks.value = newLockedBlocks;
1539
- if (holder) collaborators.value = collaborators.value.map((c) => c.id === holder.id ? {
1540
- ...c,
1541
- selectedBlockId: null
1542
- } : c);
1543
- }
1544
- function handleRemoteOperation(payload) {
1545
- isProcessingRemoteOperation = true;
1546
- try {
1547
- handleOperation(editor, payload);
1548
- } finally {
1549
- isProcessingRemoteOperation = false;
1550
- }
1551
- }
1552
- function broadcastOperation(payload) {
1553
- if (!channel.value || isProcessingRemoteOperation) return;
1554
- channel.value.trigger("client-operation", payload);
1555
- }
1556
- function broadcastBlockLocked(blockId) {
1557
- if (!channel.value) return;
1558
- channel.value.trigger("client-blockLocked", {
1559
- blockId,
1560
- userId: myUserId.value
1561
- });
1562
- }
1563
- function broadcastBlockUnlocked(blockId) {
1564
- if (!channel.value) return;
1565
- channel.value.trigger("client-blockUnlocked", { blockId });
1566
- }
1567
- watch(() => editor.state.selectedBlockId, (newBlockId, oldBlockId) => {
1568
- if (isProcessingRemoteOperation) return;
1569
- if (oldBlockId) broadcastBlockUnlocked(oldBlockId);
1570
- if (newBlockId) broadcastBlockLocked(newBlockId);
1571
- });
1572
- watch(channel, (newChannel, oldChannel) => {
1573
- if (oldChannel) unbindCollabEvents(oldChannel);
1574
- if (!newChannel) {
1575
- collaborators.value = [];
1576
- lockedBlocks.value = /* @__PURE__ */ new Map();
1577
- colorIndex = 0;
1578
- return;
1579
- }
1580
- const members = newChannel.members;
1581
- if (members) members.each((member) => {
1582
- addCollaborator(member.info);
1583
- });
1584
- newChannel.bind("pusher:member_added", (member) => {
1585
- const collaborator = addCollaborator(member.info);
1586
- if (collaborator) options.onCollaboratorJoined?.(collaborator);
1587
- });
1588
- newChannel.bind("pusher:member_removed", (member) => {
1589
- const collaborator = collaborators.value.find((c) => c.id === member.id);
1590
- removeCollaborator(member.id);
1591
- if (collaborator) options.onCollaboratorLeft?.(collaborator);
1592
- });
1593
- newChannel.bind("client-blockLocked", (data) => {
1594
- handleBlockLocked(data);
1595
- const collaborator = collaborators.value.find((c) => c.id === data.userId);
1596
- if (collaborator) options.onBlockLocked?.({
1597
- blockId: data.blockId,
1598
- collaborator
1599
- });
1600
- });
1601
- newChannel.bind("client-blockUnlocked", (data) => {
1602
- const holder = lockedBlocks.value.get(data.blockId);
1603
- handleBlockUnlocked(data);
1604
- if (holder) options.onBlockUnlocked?.({
1605
- blockId: data.blockId,
1606
- collaborator: holder
1607
- });
1608
- });
1609
- newChannel.bind("client-operation", (payload) => {
1610
- handleRemoteOperation(payload);
1611
- });
1612
- newChannel.bind("mcp-operation", (payload) => {
1613
- handleRemoteOperation(payload);
1614
- });
1615
- });
1616
- onScopeDispose(() => {
1617
- if (channel.value) unbindCollabEvents(channel.value);
1618
- });
1619
- return {
1620
- collaborators,
1621
- lockedBlocks,
1622
- _broadcastOperation: broadcastOperation,
1623
- _isProcessingRemoteOperation: () => isProcessingRemoteOperation
1624
- };
1625
- }
1626
- //#endregion
1627
- //#region src/cloud/collaboration-broadcast.ts
1628
- /**
1629
- * Wraps editor mutation methods to broadcast operations to collaboration
1630
- * peers after each mutation executes. Mutates the editor object in place.
1631
- *
1632
- * Must be applied **before** the history interceptor so the call chain is:
1633
- * history.record() → broadcast + original mutation.
1634
- */
1635
- function useCollaborationBroadcast(editor, collaboration) {
1636
- const originalAddBlock = editor.addBlock;
1637
- const originalUpdateBlock = editor.updateBlock;
1638
- const originalRemoveBlock = editor.removeBlock;
1639
- const originalMoveBlock = editor.moveBlock;
1640
- const originalUpdateSettings = editor.updateSettings;
1641
- const originalSetContent = editor.setContent;
1642
- editor.addBlock = (block, targetSectionId, columnIndex, index) => {
1643
- originalAddBlock(block, targetSectionId, columnIndex, index);
1644
- collaboration._broadcastOperation({
1645
- operation: "addBlock",
1646
- data: {
1647
- block,
1648
- sectionId: targetSectionId,
1649
- columnIndex,
1650
- index
1651
- },
1652
- timestamp: Date.now()
1653
- });
1654
- };
1655
- editor.updateBlock = (blockId, updates) => {
1656
- originalUpdateBlock(blockId, updates);
1657
- collaboration._broadcastOperation({
1658
- operation: "updateBlock",
1659
- data: {
1660
- blockId,
1661
- updates
1662
- },
1663
- timestamp: Date.now()
1664
- });
1665
- };
1666
- editor.removeBlock = (blockId) => {
1667
- originalRemoveBlock(blockId);
1668
- collaboration._broadcastOperation({
1669
- operation: "deleteBlock",
1670
- data: { blockId },
1671
- timestamp: Date.now()
1672
- });
1673
- };
1674
- editor.moveBlock = (blockId, newIndex, targetSectionId, columnIndex) => {
1675
- originalMoveBlock(blockId, newIndex, targetSectionId, columnIndex);
1676
- collaboration._broadcastOperation({
1677
- operation: "moveBlock",
1678
- data: {
1679
- blockId,
1680
- index: newIndex,
1681
- sectionId: targetSectionId,
1682
- columnIndex
1683
- },
1684
- timestamp: Date.now()
1685
- });
1686
- };
1687
- editor.updateSettings = (updates) => {
1688
- originalUpdateSettings(updates);
1689
- collaboration._broadcastOperation({
1690
- operation: "updateSettings",
1691
- data: { updates },
1692
- timestamp: Date.now()
1693
- });
1694
- };
1695
- editor.setContent = (content, markDirty) => {
1696
- originalSetContent(content, markDirty);
1697
- collaboration._broadcastOperation({
1698
- operation: "setContent",
1699
- data: { content },
1700
- timestamp: Date.now()
1701
- });
1702
- };
1703
- }
1704
- //#endregion
1705
812
  //#region src/cloud/web-socket.ts
1706
813
  function useWebSocket(options) {
1707
814
  const { authManager, onError } = options;
@@ -2357,18 +1464,6 @@ async function performHealthCheck(options = {}) {
2357
1464
  };
2358
1465
  }
2359
1466
  //#endregion
2360
- //#region src/cloud/mcp-listener.ts
2361
- function useMcpListener(options) {
2362
- const { editor, channel, onOperation } = options;
2363
- watch(channel, (newChannel, oldChannel) => {
2364
- if (oldChannel) oldChannel.unbind("mcp-operation");
2365
- if (newChannel) newChannel.bind("mcp-operation", (payload) => {
2366
- handleOperation(editor, payload);
2367
- onOperation?.(payload);
2368
- });
2369
- });
2370
- }
2371
- //#endregion
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 };
1467
+ export { API_ROUTES, ApiClient, AuthManager, MediaApiClient, WebSocketClient, buildUrl, createCloudCommentsProvider, createCloudMediaProvider, createCloudRenderProvider, createCloudSavedBlocksProvider, createCloudTemplatesProvider, createCloudTestEmailProvider, createCloudVersionHistoryProvider, createSdkAuthManager, isFatalAuthError, performHealthCheck, resolveExportFonts, resolveWebSocketConfig, useExport, usePlanConfig, useTestEmail, useWebSocket };
2373
1468
 
2374
1469
  //# sourceMappingURL=index.js.map