From d315fbc31b47e688c9b476e65a1308c52aa18183 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 24 Aug 2026 19:13:34 +0000 Subject: [PATCH 1/5] feat(score): name Add a score when the list or viewer is empty Empty score list and viewer copy now name Add a score as the next rehearsal action instead of describing a missing PDF. Korean and English stay aligned. --- AGENTS.md | 1 + ARCHITECTURE.md | 3 ++- CHANGELOG.md | 4 ++++ CLAUDE.md | 1 + apps/desktop/src/features/score/ScoreView.test.tsx | 5 +++-- apps/desktop/src/features/score/ScoreView.tsx | 4 +++- .../desktop/src/features/score/ScoreViewer.test.tsx | 6 ++++-- apps/desktop/src/features/score/ScoreViewer.tsx | 1 + apps/desktop/src/i18n/index.test.ts | 13 +++++++++++++ apps/desktop/src/locales/en/common.json | 5 +++-- apps/desktop/src/locales/ko/common.json | 5 +++-- docs/design-system/component-contract.md | 1 + 12 files changed, 39 insertions(+), 10 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index fca448ce9..2f6cc0e75 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -83,6 +83,7 @@ This section applies to any agent (Claude, Codex, Cursor, opencode, ...) working - Keep UI and analysis engine decoupled through shared contracts. - Prefer minimal, test-first changes for production code. - Prefer practical, friendly, rehearsal-first wording over academic or authority-heavy language. +- Customer-facing empty and error copy must name the next action. Score empty states name **Add a score** (or analyze first when no project is active); do not leave a text-only “no PDF yet” dead end. - Do not reduce the product to a chord analyzer when form, timing, player coordination, simplification, and setup cues are the real rehearsal blockers. - Do not frame usability as a reason to accept weak analysis quality; BandScope should aim for both easy use and high accuracy. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 3302a6fc3..aef86fe4e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,6 +1,6 @@ # ARCHITECTURE.md -Last updated: 2026-03-11 +Last updated: 2026-08-25 ## Brand source @@ -68,6 +68,7 @@ Last updated: 2026-03-11 - BandScope is not only a shell around chord labels, stems, and ranges. - The technical scope includes rehearsal-facing outputs for harmony, section roadmap, groove cues, role entry and dropout cues, simplification guidance, transposition or setup guidance, confidence flags, and rehearsal priority. - These outputs must stay aligned with `docs/brand-story.md` rather than drifting back to a song-summary-only analyzer. +- Score empty states name **Add a score** as the next rehearsal action (or analyze first when no project workspace is active). They are not text-only placeholders. ## Analysis target model diff --git a/CHANGELOG.md b/CHANGELOG.md index eea696893..0e86dacf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ - Display the analyzed song tempo (BPM) as a badge in the rehearsal workspace. - 각 합주 역할(Role)별 개인 연습 진행도를 0~100% 범위로 기록 및 시각화할 수 있는 연습 진척도(`practiceProgress`) 트래커 기능 추가. UI 컨트롤(슬라이더 및 +/- 버튼)과 한/영 다국어 지원 포함. +### Changed + +- Name **Add a score** as the next rehearsal action when the score list or viewer is empty, instead of describing the missing PDF. + ## [0.1.3] - 2026-04-29 ### Fixed diff --git a/CLAUDE.md b/CLAUDE.md index 82c2c704a..39bd2c117 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -66,6 +66,7 @@ Supporting packages: ## Key conventions - Coverage is a hard gate: the Python engine requires 100% test coverage and 100% docstring coverage (Ruff `D100`–`D107` across `src`, `tests`, and repo scripts). Exported TypeScript declarations in `packages/shared-types` and `apps/desktop/src` require JSDoc with a description; `no-console` is an error. +- Empty score list and viewer copy must name **Add a score** (or analyze first when no project is active). Do not ship text-only “no PDF attached” dead ends. - Gitflow: `develop` is the default branch; `feature/*` targets `develop`, `main` is the protected release branch. Direct pushes to protected branches are not allowed, and every merge needs the required checks plus a passing CodeRabbit review (see `CONTRIBUTING.md` and `docs/repository/gitflow.md`). - The PR template (`.github/PULL_REQUEST_TEMPLATE.md`) requires a quickcheck confirmation, `Security Notes` (attack surface, trust boundary, mitigations, test points), a dependency/supply-chain checklist, and i18n impact. - i18n: the UI ships Korean and English locales (`apps/desktop/src/locales/ko`, `en`). Any user-visible string change must update both. diff --git a/apps/desktop/src/features/score/ScoreView.test.tsx b/apps/desktop/src/features/score/ScoreView.test.tsx index de4ccb95c..33c2aed94 100644 --- a/apps/desktop/src/features/score/ScoreView.test.tsx +++ b/apps/desktop/src/features/score/ScoreView.test.tsx @@ -23,7 +23,7 @@ vi.mock("../../i18n", () => ({ scoreViewTitle: "Score", scoreViewSubtitle: "Attach validated PDF scores to the current song.", scoreListTitle: "Attached scores", - scoreListEmpty: "No scores attached to this song yet.", + scoreListEmpty: "Add a score to read it during rehearsal.", scoreAttach: "Add score", scoreAttaching: "Attaching...", scoreRemove: "Remove", @@ -84,7 +84,8 @@ describe("ScoreView", () => { render(); expect(screen.getByRole("heading", { name: /Score · Late Night Set/i })).toBeInTheDocument(); - expect(screen.getByText("No scores attached to this song yet.")).toBeInTheDocument(); + expect(screen.getByText("Add a score to read it during rehearsal.")).toBeInTheDocument(); + expect(screen.getByRole("status")).toHaveTextContent("Add a score to read it during rehearsal."); expect(screen.getByRole("button", { name: "Add score" })).toBeEnabled(); expect(screen.getByTestId("score-viewer")).toHaveTextContent("no-data"); expect(mockInvoke).not.toHaveBeenCalled(); diff --git a/apps/desktop/src/features/score/ScoreView.tsx b/apps/desktop/src/features/score/ScoreView.tsx index 72732450f..61c06f87f 100644 --- a/apps/desktop/src/features/score/ScoreView.tsx +++ b/apps/desktop/src/features/score/ScoreView.tsx @@ -169,7 +169,9 @@ export function ScoreView({ song, projectId, onSongUpdate }: ScoreViewProps) { {t("scoreListTitle")} {attachments.length === 0 ? ( -

{t("scoreListEmpty")}

+

+ {t("scoreListEmpty")} +

) : (