fix: keep docked window pinned to edge across zoom/pan via viewport poll

This commit is contained in:
Hermes Agent committed 2026-09-09 15:13:05 +02:00
1 parent 106f21b2c4
commit 9f9349efb6
1 file changed
+53 -15
+53 -15
View File
@@ -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) и размеры окна для отладочного вывода,