2026-04-29 05:24:39 +07:00
|
|
|
/**
|
|
|
|
|
* CustomChipDialog — modal for editing/compiling a Velxio custom chip.
|
|
|
|
|
*
|
|
|
|
|
* Two tabs:
|
|
|
|
|
* - "Examples": gallery of pre-built chips. Click → loads the source.
|
|
|
|
|
* - "Editor": Monaco editors for chip.c and chip.json + Compile button +
|
|
|
|
|
* stdout/stderr panel + Save button.
|
|
|
|
|
*
|
|
|
|
|
* The Save action persists the compiled WASM (base64) into the component's
|
|
|
|
|
* `properties` so the chip is fully self-contained inside the project.
|
|
|
|
|
*/
|
|
|
|
|
import { useState, useMemo, useEffect } from 'react';
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
import { useTranslation } from 'react-i18next';
|
2026-04-29 05:24:39 +07:00
|
|
|
import Editor from '@monaco-editor/react';
|
|
|
|
|
import { CHIP_EXAMPLES, BLANK_CHIP, type ChipExample } from './chipExamples';
|
|
|
|
|
import { compileChip, type ChipCompileResult } from '../../services/chipCompileService';
|
|
|
|
|
|
|
|
|
|
export interface CustomChipDialogProps {
|
|
|
|
|
/** Initial chip data (from component.properties). */
|
|
|
|
|
initial: {
|
|
|
|
|
chipName: string;
|
|
|
|
|
sourceC: string;
|
|
|
|
|
chipJson: string;
|
|
|
|
|
wasmBase64: string;
|
|
|
|
|
attrs?: Record<string, number>;
|
|
|
|
|
};
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
onSave: (data: {
|
|
|
|
|
chipName: string;
|
|
|
|
|
sourceC: string;
|
|
|
|
|
chipJson: string;
|
|
|
|
|
wasmBase64: string;
|
|
|
|
|
attrs: Record<string, number>;
|
|
|
|
|
}) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface AttributeDef {
|
|
|
|
|
name: string;
|
|
|
|
|
label?: string;
|
|
|
|
|
type?: 'int' | 'float' | 'number';
|
|
|
|
|
default?: number;
|
|
|
|
|
min?: number;
|
|
|
|
|
max?: number;
|
|
|
|
|
step?: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function parseAttributes(chipJson: string): AttributeDef[] {
|
|
|
|
|
try {
|
|
|
|
|
const obj = JSON.parse(chipJson);
|
|
|
|
|
if (Array.isArray(obj.attributes)) {
|
|
|
|
|
return obj.attributes.map((a: any): AttributeDef => ({
|
|
|
|
|
name: String(a.name ?? ''),
|
|
|
|
|
label: typeof a.label === 'string' ? a.label : undefined,
|
|
|
|
|
type: a.type === 'float' || a.type === 'int' ? a.type : 'number',
|
|
|
|
|
default: typeof a.default === 'number' ? a.default : undefined,
|
|
|
|
|
min: typeof a.min === 'number' ? a.min : undefined,
|
|
|
|
|
max: typeof a.max === 'number' ? a.max : undefined,
|
|
|
|
|
step: typeof a.step === 'number' ? a.step : undefined,
|
|
|
|
|
})).filter((a: AttributeDef) => a.name);
|
|
|
|
|
}
|
|
|
|
|
} catch { /* ignore */ }
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Tab = 'examples' | 'editor';
|
|
|
|
|
|
|
|
|
|
export const CustomChipDialog = ({ initial, onClose, onSave }: CustomChipDialogProps) => {
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
const { t } = useTranslation();
|
2026-04-29 05:24:39 +07:00
|
|
|
// If the chip already has source code, skip the examples tab on open.
|
|
|
|
|
const [tab, setTab] = useState<Tab>(initial.sourceC.trim() ? 'editor' : 'examples');
|
|
|
|
|
const [chipName, setChipName] = useState(initial.chipName || 'My Chip');
|
|
|
|
|
const [sourceC, setSourceC] = useState(initial.sourceC || BLANK_CHIP.sourceC);
|
|
|
|
|
const [chipJson, setChipJson] = useState(initial.chipJson || BLANK_CHIP.chipJson);
|
|
|
|
|
const [wasmBase64, setWasmBase64] = useState(initial.wasmBase64 || '');
|
|
|
|
|
|
|
|
|
|
const [compiling, setCompiling] = useState(false);
|
|
|
|
|
const [result, setResult] = useState<ChipCompileResult | null>(null);
|
|
|
|
|
const [attrs, setAttrs] = useState<Record<string, number>>(initial.attrs ?? {});
|
|
|
|
|
|
|
|
|
|
const attrDefs = useMemo(() => parseAttributes(chipJson), [chipJson]);
|
|
|
|
|
|
|
|
|
|
// When attribute defs change (e.g. user loaded a new example), seed defaults
|
|
|
|
|
// for any newly-introduced attribute that doesn't have a saved value yet.
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setAttrs((prev) => {
|
|
|
|
|
const next = { ...prev };
|
|
|
|
|
let changed = false;
|
|
|
|
|
for (const a of attrDefs) {
|
|
|
|
|
if (!(a.name in next) && a.default !== undefined) {
|
|
|
|
|
next[a.name] = a.default;
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return changed ? next : prev;
|
|
|
|
|
});
|
|
|
|
|
}, [attrDefs]);
|
|
|
|
|
|
|
|
|
|
// Categories shown as section headers in the gallery.
|
|
|
|
|
const grouped = useMemo(() => {
|
|
|
|
|
const m = new Map<string, ChipExample[]>();
|
|
|
|
|
for (const e of CHIP_EXAMPLES) {
|
|
|
|
|
if (!m.has(e.category)) m.set(e.category, []);
|
|
|
|
|
m.get(e.category)!.push(e);
|
|
|
|
|
}
|
|
|
|
|
return Array.from(m.entries());
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
// Pull chipName out of chip.json whenever it changes — keeps a single source
|
|
|
|
|
// of truth without forcing the user to edit two fields.
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
try {
|
|
|
|
|
const obj = JSON.parse(chipJson);
|
|
|
|
|
if (obj && typeof obj.name === 'string' && obj.name.trim()) {
|
|
|
|
|
setChipName(obj.name);
|
|
|
|
|
}
|
|
|
|
|
} catch { /* user is mid-edit, ignore */ }
|
|
|
|
|
}, [chipJson]);
|
|
|
|
|
|
|
|
|
|
const loadExample = (e: ChipExample) => {
|
|
|
|
|
setSourceC(e.sourceC);
|
|
|
|
|
setChipJson(e.chipJson);
|
|
|
|
|
setWasmBase64(''); // force re-compile so the binary matches the loaded source
|
|
|
|
|
setResult(null);
|
|
|
|
|
setTab('editor');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const doCompile = async () => {
|
|
|
|
|
setCompiling(true);
|
|
|
|
|
setResult(null);
|
|
|
|
|
try {
|
|
|
|
|
const r = await compileChip(sourceC, chipJson);
|
|
|
|
|
setResult(r);
|
|
|
|
|
if (r.success && r.wasm_base64) setWasmBase64(r.wasm_base64);
|
|
|
|
|
} finally {
|
|
|
|
|
setCompiling(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const canSave = wasmBase64.length > 0;
|
|
|
|
|
const doSave = () => {
|
|
|
|
|
if (!canSave) return;
|
|
|
|
|
onSave({ chipName, sourceC, chipJson, wasmBase64, attrs });
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div style={overlayStyle} onClick={onClose}>
|
|
|
|
|
<div style={dialogStyle} onClick={(e) => e.stopPropagation()}>
|
|
|
|
|
<div style={headerStyle}>
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
<strong style={{ flex: 1 }}>{t('editor.customChip.title', { chipName })}</strong>
|
2026-04-29 05:24:39 +07:00
|
|
|
<button style={closeBtn} onClick={onClose}>✕</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={tabsRowStyle}>
|
|
|
|
|
<button
|
|
|
|
|
style={{ ...tabBtn, ...(tab === 'examples' ? tabActive : null) }}
|
|
|
|
|
onClick={() => setTab('examples')}
|
|
|
|
|
>
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
{t('editor.customChip.tabExamples')}
|
2026-04-29 05:24:39 +07:00
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
style={{ ...tabBtn, ...(tab === 'editor' ? tabActive : null) }}
|
|
|
|
|
onClick={() => setTab('editor')}
|
|
|
|
|
>
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
{t('editor.customChip.tabEditor')}
|
2026-04-29 05:24:39 +07:00
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div style={bodyStyle}>
|
|
|
|
|
{tab === 'examples' && (
|
|
|
|
|
<div style={{ overflow: 'auto', padding: 12 }}>
|
|
|
|
|
<button style={blankBtn} onClick={() => loadExample(BLANK_CHIP)}>
|
|
|
|
|
+ Start from blank
|
|
|
|
|
</button>
|
|
|
|
|
{grouped.map(([cat, list]) => (
|
|
|
|
|
<div key={cat} style={{ marginTop: 18 }}>
|
|
|
|
|
<div style={categoryStyle}>{cat.toUpperCase()}</div>
|
|
|
|
|
<div style={gridStyle}>
|
|
|
|
|
{list.map((ex) => (
|
|
|
|
|
<button
|
|
|
|
|
key={ex.id}
|
|
|
|
|
style={cardBtn}
|
|
|
|
|
onClick={() => loadExample(ex)}
|
|
|
|
|
title={ex.description}
|
|
|
|
|
>
|
|
|
|
|
<div style={cardName}>{ex.name}</div>
|
|
|
|
|
<div style={cardDesc}>{ex.description}</div>
|
|
|
|
|
</button>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{tab === 'editor' && (
|
|
|
|
|
<div style={editorLayout}>
|
|
|
|
|
<div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}>
|
|
|
|
|
<div style={editorLabel}>chip.c</div>
|
|
|
|
|
<div style={{ flex: 1 }}>
|
|
|
|
|
<Editor
|
|
|
|
|
height="100%"
|
|
|
|
|
language="cpp"
|
|
|
|
|
theme="vs-dark"
|
|
|
|
|
value={sourceC}
|
|
|
|
|
onChange={(v) => { setSourceC(v ?? ''); setWasmBase64(''); }}
|
|
|
|
|
options={{
|
|
|
|
|
minimap: { enabled: false },
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
automaticLayout: true,
|
|
|
|
|
scrollBeyondLastLine: false,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div style={{ width: 360, display: 'flex', flexDirection: 'column' }}>
|
|
|
|
|
<div style={editorLabel}>chip.json</div>
|
|
|
|
|
<div style={{ flex: 1, minHeight: 200 }}>
|
|
|
|
|
<Editor
|
|
|
|
|
height="100%"
|
|
|
|
|
language="json"
|
|
|
|
|
theme="vs-dark"
|
|
|
|
|
value={chipJson}
|
|
|
|
|
onChange={(v) => setChipJson(v ?? '')}
|
|
|
|
|
options={{
|
|
|
|
|
minimap: { enabled: false },
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
automaticLayout: true,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
{attrDefs.length > 0 && (
|
|
|
|
|
<div style={attrPanelStyle}>
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
<div style={attrPanelHeader}>{t('editor.customChip.attributes')}</div>
|
2026-04-29 05:24:39 +07:00
|
|
|
{attrDefs.map((a) => (
|
|
|
|
|
<AttrRow
|
|
|
|
|
key={a.name}
|
|
|
|
|
def={a}
|
|
|
|
|
value={attrs[a.name] ?? a.default ?? 0}
|
|
|
|
|
onChange={(v) => setAttrs((prev) => ({ ...prev, [a.name]: v }))}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{tab === 'editor' && (
|
|
|
|
|
<div style={resultPanelStyle}>
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
{compiling && <span style={{ color: '#ffa500' }}>{t('editor.customChip.compiling')}</span>}
|
2026-04-29 05:24:39 +07:00
|
|
|
{!compiling && result && result.success && (
|
|
|
|
|
<span style={{ color: '#22c55e' }}>
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
{t('editor.customChip.compiledOk', { kb: (result.byte_size / 1024).toFixed(1) })}
|
2026-04-29 05:24:39 +07:00
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
{!compiling && result && !result.success && (
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
<pre style={errorPreStyle}>{result.error || t('editor.customChip.compileFailed')}{'\n'}{result.stderr}</pre>
|
2026-04-29 05:24:39 +07:00
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<div style={footerStyle}>
|
|
|
|
|
{tab === 'editor' && (
|
|
|
|
|
<button style={compileBtn} onClick={doCompile} disabled={compiling}>
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
{compiling ? t('editor.customChip.compiling') : t('editor.customChip.compile')}
|
2026-04-29 05:24:39 +07:00
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
<div style={{ flex: 1 }} />
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
<button style={cancelBtn} onClick={onClose}>{t('editor.customChip.cancel')}</button>
|
2026-04-29 05:24:39 +07:00
|
|
|
<button style={canSave ? saveBtn : saveBtnDisabled} disabled={!canSave} onClick={doSave}>
|
feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.
Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
+ hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
wire-mode variants, Arduino Pin label, rotate / delete buttons +
the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
with i18next pluralisation, filter dropdown, autoscroll label,
Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
Editor tabs, Attributes panel header, compile status messages
including the "✓ Compiled — N KB" success line, footer
Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).
Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
using the existing scripts/translate-i18n.mjs pipeline (one
--force run, ~1 min total). Output validated with sameShape()
before write so any LLM-introduced key drift would have failed
loudly.
Quality note
- DeepSeek's translations now cover the entire bundle, including
earlier hand-translated content. Tone may differ slightly from
the prior hand passes but the meaning is consistent and brand /
technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
etc.) are preserved unchanged in every locale per the prompt
invariants.
2026-05-09 22:25:29 +07:00
|
|
|
{canSave ? t('editor.customChip.savePlace') : t('editor.customChip.compileFirst')}
|
2026-04-29 05:24:39 +07:00
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ── Inline styles (matches the visual language of other Velxio modals) ──
|
|
|
|
|
|
|
|
|
|
const overlayStyle: React.CSSProperties = {
|
|
|
|
|
position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.6)',
|
|
|
|
|
zIndex: 2000, display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
|
|
|
};
|
|
|
|
|
const dialogStyle: React.CSSProperties = {
|
|
|
|
|
width: '90vw', height: '85vh', maxWidth: 1280,
|
|
|
|
|
background: '#1f1f1f', color: '#e0e0e0', borderRadius: 6,
|
|
|
|
|
display: 'flex', flexDirection: 'column',
|
|
|
|
|
boxShadow: '0 12px 36px rgba(0,0,0,0.5)',
|
|
|
|
|
};
|
|
|
|
|
const headerStyle: React.CSSProperties = {
|
|
|
|
|
display: 'flex', alignItems: 'center', padding: '10px 14px',
|
|
|
|
|
borderBottom: '1px solid #333', background: '#252526',
|
|
|
|
|
};
|
|
|
|
|
const closeBtn: React.CSSProperties = {
|
|
|
|
|
background: 'transparent', border: 'none', color: '#999',
|
|
|
|
|
fontSize: 18, cursor: 'pointer', padding: '2px 8px',
|
|
|
|
|
};
|
|
|
|
|
const tabsRowStyle: React.CSSProperties = {
|
|
|
|
|
display: 'flex', gap: 4, padding: '6px 12px 0',
|
|
|
|
|
borderBottom: '1px solid #333', background: '#252526',
|
|
|
|
|
};
|
|
|
|
|
const tabBtn: React.CSSProperties = {
|
|
|
|
|
padding: '6px 14px', background: 'transparent', color: '#999',
|
|
|
|
|
border: 'none', borderBottom: '2px solid transparent',
|
|
|
|
|
cursor: 'pointer', fontSize: 13,
|
|
|
|
|
};
|
|
|
|
|
const tabActive: React.CSSProperties = { color: '#e0e0e0', borderBottom: '2px solid #007acc' };
|
|
|
|
|
const bodyStyle: React.CSSProperties = { flex: 1, overflow: 'hidden', display: 'flex' };
|
|
|
|
|
const editorLayout: React.CSSProperties = {
|
|
|
|
|
display: 'flex', flex: 1, gap: 8, padding: 8, overflow: 'hidden',
|
|
|
|
|
};
|
|
|
|
|
const editorLabel: React.CSSProperties = {
|
|
|
|
|
padding: '4px 8px', fontSize: 11, color: '#888',
|
|
|
|
|
background: '#252526', borderTopLeftRadius: 4, borderTopRightRadius: 4,
|
|
|
|
|
};
|
|
|
|
|
const resultPanelStyle: React.CSSProperties = {
|
|
|
|
|
borderTop: '1px solid #333', padding: '6px 14px',
|
|
|
|
|
fontSize: 12, fontFamily: 'monospace', maxHeight: 120, overflow: 'auto',
|
|
|
|
|
};
|
|
|
|
|
const errorPreStyle: React.CSSProperties = {
|
|
|
|
|
margin: 0, color: '#f87171', whiteSpace: 'pre-wrap', fontSize: 11,
|
|
|
|
|
};
|
|
|
|
|
const footerStyle: React.CSSProperties = {
|
|
|
|
|
display: 'flex', alignItems: 'center', gap: 8, padding: '10px 14px',
|
|
|
|
|
borderTop: '1px solid #333', background: '#252526',
|
|
|
|
|
};
|
|
|
|
|
const compileBtn: React.CSSProperties = {
|
|
|
|
|
padding: '6px 14px', background: '#007acc', color: 'white',
|
|
|
|
|
border: 'none', borderRadius: 3, cursor: 'pointer', fontSize: 13,
|
|
|
|
|
};
|
|
|
|
|
const cancelBtn: React.CSSProperties = {
|
|
|
|
|
padding: '6px 14px', background: '#3a3a3a', color: '#e0e0e0',
|
|
|
|
|
border: 'none', borderRadius: 3, cursor: 'pointer', fontSize: 13,
|
|
|
|
|
};
|
|
|
|
|
const saveBtn: React.CSSProperties = {
|
|
|
|
|
padding: '6px 14px', background: '#22c55e', color: 'white',
|
|
|
|
|
border: 'none', borderRadius: 3, cursor: 'pointer', fontSize: 13,
|
|
|
|
|
};
|
|
|
|
|
const saveBtnDisabled: React.CSSProperties = { ...saveBtn, background: '#3a3a3a', cursor: 'not-allowed' };
|
|
|
|
|
const blankBtn: React.CSSProperties = {
|
|
|
|
|
padding: '8px 14px', background: '#2d2d30', color: '#e0e0e0',
|
|
|
|
|
border: '1px dashed #555', borderRadius: 4, cursor: 'pointer',
|
|
|
|
|
width: '100%', textAlign: 'left', fontSize: 13,
|
|
|
|
|
};
|
|
|
|
|
const categoryStyle: React.CSSProperties = {
|
|
|
|
|
fontSize: 10, color: '#888', letterSpacing: 1, marginBottom: 6,
|
|
|
|
|
};
|
|
|
|
|
const gridStyle: React.CSSProperties = {
|
|
|
|
|
display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: 8,
|
|
|
|
|
};
|
|
|
|
|
const cardBtn: React.CSSProperties = {
|
|
|
|
|
padding: '10px 12px', background: '#2d2d30', color: '#e0e0e0',
|
|
|
|
|
border: '1px solid #3a3a3a', borderRadius: 4, cursor: 'pointer',
|
|
|
|
|
textAlign: 'left',
|
|
|
|
|
};
|
|
|
|
|
const cardName: React.CSSProperties = { fontSize: 13, fontWeight: 'bold', marginBottom: 4 };
|
|
|
|
|
const cardDesc: React.CSSProperties = { fontSize: 11, color: '#999', lineHeight: 1.4 };
|
|
|
|
|
const attrPanelStyle: React.CSSProperties = {
|
|
|
|
|
marginTop: 8, padding: '8px 10px', background: '#252526',
|
|
|
|
|
borderRadius: 4, maxHeight: 220, overflow: 'auto',
|
|
|
|
|
};
|
|
|
|
|
const attrPanelHeader: React.CSSProperties = {
|
|
|
|
|
fontSize: 10, color: '#888', letterSpacing: 1, marginBottom: 6, textTransform: 'uppercase',
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function AttrRow({
|
|
|
|
|
def,
|
|
|
|
|
value,
|
|
|
|
|
onChange,
|
|
|
|
|
}: {
|
|
|
|
|
def: AttributeDef;
|
|
|
|
|
value: number;
|
|
|
|
|
onChange: (v: number) => void;
|
|
|
|
|
}) {
|
|
|
|
|
const isInt = def.type === 'int';
|
|
|
|
|
const min = def.min;
|
|
|
|
|
const max = def.max;
|
|
|
|
|
const step = def.step ?? (isInt ? 1 : 0.01);
|
|
|
|
|
const showSlider = min !== undefined && max !== undefined;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
|
|
|
|
|
<label style={{ flex: '0 0 96px', fontSize: 11, color: '#bbb' }}>{def.label || def.name}</label>
|
|
|
|
|
{showSlider && (
|
|
|
|
|
<input
|
|
|
|
|
type="range"
|
|
|
|
|
min={min}
|
|
|
|
|
max={max}
|
|
|
|
|
step={step}
|
|
|
|
|
value={value}
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
const v = parseFloat(e.target.value);
|
|
|
|
|
onChange(isInt ? Math.round(v) : v);
|
|
|
|
|
}}
|
|
|
|
|
style={{ flex: 1 }}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
<input
|
|
|
|
|
type="number"
|
|
|
|
|
min={min}
|
|
|
|
|
max={max}
|
|
|
|
|
step={step}
|
|
|
|
|
value={Number.isFinite(value) ? value : 0}
|
|
|
|
|
onChange={(e) => {
|
|
|
|
|
const v = parseFloat(e.target.value);
|
|
|
|
|
if (!Number.isFinite(v)) return;
|
|
|
|
|
onChange(isInt ? Math.round(v) : v);
|
|
|
|
|
}}
|
|
|
|
|
style={{
|
|
|
|
|
width: 76, padding: '2px 4px', fontSize: 11,
|
|
|
|
|
background: '#1f1f1f', color: '#e0e0e0',
|
|
|
|
|
border: '1px solid #444', borderRadius: 2,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|