-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Description
Tag Display Prioritization
Reorder and bold-highlight matched tags in the truncated Tags column of toneforge list recipes when --search or --tags filters are active, so developers can immediately see why each recipe matched.
Problem Statement
The toneforge list recipes Tags column truncates to 14 characters with a positional ellipsis. When a filter is active, the tags that matched the query may be truncated away, giving the user no visible indication of why a recipe appeared in the results.
Users
Game developers using ToneForge to generate procedural sound effects.
- As a game developer, I want the tags that matched my
--tagsfilter to appear first in the Tags column so I can confirm at a glance that the right recipes were returned. - As a game developer, I want the tags that contain my
--searchkeyword to appear first in the Tags column so I can see which tags are relevant to my search. - As a game developer, I want matched tags to be visually distinct (bold) so I can quickly scan a filtered list and identify matching attributes.
Success Criteria
- When a
--tagsfilter is active and the Tags column is truncated, tags that exactly match (case-insensitive) the filter values appear before non-matched tags. - When a
--searchfilter is active and the Tags column is truncated, tags that contain the search keyword as a substring (case-insensitive) appear before non-matched tags. - When both
--tagsand--searchare active, tags matching either filter are prioritized (matched tags first, then non-matched). - When no filter is active, or only a
--categoryfilter is active, tag order is unchanged (registration order preserved). The--categoryfilter does not affect tag prioritization since it matches against category, not tags. - In TTY mode, matched tags are rendered in ANSI bold; in non-TTY mode and when
NO_COLORis set, no ANSI codes are emitted. - The
--jsonoutput preserves the original tag order (no reordering); tag prioritization is a display-only concern for the table. - Table column alignment is not broken by ANSI escape codes in tag cells (ANSI-aware width measurement is implemented).
Constraints
- Tag column width: Fixed at 14 characters. ANSI bold escape codes are zero-width and must not count toward the column width budget.
- Backward compatibility: Unfiltered output must preserve the current tag order (registration order). No visible change when no filter is active.
- TTY gating: Bold styling must respect the existing
isStdoutTty()/NO_COLORgating insrc/output.ts. - JSON stability: JSON output schema and tag order must not change.
- No new dependencies: Implement ANSI-aware width measurement without adding external packages (a simple regex strip of
\x1b\[[0-9;]*msequences is sufficient). - Consistency: Tag match logic for prioritization should mirror the existing filter logic --
--tagsuses exact case-insensitive match,--searchuses substring across tag values (consistent withlistDetailed()insrc/core/recipe.ts).
Existing State
truncateTags()insrc/cli.ts:1016-1033joins tags in array order and truncates at 14 chars with ellipsis. No reordering, no styling.listDetailed()insrc/core/recipe.ts:362-425returnsRecipeDetailedSummary({ name, description, category, tags }) with no match metadata. Filtering is pass/fail with no indication of which tags contributed.RecipeFilterQuery(src/core/recipe.ts:160-181) carriessearch,category, andtagsfields.- Table rendering (
pad(),wordWrap()insrc/output.ts:202-261) usesString.length, which does not account for ANSI escape codes. - ANSI infrastructure (
COLORS,style(),isStdoutTty()) exists insrc/output.tsbut is not used for table cell content. - Existing tests cover tag truncation (
src/cli.test.ts:537-545) and filter behavior (src/cli.test.ts:290-409,src/core/recipe.test.ts:133-620+).
Desired Change
Registry API (src/core/recipe.ts)
- Extend
RecipeDetailedSummary(or create a new return type) to include amatchedTags: string[]field indicating which tags contributed to the filter match. - Modify
listDetailed()to populatematchedTagswhen a--tagsor--searchfilter is active:- For
--tags: tags that exactly match (case-insensitive) any filter value. - For
--search: tags that contain the search keyword as a substring (case-insensitive). - When no filter is active,
matchedTagsis an empty array.
- For
Table rendering (src/output.ts)
- Add an ANSI-stripping utility function (e.g.,
stripAnsi(s: string): string) using a regex like/\x1b\[[0-9;]*m/g. - Add an ANSI-aware string width function (e.g.,
ansiWidth(s: string): number) that strips ANSI codes before measuring length. - Update
pad()andwordWrap()to useansiWidth()instead ofString.lengthfor width calculations.
Tag rendering (src/cli.ts)
- Modify
truncateTags()(or create a new function) to accept amatchedTagsparameter and reorder matched tags to the front before truncation. - When in TTY mode, wrap matched tags in ANSI bold (
\x1b[1m...\x1b[0m) before joining. - Apply truncation after reordering and styling (using ANSI-aware width for the truncation point).
- The CLI
list recipeshandler passesmatchedTagsfrom theRecipeDetailedSummaryto the tag rendering function.
Tests
- Unit tests for matched-tag prioritization with truncation (both
--tagsand--searchtriggers). - Unit test confirming no reordering when no filter is active.
- Unit test for ANSI bold applied to matched tags in TTY mode.
- Unit test for no ANSI codes in non-TTY /
NO_COLORmode. - Unit tests for
stripAnsi()andansiWidth()utilities. - Unit test confirming
pad()andwordWrap()handle ANSI-styled strings correctly. - Unit test confirming JSON output preserves original tag order.
Risks & Assumptions
Assumptions
- All recipes have
tagspopulated asstring[]. If a recipe has no tags,matchedTagswill be empty and no reordering occurs -- this is a no-op, not an error. - The
COLORS.boldANSI escape sequence (\x1b[1m) and reset (\x1b[0m) are the only codes needed; no 256-color or truecolor sequences are required. - The
pad()andwordWrap()functions insrc/output.tsare only used for table rendering. Updating them to use ANSI-aware width has no unintended side effects elsewhere.
Risks
- ANSI-aware width regression: Changing
pad()andwordWrap()to use ANSI-aware measurement could introduce subtle layout issues if other code paths pass ANSI-styled strings that were previously measured byString.length. Mitigation: Review all call sites ofpad()andwordWrap()during implementation; add regression tests for existing table output. - Truncation mid-escape-code: If truncation slices through an ANSI escape sequence, the terminal may display corrupted output. Mitigation: Truncation logic must operate on visible character positions (after stripping ANSI), then re-apply styling to the truncated result.
- Scope creep: This feature could expand to include color-coded tags by match type, clickable tag links, or tag grouping/sorting beyond match priority. Mitigation: Record enhancement ideas as separate work items linked to TF-0MM7K306X1SLYFOL rather than expanding scope.
Related Work
- Add filtering to list recipes command (TF-0MM7ENNL90AHWC0N, in-progress) -- parent item; this is the only remaining open child.
- CLI Filter Flags & Four-Column Table (TF-0MM7K1YHL12T4EJH, completed) -- implemented current tag truncation and four-column table.
- Registry API: listDetailed with Filter (TF-0MM7K1K371LIKKZZ, completed) -- implemented
listDetailed()andRecipeFilterQuery. - JSON Output & Help Text Updates (TF-0MM7K2AW61OAO8VV, completed) -- JSON schema for list recipes.
- TUI Wizard: Interactive Sound Palette Builder (TF-0MM7HULM506CGSOP, open) -- may benefit from match metadata in the registry API.
src/library/search.ts-- existing AND-logic filter pattern for reference.
Related work (automated report)
Generated by find_related skill on 2026-03-01.
Work items
- Add filtering to list recipes command (TF-0MM7ENNL90AHWC0N, in-progress) -- Direct parent. This feature is the last remaining open child; it deferred matched-tag prioritization to this work item. The filter-flag parsing,
listDetailed()wiring, and four-column table introduced by its children are the foundation this task extends. - CLI Filter Flags & Four-Column Table (TF-0MM7K1YHL12T4EJH, completed) -- Implemented
truncateTags()insrc/cli.ts:1021-1033and the four-column table layout. This work item modifiestruncateTags()to accept matched-tag metadata, reorder tags, and apply ANSI bold styling. - Registry API: listDetailed with Filter (TF-0MM7K1K371LIKKZZ, completed) -- Introduced
RecipeDetailedSummaryandRecipeFilterQueryinsrc/core/recipe.ts. This work item extendsRecipeDetailedSummarywith amatchedTagsfield and modifieslistDetailed()to populate it. - JSON Output & Help Text Updates (TF-0MM7K2AW61OAO8VV, completed) -- Defined the JSON output schema (
{ command, resource, recipes, total, filters? }). Tag prioritization must not alter JSON output, so the schema stability established here is a constraint. - Integration Tests & Validation (TF-0MM7K2OG71UMBL1P, completed) -- Added 28 CLI integration tests covering filter behavior, tag truncation with ellipsis, and JSON output structure. New tests for matched-tag prioritization should follow the patterns established here. Has a
depends-ondependency relationship with this item. - TUI Wizard: Interactive Sound Palette Builder (TF-0MM7HULM506CGSOP, open) -- The wizard's Stage 1 (recipe browsing by category) would benefit from the
matchedTagsmetadata added toRecipeDetailedSummary, enabling richer display of filter-relevant tags in the interactive recipe picker. - list recipes should provide a summary of the sound (TF-0MLYXJASS12OSBJK, completed) -- Introduced
listSummaries(), the two-column table format,wordWrap(), andpad()utilities insrc/output.ts. Thepad()andwordWrap()functions must be updated to use ANSI-aware width measurement as part of this work item.
Repository files
src/cli.ts:1016-1033(truncateTags()) -- The primary function to modify. Currently joins tags in array order and truncates at a fixed width with ellipsis. Must be extended to acceptmatchedTags, reorder matched tags first, apply ANSI bold in TTY mode, and use ANSI-aware width for truncation.src/cli.ts:1247-1320(list recipes handler) -- Parses--search,--category,--tagsflags and callslistDetailed(). The handler must passmatchedTagsfrom the result to the updatedtruncateTags()function.src/core/recipe.ts:160-191(RecipeFilterQueryandRecipeDetailedSummaryinterfaces) --RecipeDetailedSummarymust be extended withmatchedTags: string[].RecipeFilterQuerydefines the filter shape that determines which tags match.src/core/recipe.ts:362-425(listDetailed()method) -- Must be modified to compute and populatematchedTagsbased on the active filter, using exact match for--tagsand substring match for--search.src/output.ts:46-54(isStdoutTty(),NO_COLORcheck) -- TTY/NO_COLOR gating logic that must be respected when applying ANSI bold to matched tags.src/output.ts:202-261(pad()andwordWrap()) -- Currently useString.lengthfor width measurement. Must be updated to use a newansiWidth()function that strips ANSI escape codes before measuring, to prevent ANSI bold codes from breaking column alignment.src/output.ts:19-27(COLORSobject) -- ProvidesCOLORS.bold(\x1b[1m) andCOLORS.reset(\x1b[0m) sequences to be used for matched-tag styling.src/cli.test.ts:537-545(tag truncation tests) -- Existing test for tag truncation with ellipsis. New tests for matched-tag prioritization, ANSI bold in TTY mode, and no-ANSI in non-TTY mode should be added alongside these.src/core/recipe.test.ts:133-620(listDetailedtest suite) -- Comprehensive tests for filter behavior. Tests formatchedTagspopulation should be added here.src/library/search.ts-- AND-logic filter pattern withSearchQueryinterface; serves as a reference implementation for the tag-matching logic to be added tolistDetailed().
Reactions are currently unavailable