feat: maximize window to full viewport (maxViewport = bounds*zoom), clamp drag

This commit is contained in:
Hermes Agent committed 2026-09-09 16:05:50 +02:00
1 parent 4228357a35
commit b2ae4c44d6
2 files changed
+43 -10

No files matched your search

+26 -2
View File
@@ -51,9 +51,24 @@ export default function () {
}
}, 250)
// Кламп размера окна: не меньше минимума и не больше видимого viewport.
// Видимый viewport в ЭКРАННЫХ пикселях = bounds * zoom (bounds — в canvas-space,
// zoom переводит в px). useWindowResize при drag даёт большие значения — main
// приводит к допустимому, чтобы окно не вылезало за canvas и не ломало Figma.
function clampWindowSize (size: { width: number; height: number }) {
const b = figma.viewport.bounds
const zoom = figma.viewport.zoom || 1
const maxW = Math.max(320, Math.round(b.width * zoom))
const maxH = Math.max(200, Math.round(b.height * zoom))
return {
width: Math.max(320, Math.min(maxW, Math.round(size.width))),
height: Math.max(200, Math.min(maxH, Math.round(size.height)))
}
}
// Ресайз окна плагина (drag за угол в UI -> useWindowResize -> RESIZE_WINDOW)
on('RESIZE_WINDOW', function (windowSize: { width: number; height: number }) {
const { width, height } = windowSize
const { width, height } = clampWindowSize(windowSize)
lastSize = { width, height }
figma.ui.resize(width, height)
// Если окно задокено — держим прижатым к краю при смене размера
@@ -64,7 +79,8 @@ export default function () {
// Эмуляция докинга: прижать окно к краям canvas (left/right/center).
on('DOCK_WINDOW', function (payload: { position: 'left' | 'right' | 'center'; width: number; height: number }) {
const { position, width, height } = payload
const { position } = payload
const { width, height } = clampWindowSize(payload)
lastSize = { width, height }
figma.ui.resize(width, height)
dockedPosition = position
@@ -109,6 +125,13 @@ export default function () {
const cx = Math.round(x + wCanvas / 2)
const cy = Math.round(y + hCanvas / 2)
// Максимальный размер окна (экранные px) = весь видимый viewport (bounds * zoom).
// Используется кнопкой "развернуть" в UI и как верхняя граница drag-ресайза.
const maxViewport = {
width: Math.max(320, Math.round(b.width * zoom)),
height: Math.max(200, Math.round(b.height * zoom))
}
emit('WINDOW_INFO', {
x,
y,
@@ -121,6 +144,7 @@ export default function () {
width: Math.round(b.width),
height: Math.round(b.height)
},
maxViewport,
gapL,
gapR,
gapT,
+17 -8
View File
@@ -17,7 +17,9 @@ import { h, Fragment } from 'preact'
import { useState, useEffect } from 'preact/hooks'
const MIN_WINDOW_SIZE = { width: 320, height: 200 }
const MAX_WINDOW_SIZE = { width: 800, height: 800 }
// Статичного максимума нет: "развернуть" целится в весь viewport (maxViewport из main),
// а верхняя граница drag-ресайза тоже приходит оттуда. useWindowResize ставим очень
// большой max — реальный кламп делает main через clampWindowSize (bounds*zoom).
// Одна строка отладочного вывода: имя переменной из кода (как в main.ts),
// опционально — формула расчёта, и само значение (моноширинное, по правому краю).
@@ -49,6 +51,7 @@ function Plugin () {
x: number; y: number; width: number; height: number
zoom: number
viewport: { x: number; y: number; width: number; height: number }
maxViewport: { width: number; height: number }
gapL: number; gapR: number; gapT: number; gapB: number
center: { x: number; y: number }
} | null>(null)
@@ -65,8 +68,10 @@ function Plugin () {
}, {
minWidth: 320,
minHeight: 200,
maxWidth: 800,
maxHeight: 800
// Верхний предел оставляем большим — реальный кламп (весь viewport) делает main
// через clampWindowSize (bounds * zoom), иначе на маленьком viewport окно не растянуть.
maxWidth: 100000,
maxHeight: 100000
})
// Слушаем ответ от main с актуальными x/y (canvas-space) и размерами окна
@@ -75,6 +80,7 @@ function Plugin () {
x: number; y: number; width: number; height: number
zoom: number
viewport: { x: number; y: number; width: number; height: number }
maxViewport: { width: number; height: number }
gapL: number; gapR: number; gapT: number; gapB: number
center: { x: number; y: number }
}) {
@@ -177,10 +183,12 @@ function Plugin () {
}, [])
function toggleWindowSize () {
const max = windowInfo != null
? windowInfo.maxViewport
: { width: 800, height: 800 }
const isMaximized =
currentWindowSize.width >= MAX_WINDOW_SIZE.width &&
currentWindowSize.height >= MAX_WINDOW_SIZE.height
const next = isMaximized ? { ...MIN_WINDOW_SIZE } : { ...MAX_WINDOW_SIZE }
currentWindowSize.width >= max.width && currentWindowSize.height >= max.height
const next = isMaximized ? { ...MIN_WINDOW_SIZE } : { ...max }
setCurrentWindowSize(next)
emit('RESIZE_WINDOW', next)
refreshInfo()
@@ -195,9 +203,9 @@ function Plugin () {
refreshInfo()
}
const max = windowInfo != null ? windowInfo.maxViewport : { width: 800, height: 800 }
const isMaximized =
currentWindowSize.width >= MAX_WINDOW_SIZE.width &&
currentWindowSize.height >= MAX_WINDOW_SIZE.height
currentWindowSize.width >= max.width && currentWindowSize.height >= max.height
return (
<Fragment>
@@ -246,6 +254,7 @@ function Plugin () {
<Field name="figma.viewport.bounds" value={`${windowInfo.viewport.x},${windowInfo.viewport.y} · ${windowInfo.viewport.width}×${windowInfo.viewport.height}`} />
<Field name="lastSize.width" value={windowInfo.width} />
<Field name="lastSize.height" value={windowInfo.height} />
<Field name="maxViewport (bounds·zoom)" formula="bounds.width*zoom, bounds.height*zoom" value={`${windowInfo.maxViewport.width}×${windowInfo.maxViewport.height}`} />
</Section>
{/* Расчётные — посчитаны в main.ts из вышеперечисленных raw-значений */}
<Section title="РАСЧЁТ · в main.ts">