@sequent-org/moodboard 1.4.2 → 1.4.4

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sequent-org/moodboard",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "type": "module",
5
5
  "description": "Interactive moodboard",
6
6
  "main": "./src/index.js",
@@ -18,8 +18,6 @@ export class HistoryManager {
18
18
  // Флаг для предотвращения зацикливания при undo/redo
19
19
  this.isExecutingCommand = false;
20
20
  this._listenersAttached = false;
21
- this._onUndo = () => this.undo();
22
- this._onRedo = () => this.redo();
23
21
  this._onDebug = () => this.debugHistory();
24
22
 
25
23
  this.initEventListeners();
@@ -28,10 +26,6 @@ export class HistoryManager {
28
26
  initEventListeners() {
29
27
  if (this._listenersAttached) return;
30
28
  this._listenersAttached = true;
31
- // Слушаем события клавиатуры
32
- this.eventBus.on(Events.Keyboard.Undo, this._onUndo);
33
- this.eventBus.on(Events.Keyboard.Redo, this._onRedo);
34
-
35
29
  // Для отладки
36
30
  this.eventBus.on(Events.History.Debug, this._onDebug);
37
31
  }
@@ -253,8 +247,6 @@ export class HistoryManager {
253
247
  */
254
248
  destroy() {
255
249
  this.clear();
256
- this.eventBus.off(Events.Keyboard.Undo, this._onUndo);
257
- this.eventBus.off(Events.Keyboard.Redo, this._onRedo);
258
250
  this.eventBus.off(Events.History.Debug, this._onDebug);
259
251
  this._listenersAttached = false;
260
252
  }
@@ -133,6 +133,7 @@ export class SaveManager {
133
133
  // Эмитируем событие успешного сохранения
134
134
  this.eventBus.emit(Events.Save.Success, {
135
135
  data: saveData,
136
+ response,
136
137
  timestamp: new Date().toISOString()
137
138
  });
138
139
  } else {
package/src/core/index.js CHANGED
@@ -226,13 +226,6 @@ export class CoreMoodBoard {
226
226
  historySize: data.historySize,
227
227
  });
228
228
  }
229
-
230
-
231
- // Можно здесь обновить состояние кнопок Undo/Redo в UI
232
- this.eventBus.emit(Events.UI.UpdateHistoryButtons, {
233
- canUndo: data.canUndo,
234
- canRedo: data.canRedo
235
- });
236
229
  });
237
230
  }
238
231
 
@@ -10,11 +10,11 @@ export class KeyboardSelectionActions {
10
10
  switch (actionId) {
11
11
  case 'undo':
12
12
  return () => {
13
- this.eventBus.emit(Events.Keyboard.Undo);
13
+ this.eventBus.emit(Events.UI.LoadPrevVersion);
14
14
  };
15
15
  case 'redo':
16
16
  return () => {
17
- this.eventBus.emit(Events.Keyboard.Redo);
17
+ this.eventBus.emit(Events.UI.LoadNextVersion);
18
18
  };
19
19
  case 'select-all':
20
20
  return () => {
@@ -91,40 +91,25 @@ export function bindToolbarEvents(board) {
91
91
  if (!moodboardId) return;
92
92
  if (!Number.isFinite(targetVersion) || targetVersion < 1) return;
93
93
  try {
94
- await board.loadFromApi(moodboardId, targetVersion, { fallbackToSeedOnError: false });
95
-
96
- // В append-only модели откат/переход по версии фиксируем новой записью,
97
- // чтобы дальнейшие изменения шли от нового head и не теряли "будущее".
98
- const restoreSnapshot = board.coreMoodboard?.getBoardData?.();
99
- if (restoreSnapshot && board.coreMoodboard?.apiClient) {
100
- const saveResult = await board.coreMoodboard.apiClient.saveBoard(
101
- moodboardId,
102
- restoreSnapshot,
103
- { actionType: 'history_restore' }
104
- );
105
- const restoredVersion = Number(saveResult?.historyVersion);
106
- if (Number.isFinite(restoredVersion) && restoredVersion > 0) {
107
- board.currentLoadedVersion = restoredVersion;
108
- board.coreMoodboard.eventBus.emit(Events.UI.UpdateHistoryButtons, {
109
- canUndo: restoredVersion > 1,
110
- canRedo: false,
111
- });
112
- }
113
- }
94
+ await board.loadFromApi(moodboardId, targetVersion, {
95
+ fallbackToSeedOnError: false,
96
+ historyNavigation: true,
97
+ });
114
98
  } catch (error) {
115
99
  console.warn(`⚠️ Не удалось загрузить версию ${targetVersion}:`, error?.message || error);
116
100
  }
117
101
  };
118
102
 
119
103
  board.coreMoodboard.eventBus.on(Events.UI.LoadPrevVersion, () => {
120
- const current = Number(board.currentLoadedVersion);
104
+ const current = Number(board.historyCursorVersion);
121
105
  if (!Number.isFinite(current) || current <= 1) return;
122
106
  loadVersion(current - 1);
123
107
  });
124
108
 
125
109
  board.coreMoodboard.eventBus.on(Events.UI.LoadNextVersion, () => {
126
- const current = Number(board.currentLoadedVersion);
127
- if (!Number.isFinite(current)) return;
110
+ const current = Number(board.historyCursorVersion);
111
+ const head = Number(board.historyHeadVersion);
112
+ if (!Number.isFinite(current) || !Number.isFinite(head) || current >= head) return;
128
113
  loadVersion(current + 1);
129
114
  });
130
115
  }
@@ -154,8 +139,19 @@ export function bindSaveCallbacks(board) {
154
139
  return;
155
140
  }
156
141
 
157
- if (typeof board.options.onSave === 'function') {
158
- board.coreMoodboard.eventBus.on('save:success', (data) => {
142
+ board.coreMoodboard.eventBus.on('save:success', (data) => {
143
+ const savedVersion = Number(data?.response?.historyVersion);
144
+ if (Number.isFinite(savedVersion) && savedVersion > 0) {
145
+ board.currentLoadedVersion = savedVersion;
146
+ board.historyHeadVersion = savedVersion;
147
+ board.historyCursorVersion = savedVersion;
148
+ board.coreMoodboard.eventBus.emit(Events.UI.UpdateHistoryButtons, {
149
+ canUndo: savedVersion > 1,
150
+ canRedo: false,
151
+ });
152
+ }
153
+
154
+ if (typeof board.options.onSave === 'function') {
159
155
  try {
160
156
  let screenshot = null;
161
157
  if (board.coreMoodboard.pixi && board.coreMoodboard.pixi.app && board.coreMoodboard.pixi.app.view) {
@@ -171,9 +167,11 @@ export function bindSaveCallbacks(board) {
171
167
  } catch (error) {
172
168
  console.warn('⚠️ Ошибка в коллбеке onSave:', error);
173
169
  }
174
- });
170
+ }
171
+ });
175
172
 
176
- board.coreMoodboard.eventBus.on('save:error', (data) => {
173
+ board.coreMoodboard.eventBus.on('save:error', (data) => {
174
+ if (typeof board.options.onSave === 'function') {
177
175
  try {
178
176
  board.options.onSave({
179
177
  success: false,
@@ -183,6 +181,6 @@ export function bindSaveCallbacks(board) {
183
181
  } catch (error) {
184
182
  console.warn('⚠️ Ошибка в коллбеке onSave:', error);
185
183
  }
186
- });
187
- }
184
+ }
185
+ });
188
186
  }
@@ -17,6 +17,21 @@ export function getCsrfToken(board) {
17
17
  || '';
18
18
  }
19
19
 
20
+ function resolveMoodboardApiBase(board) {
21
+ const raw = String(board?.options?.apiUrl || '').trim();
22
+ if (!raw) return '/api/v2/moodboard';
23
+
24
+ // Совместимость с legacy конфигом: /api/moodboard -> /api/v2/moodboard
25
+ if (raw.endsWith('/api/moodboard')) {
26
+ return raw.replace(/\/api\/moodboard$/, '/api/v2/moodboard');
27
+ }
28
+ if (raw.endsWith('/api/moodboard/')) {
29
+ return raw.replace(/\/api\/moodboard\/$/, '/api/v2/moodboard/');
30
+ }
31
+
32
+ return raw;
33
+ }
34
+
20
35
  function normalizeLoadedPayload(payload, moodboardIdFallback) {
21
36
  const state = (payload?.state && typeof payload.state === 'object')
22
37
  ? payload.state
@@ -36,6 +51,7 @@ function normalizeLoadedPayload(payload, moodboardIdFallback) {
36
51
 
37
52
  export async function loadExistingBoard(board, version = null, options = {}) {
38
53
  const fallbackToSeedOnError = options?.fallbackToSeedOnError !== false;
54
+ const historyNavigation = options?.historyNavigation === true;
39
55
  try {
40
56
  const boardId = board.options.boardId;
41
57
 
@@ -47,11 +63,12 @@ export async function loadExistingBoard(board, version = null, options = {}) {
47
63
  return;
48
64
  }
49
65
 
50
- console.log(`📦 MoodBoard: загружаем доску ${boardId} с ${board.options.apiUrl}`);
66
+ const apiBase = resolveMoodboardApiBase(board);
67
+ console.log(`📦 MoodBoard: загружаем доску ${boardId} с ${apiBase}`);
51
68
 
52
- const baseUrl = board.options.apiUrl.endsWith('/')
53
- ? `${board.options.apiUrl}${boardId}`
54
- : `${board.options.apiUrl}/${boardId}`;
69
+ const baseUrl = apiBase.endsWith('/')
70
+ ? `${apiBase}${boardId}`
71
+ : `${apiBase}/${boardId}`;
55
72
  const loadUrl = Number.isFinite(version) ? `${baseUrl}/${version}` : baseUrl;
56
73
 
57
74
  const response = await fetch(loadUrl, {
@@ -71,14 +88,20 @@ export async function loadExistingBoard(board, version = null, options = {}) {
71
88
 
72
89
  if (apiResponse?.success && payload) {
73
90
  const normalizedData = normalizeLoadedPayload(payload, boardId);
74
- board.currentLoadedVersion = Number(normalizedData.version) || null;
91
+ const loadedVersion = Number(normalizedData.version) || null;
92
+ board.currentLoadedVersion = loadedVersion;
93
+ board.historyCursorVersion = loadedVersion;
94
+ if (!historyNavigation) {
95
+ board.historyHeadVersion = loadedVersion;
96
+ }
75
97
  console.log('✅ MoodBoard: данные загружены с сервера', normalizedData);
76
98
  board.dataManager.loadData(normalizedData);
77
99
  if (board?.coreMoodboard?.eventBus) {
100
+ const cursor = Number(board.historyCursorVersion);
101
+ const head = Number(board.historyHeadVersion);
78
102
  board.coreMoodboard.eventBus.emit(Events.UI.UpdateHistoryButtons, {
79
- canUndo: Number(board.currentLoadedVersion) > 1,
80
- // Верхнюю границу версий backend не возвращает, поэтому оставляем переход вперед доступным.
81
- canRedo: true,
103
+ canUndo: Number.isFinite(cursor) && cursor > 1,
104
+ canRedo: Number.isFinite(cursor) && Number.isFinite(head) && cursor < head,
82
105
  });
83
106
  }
84
107
  invokeOnLoad(board, { success: true, data: normalizedData });