import { html } from 'lit'; import { LightElement } from '../lib/base.js'; function emptyIgForm() { return { provider_id: '', model_id: '', name: '', priority: 100 }; } export class ModelsImageSection extends LightElement { static properties = { onback: { attribute: false }, _models: { state: true }, _providers: { state: true }, _modal: { state: true }, _form: { state: true }, _saving: { state: true }, _error: { state: true }, _provider: { state: true }, }; constructor() { super(); this.onback = null; this._models = []; this._providers = []; this._modal = null; this._form = emptyIgForm(); this._saving = false; this._error = null; this._provider = null; } connectedCallback() { super.connectedCallback(); this._load(); } async _load() { try { const [modelsRes, providersRes] = await Promise.all([ fetch('/api/image-generate/models'), fetch('/api/llm/providers'), ]); if (!modelsRes.ok) throw new Error(`models: HTTP ${modelsRes.status}`); if (!providersRes.ok) throw new Error(`providers: HTTP ${providersRes.status}`); this._models = await modelsRes.json(); this._providers = await providersRes.json(); } catch (e) { this._error = e.message; } } // ── Add flow ───────────────────────────────────────────────────────────────── _openAdd() { this._error = null; this._provider = null; this._form = emptyIgForm(); this._modal = 'pick-provider'; } _pickProvider(provider) { this._provider = provider; this._form = { ...emptyIgForm(), provider_id: provider.id }; this._modal = 'add'; } // ── Edit flow ──────────────────────────────────────────────────────────────── async _openEdit(m) { this._error = null; try { const res = await fetch(`/api/image-generate/models/${m.id}`); if (!res.ok) throw new Error(`HTTP ${res.status}`); const r = await res.json(); this._provider = this._providers.find(p => p.id === r.provider_id) ?? null; this._form = { provider_id: r.provider_id, model_id: r.model_id, name: r.name, priority: r.priority, }; this._modal = { mode: 'edit', id: r.id, name: r.name }; } catch (e) { this._error = e.message; } } // ── Delete ─────────────────────────────────────────────────────────────────── async _delete(m) { if (!confirm(`Delete image model "${m.name}"?`)) return; try { const res = await fetch(`/api/image-generate/models/${m.id}`, { method: 'DELETE' }); if (!res.ok) throw new Error(await res.text()); await this._load(); } catch (e) { this._error = e.message; } } // ── Submit add ─────────────────────────────────────────────────────────────── async _submitAdd(e) { e.preventDefault(); if (this._saving) return; this._saving = true; this._error = null; const f = this._form; try { const res = await fetch('/api/image-generate/models', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ provider_id: Number(f.provider_id), model_id: f.model_id, name: f.name || f.model_id, priority: Number(f.priority) || 100, }), }); if (!res.ok) throw new Error(await res.text()); this._modal = null; await this._load(); } catch (err) { this._error = err.message; } finally { this._saving = false; } } // ── Submit edit ────────────────────────────────────────────────────────────── async _submitEdit(e) { e.preventDefault(); if (this._saving) return; this._saving = true; this._error = null; const f = this._form; const id = this._modal.id; try { const res = await fetch(`/api/image-generate/models/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ provider_id: Number(f.provider_id), model_id: f.model_id, name: f.name || f.model_id, priority: Number(f.priority) || 100, }), }); if (!res.ok) throw new Error(await res.text()); this._modal = null; await this._load(); } catch (err) { this._error = err.message; } finally { this._saving = false; } } _closeModal() { this._modal = null; this._error = null; } // ── Render card ────────────────────────────────────────────────────────────── _renderCard(m) { const isPlugin = m.from_plugin; return html`
${isPlugin ? html`Plugin` : html`Cloud`} ${m.name}
${isPlugin ? html` ` : html` `}
${!isPlugin ? html`${m.provider_name}` : ''} ${isPlugin ? m.model_id || m.id : m.model_id} #${m.priority}
${m.description ? html`
${m.description}
` : ''}
`; } // ── Modal: pick provider ────────────────────────────────────────────────────── _renderPickProvider() { const igProviders = this._providers.filter(p => Array.isArray(p.supported_types) && p.supported_types.includes('image_generate') ); return html`
{ if (e.target === e.currentTarget) this._closeModal(); }}>
Add Image Model — Choose Provider
${this._error ? html`
${this._error}
` : ''}
${igProviders.map(p => html` `)}
`; } // ── Modal: add / edit form ──────────────────────────────────────────────────── _renderForm(isEdit = false) { const f = this._form; const p = this._provider; const title = isEdit ? html`Edit ${this._modal.name}` : html`Add Image Model ${p?.name}`; return html`
{ if (e.target === e.currentTarget) this._closeModal(); }}>
${title}
${this._error ? html`
${this._error}
` : ''}
isEdit ? this._submitEdit(e) : this._submitAdd(e)}>
this._form = { ...this._form, model_id: e.target.value }} /> ${isEdit ? html`
Model ID cannot be changed after creation.
` : ''}
this._form = { ...this._form, name: e.target.value }} />
this._form = { ...this._form, priority: e.target.value }} />
Lower number = tried first. Default: 100.
`; } render() { const igProviders = this._providers.filter(p => Array.isArray(p.supported_types) && p.supported_types.includes('image_generate') ); const canAdd = igProviders.length > 0; return html`
${this.onback ? html` ` : ''}

Image Generation Models

${this._models.length} model${this._models.length !== 1 ? 's' : ''}
${!canAdd ? html`

No provider supports image generation yet. Add an OpenRouter provider first.

` : ''} ${this._models.some(m => m.from_plugin) ? html`

Models with the Plugin badge are read-only — managed automatically by the plugin that registered them. To add, modify, or remove them, ask the agent directly: it has all the documentation it needs.

` : ''} ${this._error && !this._modal ? html`
${this._error}
` : ''}
${this._models.length === 0 ? html`

No image generation models configured.

${canAdd ? html` ` : ''}
` : this._models.map(m => this._renderCard(m))}
${this._modal === 'pick-provider' ? this._renderPickProvider() : ''} ${this._modal === 'add' ? this._renderForm(false) : ''} ${this._modal?.mode === 'edit' ? this._renderForm(true) : ''} `; } }