From b04059cdd6a4d9ff30cde7f8e80673b32f212d86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Thu, 20 Aug 2026 17:58:28 +0000 Subject: [PATCH] untrack the atelier panel the platform should never have held MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `git mv` from src/ into plugins/ staged a rename, and a staged rename beats .gitignore — so the platform repo committed a file belonging to a plugin that owns its own repository. the file is unchanged on disk and is committed where it belongs, in plugins/atelier. worth knowing generally: moving a file OUT of a tracked directory into an ignored one needs `git rm --cached` afterwards. the ignore rule does not undo a rename that is already in the index. --- plugins/atelier/web/Providers/Providers.tsx | 248 -------------------- 1 file changed, 248 deletions(-) delete mode 100644 plugins/atelier/web/Providers/Providers.tsx diff --git a/plugins/atelier/web/Providers/Providers.tsx b/plugins/atelier/web/Providers/Providers.tsx deleted file mode 100644 index 3c7165f7..00000000 --- a/plugins/atelier/web/Providers/Providers.tsx +++ /dev/null @@ -1,248 +0,0 @@ -import { useEffect, useState } from 'react'; -import { toast } from 'sonner'; -import { useClient } from 'hooks/useClient'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; - -// Atelier's own Providers screen — moved to Settings rather than a panel, per -// plugins/atelier/PLUGIN.md: image-generation providers are a global, owner-level configuration -// (which adapter, which model, whose key), the same shape as the Apify token above rather than -// something worth a dedicated working panel. Talks to /api/atelier/providers over HTTP through -// useClient(), same as every other authenticated screen — this does not import anything from -// plugins/atelier/, so it stays on the right side of "a plugin importing platform code is fine; the -// reverse is not". - -type ProviderStatus = { - id: string; - name: string; - adapter: string; - model: string; - enabled: boolean; - is_default: boolean; - configured: boolean; - key_source: 'database' | 'environment' | null; - env_var: string; -}; - -type Adapter = { - name: string; - label: string; - models: string[]; - default_model: string; - env_var: string; -}; - -export const AtelierProviders = () => { - const client = useClient(); - const [providers, setProviders] = useState([]); - const [adapters, setAdapters] = useState([]); - const [loading, setLoading] = useState(true); - const [busy, setBusy] = useState(null); - const [keyDrafts, setKeyDrafts] = useState>({}); - const [newAdapter, setNewAdapter] = useState(''); - const [newModel, setNewModel] = useState(''); - - useEffect(() => { - Promise.all([ - client.get<{ data: ProviderStatus[] }>('/atelier/providers').then((r) => r.data), - client.get<{ data: Adapter[] }>('/atelier/providers/adapters').then((r) => r.data), - ]) - .then(([rows, catalogue]) => { - setProviders(rows); - setAdapters(catalogue); - if (catalogue[0]) { - setNewAdapter(catalogue[0].name); - setNewModel(catalogue[0].default_model); - } - }) - .catch(() => toast.error('Failed to load Atelier providers')) - .finally(() => setLoading(false)); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - const run = async (key: string, action: () => Promise<{ data: ProviderStatus[] }>) => { - setBusy(key); - try { - setProviders((await action()).data); - } catch { - toast.error('That did not work — check the server logs'); - } finally { - setBusy(null); - } - }; - - const modelsFor = (adapterName: string) => adapters.find((a) => a.name === adapterName)?.models ?? []; - - const patch = (id: string, values: Record) => - run(id, () => client.patch<{ data: ProviderStatus[] }>(`/atelier/providers/${id}`, values)); - - const saveKey = async (id: string) => { - const apiKey = keyDrafts[id]?.trim(); - if (!apiKey) return; - await patch(id, { apiKey }); - setKeyDrafts((current) => ({ ...current, [id]: '' })); - toast.success('Key saved'); - }; - - const add = () => - run('new', () => - client.post<{ data: ProviderStatus[] }>('/atelier/providers', { adapter: newAdapter, model: newModel }), - ); - - const remove = (id: string) => run(id, () => client.delete<{ data: ProviderStatus[] }>(`/atelier/providers/${id}`)); - - if (loading) return null; - - return ( -
-
-

Add a provider

-

- An adapter and a model. The same adapter can back several providers. -

-
- - - - - -
-
- - {providers.length === 0 ? ( -

No providers yet. Add one above.

- ) : ( - providers.map((provider) => { - const busyHere = busy === provider.id; - return ( -
-
-
-

- {provider.name} - {provider.is_default && ( - - default - - )} -

-

{describe(provider)}

-
-
- {!provider.is_default && ( - - )} - -
-
- -
- - {/* A form, so Enter saves. Pasting a key and pressing Enter is the strongest habit there - is in a password field, and outside a form it silently does nothing — which reads as - "the key did not save". TODO.md tracks nineteen panels with this same defect; this is - not the twentieth. */} -
{ - ev.preventDefault(); - void saveKey(provider.id); - }} - > - setKeyDrafts((current) => ({ ...current, [provider.id]: ev.target.value }))} - /> - -
-

- Stored encrypted. The server never sends a key back, so this field always starts empty. -

-
- - -
- ); - }) - )} -
- ); -}; - -function describe(provider: ProviderStatus): string { - const base = `${provider.adapter} · ${provider.model}`; - if (!provider.configured) return `${base} — no key. Set one here, or as ${provider.env_var}.`; - if (provider.key_source === 'environment') return `${base} — key from ${provider.env_var}.`; - return `${base} — key stored and encrypted.`; -}