* prevent duplicate template entries on repeated save
* add delete button to saved template list
Templates can now be removed from the template picker via a hover x button, calling the existing DELETE /api/templates/:id endpoint.
* add missing onDeleteTemplate prop in test fixtures
* add template deletion flow test for NewProjectPanel
* reject template names longer than 100 characters
* preserve original createdAt on template update
Reduces the New Project panel's top-level tab count by collapsing the
three media surfaces into a single Media tab with an inner segmented
control, and polishes the controls inside that tab so they stop
dominating the panel:
- Media tab + segmented (Image / Video / Audio) inside the panel body.
Underlying ProjectKind branches and submission contract unchanged —
the daemon still receives kind=image/video/audio.
- Model picker rewritten as a combobox: one trigger row + searchable,
provider-grouped popover with Recommended badges. Replaces the flat
grid of provider-grouped cards that scrolled past the fold once the
fourth provider landed.
- Aspect picker compressed from a 5-card grid to a single row of
segmented pills with mini ratio glyphs.
- Image surface no longer carries a free-form Style notes field; it
was redundant with the prompt template + main prompt input.
- Live artifact tab locks fidelity to high-fidelity (the wireframe
option is now hidden) — a wireframe live artifact doesn't make
sense and the picker added noise.
i18n: adds tabMedia / titleMedia / model* keys across all 18 locales,
removes imageStyleLabel / imageStylePlaceholder. Tests + e2e selectors
updated to drive the new Media tab + segmented surface flow.
* fix(web): restore media config from daemon on startup
* fix(media): preserve stored keys on settings save
* fix(web): harden daemon media restore flow
* fix(web): unify media provider empty-state rules
* fix(desktop): retry loading discovered web url
* fix(web): preserve local media providers on partial daemon reload
* fix(web): preserve media providers on daemon reload
* fix(web): skip media migration for masked-only local state
* fix(web): preserve daemon media state across reloads