- {/* Viewport */}
+ {/* Viewport with shared shortcuts context */}
{
+ beforeEach(() => {
+ // Clear localStorage mock
+ if (typeof globalThis.localStorage !== "undefined") {
+ globalThis.localStorage.removeItem(STORAGE_KEY);
+ }
+ });
+
+ it("defines 6 default shortcuts for all view modes", () => {
+ const DEFAULT_MODES = ["composite", "original", "side_by_side", "split", "overlay", "onion"];
+ const DEFAULT_KEYS = ["1", "2", "3", "4", "5", "6"];
+
+ expect(DEFAULT_MODES).toHaveLength(6);
+ expect(DEFAULT_KEYS).toHaveLength(6);
+
+ // Verify each mode has a unique key
+ const uniqueKeys = new Set(DEFAULT_KEYS);
+ expect(uniqueKeys.size).toBe(6);
+ });
+
+ it("VIEW_MODE_LABELS covers all 6 modes", async () => {
+ const { VIEW_MODE_LABELS } = await import("../client/src/contexts/ShortcutsContext");
+
+ expect(VIEW_MODE_LABELS.composite).toBe("Composite");
+ expect(VIEW_MODE_LABELS.original).toBe("Original");
+ expect(VIEW_MODE_LABELS.side_by_side).toBe("Côte à côte");
+ expect(VIEW_MODE_LABELS.split).toBe("Split");
+ expect(VIEW_MODE_LABELS.overlay).toBe("Superposition");
+ expect(VIEW_MODE_LABELS.onion).toBe("Onion Skin");
+ });
+
+ it("formatKeyLabel produces correct labels for modifier combinations", async () => {
+ const { formatKeyLabel } = await import("../client/src/contexts/ShortcutsContext");
+
+ expect(formatKeyLabel("a", false, false, false)).toBe("A");
+ expect(formatKeyLabel("1", false, false, false)).toBe("1");
+ expect(formatKeyLabel("a", true, false, false)).toBe("Ctrl+A");
+ expect(formatKeyLabel("b", true, true, false)).toBe("Ctrl+Shift+B");
+ expect(formatKeyLabel("F1", false, false, false)).toBe("F1");
+ expect(formatKeyLabel("a", true, true, true)).toBe("Ctrl+Shift+Alt+A");
+ });
+
+ it("formatKeyLabel handles special keys", async () => {
+ const { formatKeyLabel } = await import("../client/src/contexts/ShortcutsContext");
+
+ expect(formatKeyLabel("ArrowUp", false, false, false)).toBe("ArrowUp");
+ expect(formatKeyLabel("Enter", true, false, false)).toBe("Ctrl+Enter");
+ expect(formatKeyLabel("Escape", false, false, true)).toBe("Alt+Escape");
+ });
+
+ it("localStorage key is correctly defined", () => {
+ expect(STORAGE_KEY).toBe("retrotoon-viewport-shortcuts");
+ });
+
+ it("ShortcutsProvider and useShortcuts are exported", async () => {
+ const mod = await import("../client/src/contexts/ShortcutsContext");
+ expect(mod.ShortcutsProvider).toBeDefined();
+ expect(mod.useShortcuts).toBeDefined();
+ expect(mod.formatKeyLabel).toBeDefined();
+ expect(mod.VIEW_MODE_LABELS).toBeDefined();
+ });
+});
diff --git a/todo.md b/todo.md
index 22eda9a..5dffbc0 100644
--- a/todo.md
+++ b/todo.md
@@ -99,3 +99,4 @@ via les endpoints API configurables dans l'onglet Services du panneau d'administ
## Nouvelles fonctionnalités demandées
- [x] Prévisualisation côte à côte (original vs regénéré IA) dans le ViewportPanel - 6 modes: Composite, Original, Side-by-Side, Split (curseur), Overlay (opacité), Onion Skin. Branché sur les vraies données DB via trpc.frames.getByIndex, fallback robuste avec icône d'erreur.
+- [x] Raccourcis clavier personnalisables pour basculer entre les 6 modes de prévisualisation (hook useKeyboardShortcuts + composant ShortcutSettings + persistance localStorage + tooltips avec raccourcis affichés + 45 tests passants)