fix: capture title from the json#616
Conversation
The l-t keybinding read state.title directly, so when the JSON omitted title it announced the model's "MAIDR Plot" / "unavailable" placeholders instead of reporting that no title was provided. Mirror the placeholder filter already used by the d-key description path and switch the verbose unavailable phrasing to "No title available". Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
announceTraceTitle only consulted the layer/trace title in multi-panel figures, so single-panel plots that authored their title at the layer level (e.g. examples/multiline_plot.html) fell through to the DEFAULT_FIGURE_TITLE placeholder and announced "No title available". Mirror DescriptionService.getDescription precedence: prefer the authored trace title and label it "Title" in single-panel context, keeping the existing "Subplot title" label for multi-panel figures. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tighten the inline comments and JSDoc added by the title-source fix: trim redundant phrasing, match the terser inline-comment style used in adjacent command files, refresh the now-stale execute() JSDoc to describe the post-fix precedence, and add the missing @param annotation on announceTraceTitle. No behavior change. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code ReviewOverviewThis PR fixes a real UX bug: the What Works Well
Concerns1. Cross-layer coupling (moderate) // src/command/describe.ts
import { DEFAULT_SUBPLOT_TITLE } from '@model/abstract';
import { DEFAULT_FIGURE_TITLE } from '@model/plot';The command layer now imports internal model constants to filter placeholder values. If either constant is renamed or its value changes, Suggested alternative: move the authored-title predicate closer to where the defaults live. Export // model/context.ts
public get authoredFigureTitle(): string | null {
const t = this.figureTitle;
return isAuthoredTitle(t) ? t : null;
}Either approach keeps the model's internal defaults encapsulated in the model layer. 2. Fragile empty-state path in
// context.ts — current code
public get figureTitle(): string {
const figureState = this.figure.state;
if (!figureState.empty) {
return figureState.title;
}
return 'unavailable'; // ← hard-coded literal, not the exported constant
}Minor fix: change that literal to 3. No tests added The checklist item for unit tests is unchecked, and the test directory has no coverage for
4. User-facing string change (minor) The verbose unavailable message changes from SummaryThe fix is correct and the approach is sound. The main asks before merging:
|
Address PR #616 review feedback: - Move the authored-title predicate from a module-scope helper in describe.ts into Context.isAuthoredTitle, so the command layer no longer imports the model's internal DEFAULT_FIGURE_TITLE / DEFAULT_SUBPLOT_TITLE constants directly. - Replace the hard-coded 'unavailable' literal in Context.figureTitle's empty-state branch with DEFAULT_SUBPLOT_TITLE, so the filter is guaranteed to match if the constant ever changes. - Add AnnounceTitleCommand unit tests covering: authored figure title, authored layer title (single-panel fallback), no authored title in either place (verbose + terse), authored layer title (multi-panel), and figure-title-only fallback. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code Review — PR #616: fix: capture title from the jsonOverviewThis PR fixes a real and observable UX bug: the Positive Aspects
Issues and Suggestions1. Logic duplication between
|
| Command | Verbose unavailable text |
|---|---|
| Title (this PR) | "No title available" |
| Subtitle (unchanged) | "Subtitle is not available" |
| Caption (unchanged) | "Caption is not available" |
These three related commands now have inconsistent phrasing. Either align all three to the new pattern or keep the original for consistency.
3. Subtitle/Caption commands use raw string comparison (minor)
AnnounceSubtitleCommand and AnnounceCaptionCommand still compare directly against the string literal 'unavailable':
if (subtitle !== 'unavailable') { ... }Whereas the title command now uses isAuthoredTitle(). If DEFAULT_SUBTITLE or DEFAULT_CAPTION were changed, these checks would silently break. Consider extending isAuthoredTitle() (or a sibling helper) to cover these too, or at least import the constants rather than using the raw string.
4. isAuthoredTitle() does not handle empty strings (minor)
A title authored as "" (empty string) is not a placeholder default, but isAuthoredTitle("") returns true. Announcing "Title is " would be confusing. The old code had no guard for this either, so it is not a regression, but worth documenting or guarding.
5. Magic-string collision risk (informational)
If a user authors a title with the exact value "MAIDR Plot" or "unavailable", isAuthoredTitle() will incorrectly treat it as a placeholder. This is an inherent limitation of the sentinel-value approach in the model layer. A comment in isAuthoredTitle() noting this known limitation would help future maintainers.
6. Missing test cases
state.empty === truepath inexecute()(currently unreachable in tests)state.type === 'subplot'fallback branch (the// Fallback for unexpected state typescomment)- Multi-panel with no authored title at either level (falls through both
isAuthoredTitlechecks toannounceUnavailable()) - The
figureTitlegetter whenfigureState.emptyis true
Summary
The bug fix is correct and meaningfully improves accessibility. The main actionable items before merge are:
- Have
DescriptionServicereuseisAuthoredTitle()to avoid diverging filter logic (medium priority). - Align or document the verbose-mode phrasing inconsistency with subtitle/caption (minor, but affects screen-reader UX consistency).
- Add a test for the
state.emptypath and the subtitle/caption raw-string comparison gap.
Everything else is informational. Good work on the test file structure and the clean encapsulation of the sentinel-value check in Context.
Address remaining PR #616 review feedback: - DescriptionService now calls Context.isAuthoredTitle() instead of comparing against the model's default constants inline, removing the duplicated filter logic that could diverge over time. - Add Context.isAuthoredSubtitle() and Context.isAuthoredCaption() so the subtitle/caption announce commands stop comparing against the raw 'unavailable' string literal. Their figureSubtitle/figureCaption empty-state branches now return DEFAULT_SUBTITLE/DEFAULT_CAPTION too. - Align subtitle/caption verbose unavailable phrasing with the title command: 'No subtitle available' and 'No caption available'. - isAuthoredTitle/Subtitle/Caption now also treat empty / whitespace-only strings as unauthored, and the JSDoc on isAuthoredTitle calls out the known magic-string collision limitation. - Add three more AnnounceTitleCommand tests: empty state, unexpected (subplot) state-type fallback, and multi-panel with no authored title at either level. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code ReviewOverviewThis PR fixes a real usability bug: the The fix is clean and the approach is sound overall. What Works Well
Issues & Suggestions1.
|
…ates Address remaining PR #616 review feedback: - Add test/command/announce-metadata.test.ts covering AnnounceSubtitleCommand and AnnounceCaptionCommand: authored value verbose/terse output, and the "No X available" / "unavailable" fallbacks (verifying the warning tone fires). - Add test/model/context-authored.test.ts with direct unit tests for Context.isAuthoredTitle / isAuthoredSubtitle / isAuthoredCaption, covering placeholder defaults, empty/whitespace strings, and the substring-not-equal collision case. - Label the multi-panel figure-title fallback in announceTraceTitle as "Figure title" instead of plain "Title" so the announced source is self-describing (matches the figure-scope label), with a new test case. - Document why Context.figureTitle returns DEFAULT_SUBPLOT_TITLE rather than DEFAULT_FIGURE_TITLE in the empty-state branch, and note that isAuthoredSubtitle/isAuthoredCaption are intentionally symmetric so the two defaults can diverge independently. - Tighten the existing single-panel layer-title test to assert that no warning tone fires on the success path, and use the imported DEFAULT_SUBPLOT_TITLE constant instead of a string literal in the mock factory default. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Code Review – PR #616: fix: capture title from the jsonOverviewThis PR fixes a real accessibility regression: the What Works Well
Issues and Suggestions1. Known sentinel-string collision is worth flagging more prominently (minor)The documented limitation — a user who authors a title of literally "MAIDR Plot" or "unavailable" will silently get "No title available" instead — is acceptable for now, but worth a code comment at the sentinel constant definitions (in Suggestion: Add a short comment at 2. The
|
| Category | Assessment |
|---|---|
| Correctness | Fixes the reported bug correctly |
| Architecture (MVVC) | Layers respected; no boundary violations |
| Code quality | Clean, readable, well-commented |
| Test coverage | Comprehensive; one minor gap (figure-level terse mode) |
| Performance | No concerns (pure string comparisons) |
| Security | No concerns |
| Breaking changes | None — placeholder defaults still flow through model for other paths |
The PR is in good shape. Items 1 and 3 above are the most actionable improvements — everything else is cosmetic or forward-looking.
Pull Request
Description
Previously, the
l tshortcut (announce plot title) was not capturing the title authored in the MAIDR JSON. For plots that omitted a top-leveltitle, the announcement read "Title is MAIDR Plot" (the model's internal placeholder); for single-panel plots that authored their title at the layer level (e.g.examples/multiline_plot.html),l tnever consulted the layer title and announced "No title available" — even though thed(description) command correctly showed the authored title.Related Issues
Closes #613
Changes Made
After this change,
l treads the title strictly from the MAIDR JSON: it announces the authored title when one is provided (at either the figure or the layer level), and otherwise announces "No title available". Thel tanddcommands now agree on what counts as a real title.All changes are in
src/command/describe.ts:"MAIDR Plot","unavailable") and only announces titles authored in the JSON."Title"in single-panel,"Subplot title"in multi-panel), falling back to the authored figure title, then to "No title available"."Title is not available"to"No title available". Terse output stays"unavailable"to match the convention used by the X/Y/Z/subtitle/caption announce commands.No model-layer changes — the placeholder defaults still flow through the model for the UI and description paths that rely on them; only the title-announce command filters them out at announce time.
Screenshots (if applicable)
Checklist
ManualTestingProcess.md, and all tests related to this pull request pass.Additional Notes