@sequent-org/moodboard 1.4.9 → 1.4.11
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.
package/package.json
CHANGED
package/src/core/ApiClient.js
CHANGED
|
@@ -67,11 +67,17 @@ export class ApiClient {
|
|
|
67
67
|
const objects = Array.isArray(cleanedData?.objects) ? cleanedData.objects : [];
|
|
68
68
|
const imageObjects = objects.filter((obj) => obj?.type === 'image');
|
|
69
69
|
const imageObjectsWithSrc = imageObjects.filter((obj) => typeof obj?.src === 'string' && obj.src.trim().length > 0);
|
|
70
|
+
const imageObjectsWithoutSrc = imageObjects
|
|
71
|
+
.filter((obj) => !(typeof obj?.src === 'string' && obj.src.trim().length > 0))
|
|
72
|
+
.map((obj) => obj?.id || 'unknown');
|
|
70
73
|
console.log('history/save payload stats:', {
|
|
71
74
|
totalObjects: objects.length,
|
|
72
75
|
imageObjects: imageObjects.length,
|
|
73
76
|
imageObjectsWithSrc: imageObjectsWithSrc.length
|
|
74
77
|
});
|
|
78
|
+
if (imageObjectsWithoutSrc.length > 0) {
|
|
79
|
+
console.warn('history/save warning: image objects without src (kept as broken placeholders):', imageObjectsWithoutSrc);
|
|
80
|
+
}
|
|
75
81
|
|
|
76
82
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
|
|
77
83
|
|
|
@@ -157,7 +163,7 @@ export class ApiClient {
|
|
|
157
163
|
const normalizedSrc = topSrcRaw.trim() || propSrcRaw.trim();
|
|
158
164
|
|
|
159
165
|
if (!normalizedSrc) {
|
|
160
|
-
|
|
166
|
+
return obj;
|
|
161
167
|
}
|
|
162
168
|
if (/^data:/i.test(normalizedSrc) || /^blob:/i.test(normalizedSrc)) {
|
|
163
169
|
throw new Error(`Image object "${obj.id || 'unknown'}" contains forbidden data/blob src. Save is blocked.`);
|
|
@@ -174,9 +180,6 @@ export class ApiClient {
|
|
|
174
180
|
cleanedObj.properties = { ...cleanedObj.properties };
|
|
175
181
|
delete cleanedObj.properties.src;
|
|
176
182
|
}
|
|
177
|
-
if ('imageId' in cleanedObj) {
|
|
178
|
-
delete cleanedObj.imageId;
|
|
179
|
-
}
|
|
180
183
|
return cleanedObj;
|
|
181
184
|
}
|
|
182
185
|
|
|
@@ -236,9 +239,6 @@ export class ApiClient {
|
|
|
236
239
|
restoredObj.properties = { ...restoredObj.properties };
|
|
237
240
|
delete restoredObj.properties.src;
|
|
238
241
|
}
|
|
239
|
-
if ('imageId' in restoredObj) {
|
|
240
|
-
delete restoredObj.imageId;
|
|
241
|
-
}
|
|
242
242
|
return restoredObj;
|
|
243
243
|
}
|
|
244
244
|
|
package/src/core/SaveManager.js
CHANGED
|
@@ -104,9 +104,9 @@ export class SaveManager {
|
|
|
104
104
|
});
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
//
|
|
108
|
-
// - каждый image обязан иметь src
|
|
107
|
+
// Контракт сохранения картинок:
|
|
109
108
|
// - data:/blob: URL в image запрещены
|
|
109
|
+
// - image без src допускаются (legacy broken state), но логируются
|
|
110
110
|
this._assertImageSaveContract(saveData);
|
|
111
111
|
|
|
112
112
|
// Проверяем, изменились ли данные с последнего сохранения
|
|
@@ -240,7 +240,8 @@ export class SaveManager {
|
|
|
240
240
|
const effectiveSrc = topSrc.trim() || propSrc.trim();
|
|
241
241
|
|
|
242
242
|
if (!effectiveSrc) {
|
|
243
|
-
|
|
243
|
+
console.warn(`Image object "${obj.id || 'unknown'}" has no src. Saving continues for legacy compatibility.`);
|
|
244
|
+
continue;
|
|
244
245
|
}
|
|
245
246
|
if (/^data:/i.test(topSrc) || /^blob:/i.test(topSrc) || /^data:/i.test(propSrc) || /^blob:/i.test(propSrc)) {
|
|
246
247
|
throw new Error(`Image object "${obj.id || 'unknown'}" contains forbidden data/blob src. Save is blocked.`);
|
package/src/core/index.js
CHANGED
|
@@ -535,9 +535,6 @@ export class CoreMoodBoard {
|
|
|
535
535
|
objectData.properties = { ...objectData.properties };
|
|
536
536
|
delete objectData.properties.src;
|
|
537
537
|
}
|
|
538
|
-
if ('imageId' in objectData) {
|
|
539
|
-
delete objectData.imageId;
|
|
540
|
-
}
|
|
541
538
|
}
|
|
542
539
|
if (objectData.type === 'mindmap') {
|
|
543
540
|
logMindmapCompoundDebug('core:load-object', {
|
|
@@ -11,6 +11,8 @@ export class ActionHandler {
|
|
|
11
11
|
* Обрабатывает действия тулбара
|
|
12
12
|
*/
|
|
13
13
|
handleToolbarAction(action) {
|
|
14
|
+
this._assertImageActionContract(action);
|
|
15
|
+
|
|
14
16
|
switch (action.type) {
|
|
15
17
|
case 'frame':
|
|
16
18
|
case 'simple-text':
|
|
@@ -112,4 +114,19 @@ export class ActionHandler {
|
|
|
112
114
|
exportBoard() {
|
|
113
115
|
return this.handleExportBoard();
|
|
114
116
|
}
|
|
117
|
+
|
|
118
|
+
_assertImageActionContract(action) {
|
|
119
|
+
if (!action || (action.type !== 'image' && action.type !== 'revit-screenshot-img')) return;
|
|
120
|
+
const src = typeof action?.properties?.src === 'string' ? action.properties.src.trim() : '';
|
|
121
|
+
if (src) return;
|
|
122
|
+
|
|
123
|
+
const reason = 'в цепочке создания отсутствует обязательное поле properties.src';
|
|
124
|
+
const message = `Загрузить картинку не получилось: ${reason}. Попробуйте еще раз.`;
|
|
125
|
+
if (this.workspaceManager?.showNotification) {
|
|
126
|
+
this.workspaceManager.showNotification(message);
|
|
127
|
+
} else if (typeof alert === 'function') {
|
|
128
|
+
alert(message);
|
|
129
|
+
}
|
|
130
|
+
throw new Error(`Image action contract violated: ${reason}`);
|
|
131
|
+
}
|
|
115
132
|
}
|