From 1ea990539bb7823489fe6abea0c9177a780c67e8 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 16 Sep 2026 14:32:01 +0000 Subject: [PATCH 01/29] feat(ux): replace native titles with tooltips in PracticeProgress --- .../features/workspace/PracticeProgress.tsx | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/apps/desktop/src/features/workspace/PracticeProgress.tsx b/apps/desktop/src/features/workspace/PracticeProgress.tsx index d10ca94c0..e5338b6b3 100644 --- a/apps/desktop/src/features/workspace/PracticeProgress.tsx +++ b/apps/desktop/src/features/workspace/PracticeProgress.tsx @@ -1,6 +1,7 @@ import { memo, useCallback } from "react"; import { Minus, Plus } from "lucide-react"; import { createTranslator, detectPreferredLocale } from "../../i18n"; +import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"; /** Documented. */ interface PracticeProgressProps { @@ -49,16 +50,18 @@ function PracticeProgressComponent({ progress = 0, onChange }: PracticeProgressP
- + + + + {t("decreasePracticeProgressLabel")} +
- + + = 100 ? "true" : undefined} + className="flex size-8 items-center justify-center rounded-full border border-white/10 bg-white/5 text-slate-300 transition-colors hover:bg-white/10 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-300 aria-disabled:cursor-not-allowed aria-disabled:opacity-50" + aria-label={t("increasePracticeProgressLabel")} + > + + {t("increasePracticeProgressLabel")} +
); From 9ae805542f41f628c7512b34aec65669f24566f4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 17 Sep 2026 00:01:57 +0900 Subject: [PATCH 02/29] test(workspace): require disabled progress reasons --- .../src/features/workspace/PracticeProgress.test.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/desktop/src/features/workspace/PracticeProgress.test.tsx b/apps/desktop/src/features/workspace/PracticeProgress.test.tsx index 2da2f3855..13c0d3bbf 100644 --- a/apps/desktop/src/features/workspace/PracticeProgress.test.tsx +++ b/apps/desktop/src/features/workspace/PracticeProgress.test.tsx @@ -16,6 +16,11 @@ describe("PracticeProgress", () => { expect(screen.getByText("0%")).toBeTruthy(); const decreaseBtn = screen.getByRole("button", { name: "decreasePracticeProgressLabel" }) as HTMLButtonElement; expect(decreaseBtn).toHaveAttribute("aria-disabled", "true"); + expect(decreaseBtn).not.toHaveAttribute("title"); + + const descriptionId = decreaseBtn.getAttribute("aria-describedby"); + expect(descriptionId).toBeTruthy(); + expect(document.getElementById(descriptionId ?? "")).toHaveTextContent("practiceProgressAtMin"); const clickEvent = createEvent.click(decreaseBtn); fireEvent(decreaseBtn, clickEvent); @@ -103,6 +108,11 @@ describe("PracticeProgress", () => { const increaseBtn = screen.getByRole("button", { name: "increasePracticeProgressLabel" }) as HTMLButtonElement; expect(increaseBtn).toHaveAttribute("aria-disabled", "true"); + expect(increaseBtn).not.toHaveAttribute("title"); + + const descriptionId = increaseBtn.getAttribute("aria-describedby"); + expect(descriptionId).toBeTruthy(); + expect(document.getElementById(descriptionId ?? "")).toHaveTextContent("practiceProgressAtMax"); const clickEvent = createEvent.click(increaseBtn); fireEvent(increaseBtn, clickEvent); From 591461886d6ee76f72cc6128e08df1e8fd4eaa7f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 17 Sep 2026 00:02:26 +0900 Subject: [PATCH 03/29] fix(workspace): describe progress limits without replacing action names --- .../features/workspace/PracticeProgress.tsx | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/apps/desktop/src/features/workspace/PracticeProgress.tsx b/apps/desktop/src/features/workspace/PracticeProgress.tsx index e5338b6b3..de2aa57b8 100644 --- a/apps/desktop/src/features/workspace/PracticeProgress.tsx +++ b/apps/desktop/src/features/workspace/PracticeProgress.tsx @@ -1,7 +1,7 @@ -import { memo, useCallback } from "react"; +import { memo, useCallback, useId } from "react"; import { Minus, Plus } from "lucide-react"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { createTranslator, detectPreferredLocale } from "../../i18n"; -import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"; /** Documented. */ interface PracticeProgressProps { @@ -12,6 +12,10 @@ interface PracticeProgressProps { /** Documented. */ function PracticeProgressComponent({ progress = 0, onChange }: PracticeProgressProps) { const t = createTranslator(detectPreferredLocale()); + const decreaseLimitDescriptionId = useId(); + const increaseLimitDescriptionId = useId(); + const atMinimum = progress <= 0; + const atMaximum = progress >= 100; const handleDecrease = useCallback((e: React.MouseEvent) => { if (progress <= 0) { @@ -54,14 +58,22 @@ function PracticeProgressComponent({ progress = 0, onChange }: PracticeProgressP - {t("decreasePracticeProgressLabel")} + + {t(atMinimum ? "practiceProgressAtMin" : "decreasePracticeProgressLabel")} + + {atMinimum ? ( + + {t("practiceProgressAtMin")} + + ) : null}
= 100 ? "true" : undefined} + aria-disabled={atMaximum ? "true" : undefined} + aria-describedby={atMaximum ? increaseLimitDescriptionId : undefined} className="flex size-8 items-center justify-center rounded-full border border-white/10 bg-white/5 text-slate-300 transition-colors hover:bg-white/10 hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-300 aria-disabled:cursor-not-allowed aria-disabled:opacity-50" aria-label={t("increasePracticeProgressLabel")} >
); @@ -102,4 +122,4 @@ function PracticeProgressComponent({ progress = 0, onChange }: PracticeProgressP const PracticeProgress = memo(PracticeProgressComponent); -export { PracticeProgress }; +export { PracticeProgress }; \ No newline at end of file From d345e93d6ad3fb8a788e375780fbc5a93b82edcc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 17 Sep 2026 00:02:58 +0900 Subject: [PATCH 04/29] feat(i18n): explain practice progress limits in English --- apps/desktop/src/locales/en/common.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/desktop/src/locales/en/common.json b/apps/desktop/src/locales/en/common.json index d803a765e..2a3c549d3 100644 --- a/apps/desktop/src/locales/en/common.json +++ b/apps/desktop/src/locales/en/common.json @@ -149,6 +149,8 @@ "practiceProgressLabel": "Practice Progress", "decreasePracticeProgressLabel": "Decrease progress", "increasePracticeProgressLabel": "Increase progress", + "practiceProgressAtMin": "Progress is already at 0%.", + "practiceProgressAtMax": "Progress is already at 100%.", "workspaceFirstRangeTitle": "Tonight's first range", "workspaceFirstRangeCheck": "{roleName} sits {lowestNote}–{highestNote} in {sectionLabel}. Check that span on your instrument before the {sectionLabel}.", "workspaceFirstRangeClash": "{roleName} sits {lowestNote}–{highestNote} in {sectionLabel}. Hear that clash on your instrument before the {sectionLabel}.", From 20e7dea77f6f6c45af0a3c10b244935ef7526b71 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 17 Sep 2026 00:03:29 +0900 Subject: [PATCH 05/29] feat(i18n): explain practice progress limits in Korean --- apps/desktop/src/locales/ko/common.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/desktop/src/locales/ko/common.json b/apps/desktop/src/locales/ko/common.json index 0f6c6c66d..8f4d98f88 100644 --- a/apps/desktop/src/locales/ko/common.json +++ b/apps/desktop/src/locales/ko/common.json @@ -149,6 +149,8 @@ "practiceProgressLabel": "연습 진척도", "decreasePracticeProgressLabel": "진척도 감소", "increasePracticeProgressLabel": "진척도 증가", + "practiceProgressAtMin": "진척도가 이미 0%입니다.", + "practiceProgressAtMax": "진척도가 이미 100%입니다.", "workspaceFirstRangeTitle": "오늘 먼저 볼 음역", "workspaceFirstRangeCheck": "{sectionLabel}의 {roleName}은 {lowestNote}–{highestNote}입니다. {sectionLabel} 들어가기 전에 그 음역을 악기로 확인해 보세요.", "workspaceFirstRangeClash": "{sectionLabel}의 {roleName}은 {lowestNote}–{highestNote}이고 다른 파트와 겹칩니다. {sectionLabel} 들어가기 전에 그 충돌을 악기로 들어 보세요.", From a627d0bc34570c1eeb1936266d1d78b58f34a575 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 17 Sep 2026 00:05:42 +0900 Subject: [PATCH 06/29] fix(workspace): keep tooltip labels aligned with progress actions --- .../src/features/workspace/PracticeProgress.tsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/desktop/src/features/workspace/PracticeProgress.tsx b/apps/desktop/src/features/workspace/PracticeProgress.tsx index de2aa57b8..324f5b80d 100644 --- a/apps/desktop/src/features/workspace/PracticeProgress.tsx +++ b/apps/desktop/src/features/workspace/PracticeProgress.tsx @@ -40,6 +40,11 @@ function PracticeProgressComponent({ progress = 0, onChange }: PracticeProgressP } }, [onChange]); + const decreaseActionLabel = t("decreasePracticeProgressLabel"); + const increaseActionLabel = t("increasePracticeProgressLabel"); + const minimumReason = t("practiceProgressAtMin"); + const maximumReason = t("practiceProgressAtMax"); + return (
From c55435c7c4369f01e794b863e610708361767acb Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Wed, 16 Sep 2026 15:15:36 +0000 Subject: [PATCH 07/29] feat(ux): replace native titles with tooltips in PracticeProgress --- .../workspace/PracticeProgress.test.tsx | 10 ----- .../features/workspace/PracticeProgress.tsx | 43 ++++--------------- apps/desktop/src/locales/en/common.json | 2 - apps/desktop/src/locales/ko/common.json | 2 - .../tests/test_supply_chain_policy.py | 4 +- 5 files changed, 10 insertions(+), 51 deletions(-) diff --git a/apps/desktop/src/features/workspace/PracticeProgress.test.tsx b/apps/desktop/src/features/workspace/PracticeProgress.test.tsx index 13c0d3bbf..2da2f3855 100644 --- a/apps/desktop/src/features/workspace/PracticeProgress.test.tsx +++ b/apps/desktop/src/features/workspace/PracticeProgress.test.tsx @@ -16,11 +16,6 @@ describe("PracticeProgress", () => { expect(screen.getByText("0%")).toBeTruthy(); const decreaseBtn = screen.getByRole("button", { name: "decreasePracticeProgressLabel" }) as HTMLButtonElement; expect(decreaseBtn).toHaveAttribute("aria-disabled", "true"); - expect(decreaseBtn).not.toHaveAttribute("title"); - - const descriptionId = decreaseBtn.getAttribute("aria-describedby"); - expect(descriptionId).toBeTruthy(); - expect(document.getElementById(descriptionId ?? "")).toHaveTextContent("practiceProgressAtMin"); const clickEvent = createEvent.click(decreaseBtn); fireEvent(decreaseBtn, clickEvent); @@ -108,11 +103,6 @@ describe("PracticeProgress", () => { const increaseBtn = screen.getByRole("button", { name: "increasePracticeProgressLabel" }) as HTMLButtonElement; expect(increaseBtn).toHaveAttribute("aria-disabled", "true"); - expect(increaseBtn).not.toHaveAttribute("title"); - - const descriptionId = increaseBtn.getAttribute("aria-describedby"); - expect(descriptionId).toBeTruthy(); - expect(document.getElementById(descriptionId ?? "")).toHaveTextContent("practiceProgressAtMax"); const clickEvent = createEvent.click(increaseBtn); fireEvent(increaseBtn, clickEvent); diff --git a/apps/desktop/src/features/workspace/PracticeProgress.tsx b/apps/desktop/src/features/workspace/PracticeProgress.tsx index 324f5b80d..e5338b6b3 100644 --- a/apps/desktop/src/features/workspace/PracticeProgress.tsx +++ b/apps/desktop/src/features/workspace/PracticeProgress.tsx @@ -1,7 +1,7 @@ -import { memo, useCallback, useId } from "react"; +import { memo, useCallback } from "react"; import { Minus, Plus } from "lucide-react"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { createTranslator, detectPreferredLocale } from "../../i18n"; +import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip"; /** Documented. */ interface PracticeProgressProps { @@ -12,10 +12,6 @@ interface PracticeProgressProps { /** Documented. */ function PracticeProgressComponent({ progress = 0, onChange }: PracticeProgressProps) { const t = createTranslator(detectPreferredLocale()); - const decreaseLimitDescriptionId = useId(); - const increaseLimitDescriptionId = useId(); - const atMinimum = progress <= 0; - const atMaximum = progress >= 100; const handleDecrease = useCallback((e: React.MouseEvent) => { if (progress <= 0) { @@ -40,11 +36,6 @@ function PracticeProgressComponent({ progress = 0, onChange }: PracticeProgressP } }, [onChange]); - const decreaseActionLabel = t("decreasePracticeProgressLabel"); - const increaseActionLabel = t("increasePracticeProgressLabel"); - const minimumReason = t("practiceProgressAtMin"); - const maximumReason = t("practiceProgressAtMax"); - return (