From 9f9349efb61ce67b13cffd7a9aea3f491f383c33 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Wed, 9 Sep 2026 15:13:05 +0200 Subject: [PATCH] fix: keep docked window pinned to edge across zoom/pan via viewport poll --- src/main.ts | 68 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/src/main.ts b/src/main.ts index 4a5ac25..7baa7d4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,24 +4,20 @@ export default function () { // Глобальный ярлык relaunch в правой панели Figma (команда runAI из manifest.json) figma.root.setRelaunchData({ runAI: '' }) - // Последнее известное состояние окна (позиция в canvas-space + размер), - // чтобы UI могла запросить его через GET_WINDOW_INFO. + // Последнее известное состояние окна (размер), чтобы пересчитывать позицию + // при ресайзе/зуме, пока активен dock-режим. let lastSize = { width: 320, height: 200 } - // Ресайз окна плагина (drag за угол в UI -> useWindowResize -> RESIZE_WINDOW) - on('RESIZE_WINDOW', function (windowSize: { width: number; height: number }) { - const { width, height } = windowSize - lastSize = { width, height } - figma.ui.resize(width, height) - }) + // Текущий режим докинга. null = окно свободно (не прижато). + // Пока не null — позиция пересчитывается при каждом изменении viewport + // (через поллинг, т.к. события viewportchange в API нет), иначе + // canvas-space-точка "уплывает" на экране при смене зума/пана. + let dockedPosition: 'left' | 'right' | 'center' | null = null - // Эмуляция докинга: прижать окно плагина к краям canvas (left/right/center). - // reposition() принимает canvas-space координаты (как figma.viewport.center). - // Ширину панелей Figma API не отдаёт, поэтому прижимаем к bounds видимой области. - on('DOCK_WINDOW', function (payload: { position: 'left' | 'right' | 'center'; width: number; height: number }) { - const { position, width, height } = payload - lastSize = { width, height } - figma.ui.resize(width, height) + // Прижать окно к краю видимой области canvas (bounds) в canvas-space. + // reposition() интерпретирует x/y как canvas-space, поэтому окно визуально + // остаётся у края ЭКРАНА только пока мы пересчитываем его при изменении viewport. + function applyDock (position: 'left' | 'right' | 'center', width: number, height: number) { const b = figma.viewport.bounds let x = b.x let y = b.y @@ -32,6 +28,48 @@ export default function () { y = b.y + (b.height - height) / 2 } figma.ui.reposition(x, y) + } + + // Поллинг viewport: держим окно прижатым к краю при любом зуме/панорамировании. + // События viewportchange в Figma API НЕТ (нельзя подписаться через figma.on), + // поэтому опрашиваем bounds и репозиционируем только когда он реально изменился. + // Это предотвращает "уплывание" окна на экране: reposition() работает в canvas-space, + // а bounds при зуме сжимается/растягивается — без пересчёта точка уезжает. + let lastBoundsKey = '' + const dockInterval = setInterval(function () { + if (dockedPosition === null) return + const b = figma.viewport.bounds + const key = `${b.x}|${b.y}|${b.width}|${b.height}` + if (key !== lastBoundsKey) { + lastBoundsKey = key + applyDock(dockedPosition, lastSize.width, lastSize.height) + } + }, 250) + + // Ресайз окна плагина (drag за угол в UI -> useWindowResize -> RESIZE_WINDOW) + on('RESIZE_WINDOW', function (windowSize: { width: number; height: number }) { + const { width, height } = windowSize + lastSize = { width, height } + figma.ui.resize(width, height) + // Если окно задокено — держим прижатым к краю при смене размера + if (dockedPosition !== null) { + applyDock(dockedPosition, width, height) + } + }) + + // Эмуляция докинга: прижать окно к краям canvas (left/right/center). + on('DOCK_WINDOW', function (payload: { position: 'left' | 'right' | 'center'; width: number; height: number }) { + const { position, width, height } = payload + lastSize = { width, height } + figma.ui.resize(width, height) + dockedPosition = position + lastBoundsKey = '' // форсируем пересчёт на ближайшем тике + applyDock(position, width, height) + }) + + // Останавливаем поллинг при закрытии плагина, чтобы не висел в памяти. + figma.on('close', function () { + clearInterval(dockInterval) }) // UI запрашивает текущие x/y (canvas-space) и размеры окна для отладочного вывода,