diff --git a/package.json b/package.json index 591ab4b..8559f6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "figma-ai-plugin", - "version": "0.1.15", + "version": "0.1.16", "private": true, "dependencies": { "@create-figma-plugin/ui": "^4.0.3", diff --git a/src/main.ts b/src/main.ts index d4d2b10..55ab82e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,16 @@ import { emit, on, showUI } from '@create-figma-plugin/utilities' +// Probe whether the plugin sandbox can reach the public internet. +// Used on plugin open (so the user sees network status immediately) and before each model fetch. +async function probeConnectivity () { + try { + const pr = await fetch('https://example.com') + emit('PROBE_RESULT', { msg: 'example.com reachable · status=' + pr.status }) + } catch (pe) { + emit('PROBE_RESULT', { msg: 'example.com UNREACHABLE · ' + (pe != null ? (typeof pe === 'string' ? pe : JSON.stringify(pe)) : 'unknown') }) + } +} + export default function () { // Глобальный ярлык relaunch в правой панели Figma (команда runAI из manifest.json) figma.root.setRelaunchData({ runAI: '' }) @@ -511,13 +522,8 @@ export default function () { emit('MODELS_RESULT', { ok: false, error: 'fetch is not available in the plugin main thread', models: [], status: 0, requestEcho: echo }) return } - // diagnostic: can the sandbox reach the public internet at all? - try { - const pr = await fetch('https://example.com') - emit('PROBE_RESULT', { msg: 'example.com reachable · status=' + pr.status }) - } catch (pe) { - emit('PROBE_RESULT', { msg: 'example.com UNREACHABLE · ' + (pe != null ? (typeof pe === 'string' ? pe : JSON.stringify(pe)) : 'unknown') }) - } + // Diagnostic: can the sandbox reach the public internet at all? + await probeConnectivity() const base = String(payload.baseUrl == null ? '' : payload.baseUrl) .replace(/`/g, '') // drop stray markdown backticks .replace(/^@url:\s*/i, '') // drop '@url:' annotation if pasted from chat @@ -559,6 +565,9 @@ export default function () { showUI({ width: 380, height: 480, themeColors: true, title: 'AI assistant (MVP)' }) + // On open, proactively probe connectivity so the user sees network status without clicking Fetch. + setTimeout(function () { probeConnectivity() }, 600) + // Restore persisted window placement (saved in clientStorage on the Figma file). // Default when nothing is stored: behave like the Center button (50% viewport, centered). try { @@ -573,11 +582,11 @@ export default function () { const ch = Math.round(maxH / 2) figma.ui.resize(Math.max(1, cw), Math.max(1, ch)) lastSize = { width: cw, height: ch } - requestAnimationFrame(function () { - requestAnimationFrame(function () { + setTimeout(function () { + setTimeout(function () { applyDock('center', cw, ch, false) - }) - }) + }, 0) + }, 0) return } if (saved.mode === 'dock') { @@ -596,12 +605,12 @@ export default function () { const state = computeDock(saved.position, 320, 200, true) figma.ui.resize(Math.max(1, state.width), Math.max(1, state.height)) lastSize = { width: clampWindowSize(state).width, height: clampWindowSize(state).height } - requestAnimationFrame(function () { - requestAnimationFrame(function () { + setTimeout(function () { + setTimeout(function () { applyDock(saved.position, lastSize.width, lastSize.height, false) lastBoundsKey = '' // force dock poll to keep it pinned on zoom/pan - }) - }) + }, 0) + }, 0) } } else { // Free placement: restore exact canvas-space position + size. diff --git a/src/ui.tsx b/src/ui.tsx index 60c693d..5f955f0 100644 --- a/src/ui.tsx +++ b/src/ui.tsx @@ -30,7 +30,7 @@ const MIN_WINDOW_SIZE = { width: 320, height: 200 } // very high — actual clamp is done by main via clampWindowSize (bounds*zoom). // Full plugin version shown in the footer status bar. Keep in sync with package.json "version". -const PLUGIN_VERSION = '0.1.15' +const PLUGIN_VERSION = '0.1.16' const fieldStyle = { display: 'flex', @@ -274,6 +274,14 @@ function Plugin () { .catch(function () {}) .finally(function () { setLoaded(true) }) }, []) + + // Auto-persist settings (incl. API key) on every change — no need to click Save. + // Gated on `loaded` so we don't overwrite stored settings with defaults before load completes. + useEffect(function () { + if (!loaded) return + const t = setTimeout(function () { saveSettings(settings) }, 400) + return function () { clearTimeout(t) } + }, [settings, loaded]) useEffect(function () { try { logEvent('Plugin loaded') } catch (e) {} }, []) const resizeRef = useRef({ active: false, dir: '', startX: 0, startY: 0, startW: 0, startH: 0, fixedAxis: 'none' as 'none' | 'x' | 'y' }) // Current dock position (for "window is edge-docked" detection in UI). Center and