/** * Editor setup checklist ("preflight"). * * Shown over the canvas the first time the image editor opens (and * re-openable from the footer "Setup" button). Each row is a setup step * with a short how-to and an automatic ✓ / ✕ mark; a manual checkbox lets * the user tick a step the detector cannot see. State is persisted in * localStorage so the checklist doesn't nag once dismissed. */ import { resolveQuickEditModel, resolveQuickEditAgentModel, getAIModelsStatus, } from './ai-models.js'; const DISMISS_KEY = 'ge-preflight-dismissed'; const OVERRIDE_KEY = 'ge-preflight-done'; export const PREFLIGHT_STEPS = [ { id: 'provider', title: 'Image server / AI provider reachable', hint: 'The editor sends edits and generations to your enabled image model (local Qwen). ' + 'This loads automatically — click Retry if it is stale.', action: 'retry', actionLabel: 'Retry', }, { id: 'editModel', title: 'Image edit model set', hint: 'Open AI settings and pick the image edit model. “Auto” uses the local ' + 'Qwen-Image-Edit model.', action: 'model', actionLabel: 'Open settings', }, { id: 'agentModel', title: 'AI agent (vision) model set', hint: 'Optional but recommended. Open AI settings and pick the vision agent — it ' + 'plans edits and can double-check them when “Double-check AI edits” is on.', action: 'agent', actionLabel: 'Open settings', optional: true, }, { id: 'bgRemover', title: 'Background remover available', hint: 'Removes the background via the image server. Use Quick Edit (“remove the ' + 'background”) or the toolbar remove-background tool.', action: 'retry', actionLabel: 'Retry', }, ]; function esc(value) { return String(value == null ? '' : value) .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"'); } function readOverride() { try { const raw = JSON.parse(localStorage.getItem(OVERRIDE_KEY) || '[]'); return new Set(Array.isArray(raw) ? raw : []); } catch (_) { return new Set(); } } function writeOverride(set) { try { localStorage.setItem(OVERRIDE_KEY, JSON.stringify([...set])); } catch (_) {} } export function createPreflight({ canvasArea }) { const el = document.createElement('div'); el.className = 'ge-preflight'; el.hidden = true; el.setAttribute('role', 'dialog'); el.setAttribute('aria-modal', 'true'); el.setAttribute('aria-label', 'Editor setup checklist'); // Compute automatic status for every step + a short detail line. function detect() { const s = getAIModelsStatus() || {}; const models = Array.isArray(s.models) ? s.models : []; const usable = models.filter((m) => m && m.usable); const provider = !!s.ready && usable.length > 0; const edit = resolveQuickEditModel(); const editModel = !!(edit && (edit.model || edit.endpoint)); const agentSel = document.getElementById('ge-ai-command-agent-model'); const agentModel = !!(agentSel && String(agentSel.value || '').trim()); const bgRemover = provider; return { provider: { done: provider, detail: provider ? `${usable.length} image model${usable.length === 1 ? '' : 's'} online` : (s.error ? `Not reachable — ${s.error}` : 'No image models loaded'), }, editModel: { done: editModel, detail: editModel ? (edit.label || edit.model) : 'No edit model resolved', }, agentModel: { done: agentModel, detail: agentModel ? ((resolveQuickEditAgentModel() || {}).label || 'Server default') : 'No agent model selected', }, bgRemover: { done: bgRemover, detail: bgRemover ? 'Serviced by the image server' : 'Unavailable while the server is offline', }, }; } function render() { const detected = detect(); const override = readOverride(); let doneRequired = 0; let requiredCount = 0; const rows = PREFLIGHT_STEPS.map((step) => { const info = detected[step.id] || { done: false, detail: '' }; const manual = override.has(step.id); const done = info.done || manual; const optional = !!step.optional; if (!optional) { requiredCount += 1; if (done) doneRequired += 1; } const stateClass = done ? 'is-done' : (optional ? 'is-optional' : 'is-todo'); const badge = done ? '✓' : (optional ? '○' : '✕'); // Only offer an action / manual tick while the step is still missing — // otherwise a ✓ row would still say “Retry”, which is confusing. const actions = done ? '' : `
A quick check before you start. Fix any ✕ below — optional steps can be skipped.