diff --git a/frontend/src/components/ResultPanel.tsx b/frontend/src/components/ResultPanel.tsx
index 5067e83..dff31a0 100644
--- a/frontend/src/components/ResultPanel.tsx
+++ b/frontend/src/components/ResultPanel.tsx
@@ -11,7 +11,8 @@ import { computeCentroid, flattenCurves, ctrlToHandle, handleToCtrl } from '../u
import { Toolbar, SelectIcon, CropIcon, MeasureIcon, BoxIcon, DetectAllIcon, ViewIcon, HandIcon, PenIcon, PencilIcon } from './Toolbar';
import { IconUpload, IconSquare, IconLamp } from './icons';
import type { ToolId } from './Toolbar';
-import { SelectAnimation, BoxAnimation, CropAnimation, MeasureAnimation, DetectAllAnimation, InspectAnimation, PanAnimation, PenAnimation, PencilAnimation } from './ToolTooltipAnimations';
+import { SelectAnimation, BoxAnimation, CropAnimation, MeasureAnimation, DetectAllAnimation, InspectAnimation, PanAnimation, PenAnimation, PencilAnimation, SolderAnimation, SymmetryAnimation, ProfileAnimation } from './ToolTooltipAnimations';
+import { ToolTooltip } from './ToolTooltip';
import { CropOverlay } from './CropOverlay';
import { MeasureInput } from './MeasureInput';
import { MeasureLineOverlay } from './MeasureLineOverlay';
@@ -1532,7 +1533,14 @@ export function ResultPanel({
- {!isSolderPopoverOpen && {t('solderThicknessTooltip')}}
+ {!isSolderPopoverOpen && (
+ }
+ />
+ )}
{isSolderPopoverOpen && (
@@ -1599,7 +1607,12 @@ export function ResultPanel({
-
{t('lampSymmetryTooltip')}
+
}
+ />
>
)}
@@ -1619,7 +1632,12 @@ export function ResultPanel({
- {t('lampProfileButtonTooltip')}
+ }
+ />
>
)}
diff --git a/frontend/src/components/SheetPanel.tsx b/frontend/src/components/SheetPanel.tsx
index ca10dcb..2091299 100644
--- a/frontend/src/components/SheetPanel.tsx
+++ b/frontend/src/components/SheetPanel.tsx
@@ -12,7 +12,8 @@ import { packPiecesSmart, defaultCuttingGapPx } from '../utils/packing';
import { toImageCoords, toScreenCoords } from '../utils/viewport';
import { Toolbar, SelectIcon, CropIcon, MeasureIcon, HandIcon } from './Toolbar';
import type { ToolId } from './Toolbar';
-import { SelectAnimation, CropAnimation, MeasureAnimation, PanAnimation } from './ToolTooltipAnimations';
+import { SelectAnimation, CropAnimation, MeasureAnimation, PanAnimation, PackAnimation } from './ToolTooltipAnimations';
+import { ToolTooltip } from './ToolTooltip';
import { CropOverlay } from './CropOverlay';
import { MeasureInput } from './MeasureInput';
import { MeasureLineOverlay } from './MeasureLineOverlay';
@@ -502,7 +503,14 @@ export function SheetPanel({
{isPacking ? t('packing', 'Packing...') : t('toolPack')}
- {!isPackPopoverOpen && {t('tooltipPackDesc')}}
+ {!isPackPopoverOpen && (
+ }
+ />
+ )}
{isPackPopoverOpen && (
diff --git a/frontend/src/components/ToolTooltip.tsx b/frontend/src/components/ToolTooltip.tsx
index b91a2ac..e3f15d9 100644
--- a/frontend/src/components/ToolTooltip.tsx
+++ b/frontend/src/components/ToolTooltip.tsx
@@ -14,7 +14,7 @@ export function ToolTooltip({ name, shortcut, description, animation }: ToolTool
{name}
- {shortcut}
+ {shortcut && {shortcut}}
{description}
diff --git a/frontend/src/components/ToolTooltipAnimations.tsx b/frontend/src/components/ToolTooltipAnimations.tsx
index 2a782c5..ca08006 100644
--- a/frontend/src/components/ToolTooltipAnimations.tsx
+++ b/frontend/src/components/ToolTooltipAnimations.tsx
@@ -608,7 +608,7 @@ export function PencilAnimation() {
"
keyTimes="0; 0.15; 0.35; 0.55; 0.70; 0.85; 1"
dur={dur} repeatCount="indefinite" />
-
+
{/* Pencil body */}
@@ -620,3 +620,199 @@ export function PencilAnimation() {
);
}
+
+/* ═══════════════════════════════════════════════════════════════════════════
+ Solder Animation — the lead lines between glass pieces grow and shrink as
+ the solder width is adjusted with a slider
+ ═══════════════════════════════════════════════════════════════════════════ */
+export function SolderAnimation() {
+ const dur = '3.5s';
+ const KT = '0; 0.45; 0.55; 1';
+ const SPL = '0.4,0,0.2,1; 0,0,1,1; 0.4,0,0.2,1';
+ const SW = '1.5;6;6;1.5';
+ const SOLDER = '#3f3f46';
+
+ return (
+
+ );
+}
+
+/* ═══════════════════════════════════════════════════════════════════════════
+ Symmetry Animation — a mark drawn in one facet is replicated radially across
+ all facets at once
+ ═══════════════════════════════════════════════════════════════════════════ */
+export function SymmetryAnimation() {
+ const dur = '4s';
+ const cx = 110, cy = 55;
+ const R = 40; // facet boundary radius
+ const r = 22; // drawn-mark orbit radius
+ const angs = [-90, -30, 30, 90, 150, 210];
+ const pt = (ang: number, rad: number) => {
+ const t = (ang * Math.PI) / 180;
+ return { x: cx + rad * Math.cos(t), y: cy + rad * Math.sin(t) };
+ };
+ const top = pt(-90, r);
+ const SPRING = '0,0,1,1; 0.3,1.3,0.4,1; 0,0,1,1; 0,0,1,1; 0,0,1,1';
+
+ return (
+
+ );
+}
+
+/* ═══════════════════════════════════════════════════════════════════════════
+ Profile Animation — dragging a control handle reshapes the lamp's silhouette,
+ mirrored across its vertical axis
+ ═══════════════════════════════════════════════════════════════════════════ */
+export function ProfileAnimation() {
+ const dur = '4s';
+ const KT = '0; 0.5; 1';
+ const SPL = '0.4,0,0.2,1; 0.4,0,0.2,1';
+ const dNarrow = 'M 92,26 Q 88,57 80,86 L 140,86 Q 132,57 128,26 Z';
+ const dWide = 'M 92,26 Q 66,57 80,86 L 140,86 Q 154,57 128,26 Z';
+
+ return (
+
+ );
+}
+
+/* ═══════════════════════════════════════════════════════════════════════════
+ Pack Animation — scattered pieces translate and rotate into a tightly
+ packed arrangement on the sheet, then scatter again
+ ═══════════════════════════════════════════════════════════════════════════ */
+export function PackAnimation() {
+ const dur = '4.5s';
+ const KT = '0; 0.14; 0.55; 0.82; 1';
+ const SPL = '0,0,1,1; 0.4,0,0.2,1; 0,0,1,1; 0.45,0,0.55,1';
+
+ const pieces = [
+ { w: 40, h: 34, sx: 150, sy: 30, sr: 18, px: 40, py: 33 },
+ { w: 30, h: 34, sx: 58, sy: 82, sr: -22, px: 79, py: 33 },
+ { w: 72, h: 22, sx: 150, sy: 84, sr: 12, px: 53, py: 62 },
+ ];
+
+ return (
+
+ );
+}
diff --git a/frontend/src/i18n.ts b/frontend/src/i18n.ts
index 3acf4d2..7cee2d5 100644
--- a/frontend/src/i18n.ts
+++ b/frontend/src/i18n.ts
@@ -55,6 +55,10 @@ const resources = {
tooltipInspectDesc: "Hide glass pieces to see the original pattern clearly",
tooltipPanName: "Pan Tool",
tooltipPanDesc: "Click and drag to pan around the workspace. Hold Spacebar to temporarily activate from any tool.",
+ tooltipPackName: "Pack Sheet",
+ tooltipSolderName: "Solder Line",
+ tooltipSymmetryName: "Symmetry",
+ tooltipProfileName: "Lamp Profile",
clickToRename: "Click to rename",
sheet: "Sheet",
addSheetOption: "Add sheet…",
@@ -277,6 +281,10 @@ const resources = {
tooltipInspectDesc: "Cachez les pièces de verre pour voir le patron original clairement",
tooltipPanName: "Outil Main",
tooltipPanDesc: "Cliquez et glissez pour vous déplacer. Maintenez Espace pour l'activer temporairement depuis n'importe quel outil.",
+ tooltipPackName: "Tasser",
+ tooltipSolderName: "Soudure",
+ tooltipSymmetryName: "Symétrie",
+ tooltipProfileName: "Profil de Lampe",
clickToRename: "Cliquez pour renommer",
sheet: "Plaque",
addSheetOption: "Ajouter une plaque…",