Add component name formatters and grid-link navigation

Templates section:
- Define JS formatter functions per template (e.g. resistor, capacitor)
- First non-null result from any formatter is used as display name
- Live preview in template editor against first component
- Display names applied in component list, detail view, and inventory rows

Grid navigation:
- Grid-type inventory entries in component detail view show a '⊞' button
  to navigate directly to that grid's viewer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-21 00:07:01 +00:00
parent 27970e74f9
commit 57c697cbfc
7 changed files with 338 additions and 5 deletions

View File

@@ -15,6 +15,7 @@ import {
list_grid_drafts, get_grid_draft, set_grid_draft, delete_grid_draft,
list_source_images, get_source_image, add_source_image, delete_source_image,
list_grid_images, get_grid_image, set_grid_image, delete_grid_image,
list_component_templates, get_component_template, set_component_template, delete_component_template,
} from './lib/storage.mjs';
mkdirSync('./data/images', { recursive: true });
@@ -423,6 +424,38 @@ app.delete('/api/grid-images/:id', (req, res) => {
ok(res);
});
// ---------------------------------------------------------------------------
// Component templates
// ---------------------------------------------------------------------------
app.get('/api/component-templates', (req, res) => {
ok(res, { templates: list_component_templates() });
});
app.post('/api/component-templates', (req, res) => {
const { name, formatter } = req.body;
if (!name) return fail(res, 'name is required');
const tmpl = { id: generate_id(), name, formatter: formatter ?? '', created_at: Date.now(), updated_at: Date.now() };
set_component_template(tmpl);
ok(res, { template: tmpl });
});
app.put('/api/component-templates/:id', (req, res) => {
const existing = get_component_template(req.params.id);
if (!existing) return fail(res, 'not found', 404);
const updated = { ...existing, updated_at: Date.now() };
if (req.body.name !== undefined) updated.name = req.body.name;
if (req.body.formatter !== undefined) updated.formatter = req.body.formatter;
set_component_template(updated);
ok(res, { template: updated });
});
app.delete('/api/component-templates/:id', (req, res) => {
if (!get_component_template(req.params.id)) return fail(res, 'not found', 404);
delete_component_template(req.params.id);
ok(res);
});
// SPA fallback — serve index.html for any non-API, non-asset path
const INDEX_HTML = new URL('./public/index.html', import.meta.url).pathname;
app.get('/{*path}', (req, res) => res.sendFile(INDEX_HTML));