2026-05-08 21:12:26 +07:00
|
|
|
/**
|
|
|
|
|
* SelectionActionBar
|
|
|
|
|
*
|
|
|
|
|
* Floating toolbar pinned to the top-center of the simulator canvas. Visible
|
|
|
|
|
* whenever the user has something selected (wire / component / board). Gives
|
|
|
|
|
* touch users a clear way to delete or rotate a selection without keyboard
|
|
|
|
|
* shortcuts or right-click — the only way these actions were available before.
|
|
|
|
|
*
|
|
|
|
|
* Renders nothing when nothing is selected.
|
|
|
|
|
*
|
|
|
|
|
* Touch propagation note: the simulator canvas binds *native* touch listeners
|
|
|
|
|
* with `addEventListener` and calls `preventDefault()` on touchend, which
|
|
|
|
|
* suppresses the synthetic `click` event for buttons rendered above the
|
|
|
|
|
* canvas. We therefore call `stopPropagation()` on the *native* touchstart
|
|
|
|
|
* (React's synthetic stopPropagation does not affect native listeners) and
|
|
|
|
|
* also wire each button to fire on `touchend` directly so taps work.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import React, { useEffect, useRef } 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-05-11 22:14:36 +07:00
|
|
|
import { WIRE_KEY_COLORS } from '../../utils/wireUtils';
|
2026-05-08 21:12:26 +07:00
|
|
|
|
|
|
|
|
export type SelectionKind = 'wire' | 'component' | 'board';
|
|
|
|
|
|
|
|
|
|
interface SelectionActionBarProps {
|
|
|
|
|
kind: SelectionKind | null;
|
|
|
|
|
/** Human label, e.g. "Wire", "Arduino Uno", "LED" */
|
|
|
|
|
label: string;
|
|
|
|
|
/** Show the rotate button (components only). */
|
|
|
|
|
canRotate?: boolean;
|
|
|
|
|
onDelete: () => void;
|
|
|
|
|
onRotate?: () => void;
|
|
|
|
|
onDeselect: () => void;
|
2026-05-11 22:14:36 +07:00
|
|
|
onColorChange?: (color: string) => void;
|
|
|
|
|
currentColor?: string;
|
2026-05-08 21:12:26 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ICON_SIZE = 16;
|
|
|
|
|
|
|
|
|
|
const TrashIcon: React.FC = () => (
|
|
|
|
|
<svg
|
|
|
|
|
width={ICON_SIZE}
|
|
|
|
|
height={ICON_SIZE}
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
>
|
|
|
|
|
<polyline points="3 6 5 6 21 6" />
|
|
|
|
|
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
|
|
|
|
<line x1="10" y1="11" x2="10" y2="17" />
|
|
|
|
|
<line x1="14" y1="11" x2="14" y2="17" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const RotateIcon: React.FC = () => (
|
|
|
|
|
<svg
|
|
|
|
|
width={ICON_SIZE}
|
|
|
|
|
height={ICON_SIZE}
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
>
|
|
|
|
|
<polyline points="23 4 23 10 17 10" />
|
|
|
|
|
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const CloseIcon: React.FC = () => (
|
|
|
|
|
<svg
|
|
|
|
|
width={ICON_SIZE}
|
|
|
|
|
height={ICON_SIZE}
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
aria-hidden="true"
|
|
|
|
|
>
|
|
|
|
|
<line x1="18" y1="6" x2="6" y2="18" />
|
|
|
|
|
<line x1="6" y1="6" x2="18" y2="18" />
|
|
|
|
|
</svg>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Bind native touch listeners to a button so taps fire the action even when
|
|
|
|
|
* the simulator canvas (with its own native `addEventListener('touchend')`
|
|
|
|
|
* + preventDefault) would otherwise swallow the synthetic click.
|
|
|
|
|
*
|
|
|
|
|
* Strategy: stopPropagation on the native touchstart so the canvas listener
|
|
|
|
|
* never sees the event, and call the handler directly on touchend. This
|
|
|
|
|
* runs as a layout effect so the handler closure is current after every
|
|
|
|
|
* render — no ref-during-render hacks needed.
|
|
|
|
|
*/
|
|
|
|
|
function useTouchSafeAction(
|
|
|
|
|
ref: React.RefObject<HTMLElement | null>,
|
|
|
|
|
handler: () => void,
|
|
|
|
|
) {
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const el = ref.current;
|
|
|
|
|
if (!el) return;
|
|
|
|
|
const stopNative = (e: Event) => e.stopPropagation();
|
|
|
|
|
const onTouchEndNative = (e: TouchEvent) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
handler();
|
|
|
|
|
};
|
|
|
|
|
el.addEventListener('touchstart', stopNative, { passive: false });
|
|
|
|
|
el.addEventListener('touchend', onTouchEndNative, { passive: false });
|
|
|
|
|
return () => {
|
|
|
|
|
el.removeEventListener('touchstart', stopNative);
|
|
|
|
|
el.removeEventListener('touchend', onTouchEndNative);
|
|
|
|
|
};
|
|
|
|
|
}, [ref, handler]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const SelectionActionBar: React.FC<SelectionActionBarProps> = ({
|
|
|
|
|
kind,
|
|
|
|
|
label,
|
|
|
|
|
canRotate,
|
|
|
|
|
onDelete,
|
|
|
|
|
onRotate,
|
|
|
|
|
onDeselect,
|
2026-05-11 22:14:36 +07:00
|
|
|
onColorChange,
|
|
|
|
|
currentColor,
|
2026-05-08 21:12:26 +07:00
|
|
|
}) => {
|
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-05-11 22:14:36 +07:00
|
|
|
const [showPalette, setShowPalette] = React.useState(false);
|
2026-05-08 21:12:26 +07:00
|
|
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
|
|
|
|
const deleteRef = useRef<HTMLButtonElement | null>(null);
|
|
|
|
|
const rotateRef = useRef<HTMLButtonElement | null>(null);
|
|
|
|
|
const deselectRef = useRef<HTMLButtonElement | null>(null);
|
2026-05-11 22:14:36 +07:00
|
|
|
const colorToggleRef = useRef<HTMLButtonElement | null>(null);
|
|
|
|
|
|
|
|
|
|
// Reset the palette when the selection kind or colour-change handler changes
|
|
|
|
|
// (i.e. the user switched from one wire to another, or deselected).
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
setShowPalette(false);
|
|
|
|
|
}, [kind, onColorChange]);
|
2026-05-08 21:12:26 +07:00
|
|
|
|
|
|
|
|
// Stop native touch events from reaching the canvas listener so it can't
|
|
|
|
|
// call preventDefault on touchend (which would kill the synthetic click).
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const el = containerRef.current;
|
|
|
|
|
if (!el) return;
|
|
|
|
|
const stop = (e: Event) => e.stopPropagation();
|
|
|
|
|
el.addEventListener('touchstart', stop, { passive: false });
|
|
|
|
|
el.addEventListener('touchmove', stop, { passive: false });
|
|
|
|
|
el.addEventListener('touchend', stop, { passive: false });
|
|
|
|
|
el.addEventListener('mousedown', stop);
|
|
|
|
|
return () => {
|
|
|
|
|
el.removeEventListener('touchstart', stop);
|
|
|
|
|
el.removeEventListener('touchmove', stop);
|
|
|
|
|
el.removeEventListener('touchend', stop);
|
|
|
|
|
el.removeEventListener('mousedown', stop);
|
|
|
|
|
};
|
|
|
|
|
}, [kind]);
|
|
|
|
|
|
|
|
|
|
useTouchSafeAction(deleteRef, onDelete);
|
|
|
|
|
useTouchSafeAction(rotateRef, onRotate ?? noop);
|
|
|
|
|
useTouchSafeAction(deselectRef, onDeselect);
|
2026-05-11 22:14:36 +07:00
|
|
|
useTouchSafeAction(colorToggleRef, () => setShowPalette(!showPalette));
|
2026-05-08 21:12:26 +07:00
|
|
|
|
|
|
|
|
if (!kind) return null;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
ref={containerRef}
|
|
|
|
|
role="toolbar"
|
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
|
|
|
aria-label={t('editor.selectionBar.label')}
|
2026-05-08 21:12:26 +07:00
|
|
|
className="selection-action-bar"
|
2026-05-11 22:14:36 +07:00
|
|
|
onMouseDown={(e) => e.stopPropagation()}
|
|
|
|
|
onClick={(e) => e.stopPropagation()}
|
2026-05-08 21:12:26 +07:00
|
|
|
style={{
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
top: 12,
|
|
|
|
|
left: '50%',
|
|
|
|
|
transform: 'translateX(-50%)',
|
|
|
|
|
display: 'flex',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
gap: 6,
|
|
|
|
|
background: '#252526',
|
|
|
|
|
border: '1px solid #3c3c3c',
|
|
|
|
|
borderRadius: 8,
|
|
|
|
|
padding: '6px 8px',
|
|
|
|
|
boxShadow: '0 4px 16px rgba(0,0,0,0.5)',
|
|
|
|
|
zIndex: 50,
|
|
|
|
|
fontSize: 13,
|
|
|
|
|
color: '#e0e0e0',
|
|
|
|
|
pointerEvents: 'auto',
|
|
|
|
|
touchAction: 'none',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<span
|
|
|
|
|
style={{
|
|
|
|
|
padding: '0 8px 0 4px',
|
|
|
|
|
color: '#bbb',
|
|
|
|
|
maxWidth: 180,
|
|
|
|
|
overflow: 'hidden',
|
|
|
|
|
textOverflow: 'ellipsis',
|
|
|
|
|
whiteSpace: 'nowrap',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{label}
|
|
|
|
|
</span>
|
|
|
|
|
|
2026-05-11 22:14:36 +07:00
|
|
|
{kind === 'wire' && onColorChange && (
|
|
|
|
|
<div style={{ display: 'flex', alignItems: 'center', position: 'relative' }}>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
ref={colorToggleRef}
|
|
|
|
|
onClick={() => setShowPalette(!showPalette)}
|
|
|
|
|
style={{ ...buttonStyle, padding: '6px 8px' }}
|
|
|
|
|
title={t('editor.selectionBar.changeColor')}
|
|
|
|
|
aria-label={t('editor.selectionBar.changeColor')}
|
|
|
|
|
>
|
|
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
width: 20,
|
|
|
|
|
height: 20,
|
|
|
|
|
borderRadius: '50%',
|
|
|
|
|
backgroundColor: currentColor || '#22c55e',
|
|
|
|
|
border: '2px solid rgba(255,255,255,0.2)',
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
{showPalette && (
|
|
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
position: 'absolute',
|
|
|
|
|
top: '100%',
|
|
|
|
|
marginTop: 8,
|
|
|
|
|
left: '50%',
|
|
|
|
|
transform: 'translateX(-50%)',
|
|
|
|
|
display: 'flex',
|
|
|
|
|
flexWrap: 'wrap',
|
|
|
|
|
gap: 8,
|
|
|
|
|
padding: '10px',
|
|
|
|
|
background: '#252526',
|
|
|
|
|
border: '1px solid #3c3c3c',
|
|
|
|
|
borderRadius: 8,
|
|
|
|
|
boxShadow: '0 4px 16px rgba(0,0,0,0.5)',
|
|
|
|
|
justifyContent: 'center',
|
|
|
|
|
width: 240,
|
|
|
|
|
zIndex: 101,
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{Object.values(WIRE_KEY_COLORS).map((color) => (
|
|
|
|
|
<ColorButton
|
|
|
|
|
key={color}
|
|
|
|
|
color={color}
|
|
|
|
|
isSelected={color.toLowerCase() === currentColor?.toLowerCase()}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
onColorChange(color);
|
|
|
|
|
setShowPalette(false);
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
2026-05-08 21:12:26 +07:00
|
|
|
{canRotate && onRotate && (
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
ref={rotateRef}
|
|
|
|
|
onClick={onRotate}
|
|
|
|
|
style={buttonStyle}
|
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
|
|
|
title={t('editor.selectionBar.rotate90')}
|
|
|
|
|
aria-label={t('editor.selectionBar.rotate')}
|
2026-05-08 21:12:26 +07:00
|
|
|
>
|
|
|
|
|
<RotateIcon />
|
|
|
|
|
<span className="selection-action-bar__label" style={buttonLabelStyle}>
|
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.selectionBar.rotate')}
|
2026-05-08 21:12:26 +07:00
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
ref={deleteRef}
|
|
|
|
|
onClick={onDelete}
|
|
|
|
|
style={{ ...buttonStyle, color: '#e06c75' }}
|
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
|
|
|
title={t(`editor.selectionBar.deleteKind.${kind}`)}
|
|
|
|
|
aria-label={t(`editor.selectionBar.deleteKind.${kind}`)}
|
2026-05-08 21:12:26 +07:00
|
|
|
>
|
|
|
|
|
<TrashIcon />
|
|
|
|
|
<span className="selection-action-bar__label" style={buttonLabelStyle}>
|
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.selectionBar.delete')}
|
2026-05-08 21:12:26 +07:00
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
ref={deselectRef}
|
|
|
|
|
onClick={onDeselect}
|
|
|
|
|
style={{ ...buttonStyle, padding: '6px 8px' }}
|
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
|
|
|
title={t('editor.selectionBar.deselect')}
|
|
|
|
|
aria-label={t('editor.selectionBar.deselect')}
|
2026-05-08 21:12:26 +07:00
|
|
|
>
|
|
|
|
|
<CloseIcon />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-11 22:14:36 +07:00
|
|
|
const ColorButton: React.FC<{
|
|
|
|
|
color: string;
|
|
|
|
|
isSelected: boolean;
|
|
|
|
|
onClick: () => void;
|
|
|
|
|
}> = ({ color, isSelected, onClick }) => {
|
|
|
|
|
const ref = useRef<HTMLButtonElement | null>(null);
|
|
|
|
|
useTouchSafeAction(ref, onClick);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<button
|
|
|
|
|
ref={ref}
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={onClick}
|
|
|
|
|
style={{
|
|
|
|
|
width: 22,
|
|
|
|
|
height: 22,
|
|
|
|
|
borderRadius: '50%',
|
|
|
|
|
backgroundColor: color,
|
|
|
|
|
border: isSelected ? '2px solid #fff' : '1px solid rgba(255,255,255,0.2)',
|
|
|
|
|
cursor: 'pointer',
|
|
|
|
|
padding: 0,
|
|
|
|
|
flexShrink: 0,
|
|
|
|
|
boxShadow: isSelected ? '0 0 6px rgba(255,255,255,0.5)' : 'none',
|
|
|
|
|
}}
|
|
|
|
|
title={color}
|
|
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2026-05-08 21:12:26 +07:00
|
|
|
const noop = () => {};
|
|
|
|
|
|
|
|
|
|
const buttonStyle: React.CSSProperties = {
|
|
|
|
|
display: 'inline-flex',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
gap: 6,
|
|
|
|
|
padding: '6px 10px',
|
|
|
|
|
background: 'transparent',
|
|
|
|
|
border: '1px solid transparent',
|
|
|
|
|
borderRadius: 6,
|
|
|
|
|
color: 'inherit',
|
|
|
|
|
cursor: 'pointer',
|
|
|
|
|
fontSize: 13,
|
|
|
|
|
lineHeight: 1,
|
|
|
|
|
minHeight: 36, // touch-friendly hit target
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const buttonLabelStyle: React.CSSProperties = {
|
|
|
|
|
// Hidden on very narrow toolbars; kept visible by default for clarity
|
|
|
|
|
};
|