From a6d02322f310e7b480fd5b74829935d1ec91448d Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Fri, 11 Sep 2026 13:59:24 +0200 Subject: [PATCH] fix: persist maximize + corners as free placement (exact size/pos), edges as dock --- src/main.ts | 76 ++++++++++++++++++++++++++++++++++++++++------------- src/ui.tsx | 3 +++ 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/src/main.ts b/src/main.ts index 1145d19..ea9352d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -53,6 +53,21 @@ export default function () { if (saveTimer != null) clearTimeout(saveTimer) saveTimer = setTimeout(function () { savePlacement(p) }, 400) } + // Persist the CURRENT window as a free placement (real canvas-space x/y + size). + // Used by maximize and corner docks, which must remember the EXACT size/position + // rather than re-deriving it from a dock formula (which would reset the size). + function saveFreeFromCurrent () { + try { + const pos = (figma.ui as any).getPosition() + savePlacement({ + mode: 'free', + x: Math.round(pos.canvasSpace.x), + y: Math.round(pos.canvasSpace.y), + w: lastSize.width, + h: lastSize.height + }) + } catch (e) { /* getPosition unavailable — skip */ } + } // computeDock считает целевой размер+позицию для док-позиции. // canonical=true — НАЖАТИЕ кнопки дока: задаём канонический размер // (edge: фикс-сторона dockEdgeSize, другая = max viewport; center: 50%). @@ -293,28 +308,36 @@ export default function () { // к левому/верхнему краю вместо центра (баг relX=0 на top-center). on('DOCK_WINDOW', function (payload: { position: DockPosition; width: number; height: number }) { const { position } = payload - // Центр — это one-shot: применяем 50% по центру ОДИН раз, окно остаётся в покое - // (НЕ приклеено, НЕ держит центр при ручном ресайзе/перемещении). - if (position === 'center') { - dockedPosition = null - } else { - // Остальные позиции — удерживаем прижатым (поллинг пересчитывает при зуме/пане). + const isEdge = position === 'top-center' || position === 'bottom-center' || + position === 'center-left' || position === 'center-right' + // Центр и углы — one-shot (не удерживаются поллингом). Края — удерживаются прижатыми. + if (isEdge) { dockedPosition = position lastBoundsKey = '' // форсируем пересчёт на ближайшем тике + } else { + dockedPosition = null } // Попытка растяжения до канона (Figma клампит до лимита при видимом UI). const state = computeDock(position, payload.width, payload.height, true) figma.ui.resize(state.width, state.height) lastSize = { width: clampWindowSize(state).width, height: clampWindowSize(state).height } // reposition откладываем на DOCK_REFINE (UI пришлёт реальный размер после клампа). - // Remember placement (center is one-shot but we still persist it as a logical position). - savePlacement({ mode: 'dock', position: position }) + // Сохранение позиции тоже в DOCK_REFINE (там известен реальный размер после клампа). }) // Точная прижимка от РЕАЛЬНОГО размера окна (после клампа Figma). on('DOCK_REFINE', function (payload: { position: DockPosition; width: number; height: number }) { const { position, width, height } = payload applyDock(position, width, height, false) + const isEdge = position === 'top-center' || position === 'bottom-center' || + position === 'center-left' || position === 'center-right' + if (isEdge) { + // Edges are held docked — persist as dock so re-open re-pins to the edge. + savePlacement({ mode: 'dock', position: position }) + } else { + // Corners / center are one-shot — remember the EXACT size+position (free). + saveFreeFromCurrent() + } }) // Выход из дока кликом по углу: свободная ось (та, что была растянута) становится @@ -354,6 +377,14 @@ export default function () { } catch (e) { /* getPosition unavailable — skip */ } }) + // UI asks to persist the CURRENT window as a free placement (used by Maximize, + // which is not a held dock — it must remember the exact size+position across restart). + on('SAVE_FREE', function () { + dockedPosition = null // maximize is not held; clear any stale dock state + lastBoundsKey = '' + saveFreeFromCurrent() + }) + // Останавливаем поллинг при закрытии плагина, чтобы не висел в памяти. figma.on('close', function () { clearInterval(dockInterval) @@ -457,18 +488,27 @@ export default function () { return } if (saved.mode === 'dock') { - // Dock mode (incl. center): stretch to its canonical size and pin. - // For center the window is NOT held (one-shot); for edges it stays docked. - dockedPosition = saved.position === 'center' ? null : saved.position - const state = computeDock(saved.position, 320, 200, true) - figma.ui.resize(state.width, state.height) - lastSize = { width: clampWindowSize(state).width, height: clampWindowSize(state).height } - requestAnimationFrame(function () { + const isEdge = saved.position === 'top-center' || saved.position === 'bottom-center' || + saved.position === 'center-left' || saved.position === 'center-right' + if (!isEdge) { + // Stale record (corner/center saved as dock by an older build) — treat as free + // with the current size, so the exact geometry is restored. + figma.ui.reposition(0, 0) // will be corrected by native re-open; keep simple + lastSize = { width: 320, height: 200 } + dockedPosition = null + } else { + // Edge dock: stretch to its canonical size and pin (held across zoom/pan). + dockedPosition = saved.position + const state = computeDock(saved.position, 320, 200, true) + figma.ui.resize(state.width, state.height) + lastSize = { width: clampWindowSize(state).width, height: clampWindowSize(state).height } requestAnimationFrame(function () { - applyDock(saved.position, lastSize.width, lastSize.height, false) - lastBoundsKey = '' // force dock poll to keep it pinned on zoom/pan + requestAnimationFrame(function () { + applyDock(saved.position, lastSize.width, lastSize.height, false) + lastBoundsKey = '' // force dock poll to keep it pinned on zoom/pan + }) }) - }) + } } else { // Free placement: restore exact canvas-space position + size. figma.ui.resize(saved.w, saved.h) diff --git a/src/ui.tsx b/src/ui.tsx index cd43bd3..4e2e083 100644 --- a/src/ui.tsx +++ b/src/ui.tsx @@ -215,6 +215,9 @@ function Plugin () { width: window.innerWidth, height: window.innerHeight }) + // Maximize is NOT a held dock — persist the exact size+position so re-open + // restores it (overrides the edge-dock save done by DOCK_REFINE above). + emit('SAVE_FREE') refreshInfo() }) })