fix: guard against malformed stored settings (crash → blank window); add ErrorBoundary to surface render errors
This commit is contained in:
1 parent
bfd3638093
commit
895b254c2a
3 files changed
+36
-7
No files matched your search
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "figma-ai-plugin",
|
||||
"version": "0.1.3",
|
||||
"version": "0.1.4",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@create-figma-plugin/ui": "^4.0.3",
|
||||
|
||||
+9
-3
@@ -52,12 +52,18 @@ export function createApinexProvider (): ProviderConfig {
|
||||
}
|
||||
|
||||
export async function loadSettings (): Promise<Settings> {
|
||||
const defaults = defaultSettings()
|
||||
try {
|
||||
const stored = await figma.clientStorage.getAsync(SETTINGS_KEY)
|
||||
if (stored == null || stored.version !== 1) return defaultSettings()
|
||||
return stored as Settings
|
||||
if (stored == null || stored.version !== 1) return defaults
|
||||
const merged: Settings = { ...defaults, ...(stored as Settings) }
|
||||
if (!Array.isArray(merged.providers) || merged.providers.length === 0) {
|
||||
merged.providers = defaults.providers
|
||||
merged.activeProviderId = defaults.providers[0].id
|
||||
}
|
||||
return merged
|
||||
} catch (e) {
|
||||
return defaultSettings()
|
||||
return defaults
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+26
-3
@@ -18,7 +18,7 @@ import {
|
||||
IconComponent24
|
||||
} from '@create-figma-plugin/ui'
|
||||
import { emit, on } from '@create-figma-plugin/utilities'
|
||||
import { h, Fragment } from 'preact'
|
||||
import { h, Fragment, Component } from 'preact'
|
||||
import { useState, useEffect, useRef } from 'preact/hooks'
|
||||
import MenuItem from './MenuItem'
|
||||
import { type Settings, type ProviderConfig, defaultSettings, loadSettings, saveSettings, settingsToJSON, settingsFromJSON, APINEX_BASE_URL } from './settings'
|
||||
@@ -29,7 +29,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.3'
|
||||
const PLUGIN_VERSION = '0.1.4'
|
||||
|
||||
const fieldStyle = {
|
||||
display: 'flex',
|
||||
@@ -55,6 +55,26 @@ function Separator () {
|
||||
)
|
||||
}
|
||||
|
||||
class ErrorBoundary extends Component<{ children: any }, { error: Error | null }> {
|
||||
constructor (props: { children: any }) {
|
||||
super(props)
|
||||
this.state = { error: null }
|
||||
}
|
||||
componentDidCatch (error: Error) {
|
||||
this.setState({ error: error })
|
||||
}
|
||||
render (props: { children: any }, state: { error: Error | null }) {
|
||||
if (state.error != null) {
|
||||
return (
|
||||
<pre style={{ margin: 0, padding: 8, color: 'var(--figma-color-text-danger)', whiteSpace: 'pre-wrap', fontSize: '10px', overflow: 'auto' }}>
|
||||
{String(state.error && state.error.stack ? state.error.stack : state.error)}
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
return props.children
|
||||
}
|
||||
}
|
||||
|
||||
function SettingsPanel (props: { settings: Settings, setSettings: (s: Settings) => void }) {
|
||||
const { settings, setSettings } = props
|
||||
const [showKey, setShowKey] = useState(false)
|
||||
@@ -65,7 +85,8 @@ function SettingsPanel (props: { settings: Settings, setSettings: (s: Settings)
|
||||
settingsRef.current = settings
|
||||
|
||||
// Static single provider (APInex). Always edit providers[0].
|
||||
const provider = settings.providers[0] != null ? settings.providers[0] : null
|
||||
const providers = Array.isArray(settings.providers) ? settings.providers : []
|
||||
const provider = providers.length > 0 ? providers[0] : null
|
||||
|
||||
useEffect(function () {
|
||||
const dispose = on('MODELS_RESULT', function (payload: { ok: boolean, models: string[], error?: string }) {
|
||||
@@ -488,6 +509,7 @@ function Plugin () {
|
||||
return (
|
||||
<div className="plugin-root" style={{ position: 'relative', width: '100vw', height: '100vh' }}>
|
||||
<style>{'html,body{height:100%;overflow:hidden}'}</style>
|
||||
<ErrorBoundary>
|
||||
<Container space="medium" style={{ height: '100%', display: 'flex', flexDirection: 'column', padding: 0 }}>
|
||||
{/* Header — navigation bar */}
|
||||
<header style={{ display: 'flex', alignItems: 'center', flex: '0 0 auto', padding: 'var(--space-8)' }}>
|
||||
@@ -523,6 +545,7 @@ function Plugin () {
|
||||
<Muted style={{ fontSize: '11px' }}>Version {PLUGIN_VERSION}</Muted>
|
||||
</footer>
|
||||
</Container>
|
||||
</ErrorBoundary>
|
||||
{/* More-menu modal — old-plugin style: library Modal + Container + Stack of MenuItem */}
|
||||
<Modal open={moreOpen} onClose={function () { setMoreOpen(false) }} onOverlayClick={function () { setMoreOpen(false) }}>
|
||||
<Container space="extraSmall">
|
||||
|
||||
Reference in new issue
Block a user