@leviosa-ai/detail-page-editor 0.13.1 → 0.13.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.
@@ -207,9 +207,19 @@ export function LeviosaCanvasWorkspace({
207
207
  const frame = inner.getBoundingClientRect();
208
208
  const cx = frame.left + inner.clientWidth / 2;
209
209
  const cy = frame.top + inner.clientHeight / 2;
210
+ // **벌은 스크롤로 안 바뀐다.** 벌을 고르는 것은 그 안을 누르는 일이지 지나가는
211
+ // 일이 아니다 — 빈 자리를 잡아 화면을 옮겼을 뿐인데 «대표로 지정»이 다른 벌로
212
+ // 건너뛰면 어디를 보고 있는지 알 수가 없다. 같은 벌 안에서 어느 판을 보고 있는지만
213
+ // 따라간다. 꼬리표 없는 문서는 전부가 한 벌이라 지금까지와 똑같다.
214
+ const current = frameOf(store.activePage ?? store.pages[0] ?? {});
210
215
  const nodes = Array.from(
211
216
  inner.querySelectorAll<HTMLElement>("[data-lc-page]"),
212
- );
217
+ ).filter((node) => {
218
+ const page = node.dataset.lcPage
219
+ ? store.getPageById(node.dataset.lcPage)
220
+ : null;
221
+ return page ? frameOf(page) === current : false;
222
+ });
213
223
  const distance = (node: HTMLElement) => {
214
224
  const box = node.getBoundingClientRect();
215
225
  const dx = Math.max(box.left - cx, 0, cx - box.right);
@@ -229,11 +239,28 @@ export function LeviosaCanvasWorkspace({
229
239
  store.selectPage(id);
230
240
  }, [store]);
231
241
 
242
+ /**
243
+ * 고른 요소가 있으면 «보고 있는 벌»은 손이 가 있는 곳이지 화면 가운데가 아니다.
244
+ *
245
+ * 이게 없으면 4번 벌의 요소를 집는 순간 그 선택이 활성 페이지를 옮기고, 그 때문에
246
+ * 화면이 스르륵 움직이고, 그 스크롤이 가운데에 있는 2번 벌을 다시 활성으로 만든다 —
247
+ * 집은 것은 4번인데 «대표로 지정»은 2번 위에 뜬다.
248
+ */
249
+ const holding = store.selectedElementsIds.length > 0;
250
+ /** 고른 요소가 놓인 페이지. 활성 화면이 여기로 온 것이면 손으로 누른 것이다. */
251
+ const heldPageId =
252
+ store.pageOfElement(store.selectedElementsIds[0] ?? "")?.id ?? null;
253
+
232
254
  const scrollRaf = useRef<number | null>(null);
255
+ const holdingRef = useRef(holding);
256
+ holdingRef.current = holding;
257
+ const heldPageIdRef = useRef(heldPageId);
258
+ heldPageIdRef.current = heldPageId;
233
259
  const onScroll = useCallback(() => {
234
260
  if (scrollRaf.current != null) return;
235
261
  scrollRaf.current = requestAnimationFrame(() => {
236
262
  scrollRaf.current = null;
263
+ if (holdingRef.current) return;
237
264
  recomputeActive();
238
265
  });
239
266
  }, [recomputeActive]);
@@ -243,6 +270,13 @@ export function LeviosaCanvasWorkspace({
243
270
  useEffect(() => {
244
271
  const inner = innerRef.current;
245
272
  if (!inner || !activeId || activeId === scrollSetId.current) return;
273
+ // 캔버스에서 요소를 집어 활성 화면이 바뀐 것이면 옮기지 않는다. 방금 손으로
274
+ // 누른 것이라 이미 보고 있고, 여기서 화면을 움직이면 누를 때마다 밑이 흔들린다.
275
+ // 목록에서 고른 때만 데려간다.
276
+ if (activeId === heldPageIdRef.current) {
277
+ scrollSetId.current = activeId;
278
+ return;
279
+ }
246
280
  scrollSetId.current = activeId;
247
281
  const node = inner.querySelector<HTMLElement>(
248
282
  `[data-lc-page="${CSS.escape(activeId)}"]`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leviosa-ai/detail-page-editor",
3
- "version": "0.13.1",
3
+ "version": "0.13.4",
4
4
  "description": "Leviosa detail-page editor shell. Source-only: consumers compile it through Next transpilePackages. Every app coupling (sourcing server, brand store, cache keys, toast) arrives through the DetailPageHost interface, and every URL it fetches comes from configureDetailPageEditor.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -67,7 +67,7 @@
67
67
  "woff2-encoder": "2.0.0"
68
68
  },
69
69
  "peerDependencies": {
70
- "@leviosa-ai/canvas": "0.6.1",
70
+ "@leviosa-ai/canvas": "0.6.4",
71
71
  "@tanstack/react-query": "^5.100.3",
72
72
  "konva": "^10.0.0",
73
73
  "lucide-react": "^0.563.0 || ^1.0.0",