The ManifestEditor had a feedback loop:
1. State change → buildManifest changes → onChange notifies parent
2. Parent updates manifestData → new manifest prop
3. Loading effect sets all state from manifest (arrays get new refs even if same content)
4. New array refs → buildManifest changes → onChange fires again → loop
Fix: track the last-sent manifest via a ref and only call onChange when the
serialized built manifest actually differs. This breaks the cycle because after
the loading effect syncs state, the rebuilt manifest is identical in content
so we skip the parent notification.
- Add tool_definitions API client with types for manifests
- Add ManifestEditor component: base image selector, package editors
(apt/npm/pip/node), script editors (build/startup), mount schema
designer, runtime config, and live preview panel
- Integrate ManifestEditor into Tool Workshop as 'Manifest (Declarative)'
definition type alongside Compose and Dockerfile
- Update ToolType API types to include manifest_id and 'manifest'
definition_type
- Frontend builds clean, TypeScript typecheck passes