feat(take-notes): add a manual tag vocabulary and gallery tag chips - #4
Merged
Conversation
Browsing the archive meant remembering a word that happens to appear in a
note's text. Tags give it a spine.
The vocabulary is closed and hand-curated in ~/take-notes/config.json: the
skill picks from the list and never invents a tag, so the taxonomy stays the
user's rather than the model's. `Unknown` is always present as the silent
fallback, which is also the default state.
notes.py becomes the single config reader — one place to decide what a
malformed file means — and learns to parse the rail's tag row back out, so an
untagged note written before this still parses as ("", ()).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The payoff of tagging. A chip per distinct tag under the filter bar: click to narrow, click again to clear, and it ANDs with the text filter rather than replacing it — the existing apply() grew one piece of state instead of a second filtering engine. Cards carry every tag in data-tags, folded and pipe-delimited at both ends so a whole tag matches without "ai" also hitting "air gap". Notes with no tag of their own group under Unknown, which keeps the committed examples reachable. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Tags that stop at the gallery are half a feature. The Markdown frontmatter lists them after take-notes, which is what Obsidian's tag pane reads, and they join the card kind in the Anki tags column so a deck filters by topic. Anki splits that field on whitespace, so a two-word tag is hyphenated on the way out or it silently becomes two unrelated tags. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A Tags section beside Language, since both are config.json concerns: the closed vocabulary, the silent Unknown fallback, and both ways to edit it. The Gallery and Export sections gain the line each was missing. CONTRIBUTING gets .tags/.tag in the masthead-contract table — notes.py parses it now, so renaming those classes breaks the parser, which is the point. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A script with its own asserts that nothing runs is a script with no asserts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Screenshotting the gallery caught it: `overflow: hidden` on the tag resolves its flex min-width to 0, so it became the item that shrank and rendered as "ENGINEE…" — a clipped label defeats the whole point of putting it there. Both labels keep their intrinsic width and the foot wraps instead, dropping the date onto its own line when the row is full. `justify-content: space-between` goes with it: the date's margin-left:auto already pushes it right, and space-between flung the tag away from the kind once the row wrapped. Adds the screenshot the README's new Tags section points at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a manually curated tag vocabulary so the archive can answer "show me my AI notes" — today the gallery offers only a free-text filter over title/byline/meta, so browsing by topic means remembering a word that happens to appear in the text.
The vocabulary is closed: it lives in
~/take-notes/config.json, the skill picks from it and never invents a tag, so the taxonomy stays the user's rather than whatever the model felt like that day. Default state is a single tag,Unknown, which is also the silent fallback when nothing fits.{ "language": "es", "tags": ["Unknown", "AI", "Investing", "Engineering"] }How it looks in the gallery
→
docs/gallery-tags.png— added in this PR, and rendered inline in the README's new Tags section.(The repo is private, so GitHub can't proxy the image into this body. The Files changed tab shows it too.)
Two things are new on this page:
2/4. Chip and text filter combine with AND — the existingapply()grew one piece of state rather than a second filtering engine.VIDEO AI,ARTICLE ENGINEERING). Extras don't show there but are still filterable: every tag goes intodata-tags, folded and pipe-delimited at both ends so a whole tag matches withoutaialso hittingair gap.Notes written before tagging existed parse as untagged and group under the
Unknownchip, so nothing falls out of the grid.What's in it
scripts/notes.pyread_config,configured_tags) and parses the rail's tag row back out;Notegainstag+tagsscripts/tags.py(new)uv run tags.py [--add X] [--remove X]— read-modify-write, solanguagesurvives; refuses to removeUnknownscripts/render.py--tag, first occurrence is primary; stays a dumb renderer and validates nothingscripts/gallery.pydata-tagsscripts/export.pytags:list and the Anki tags columnSKILL.md--tagin Step 5{{TAGS}}after the.watchlink, plus.tags/.tag/.tag.is-primarystylingManaging the vocabulary works from either side —
/take-notes --tags | --add-tag "AI" | --remove-tag "AI", ortags.pydirectly. Both are worth having: the skill isdisable-model-invocation: true, so saying "add a tag" in ordinary conversation never reaches it.Verified
for s in render notes gallery export transcript tags; do uv run skills/take-notes/scripts/$s.py --selftest; done— all six pass, andtags.pyis now in the CI loop.Unknownchip.--add AI --add Investing, then--remove Investing;"language": "es"intact throughout, andUnknownrefused removal.--tag AI --tag Engineeringreads back astag == "AI",tags == ("AI", "Engineering").tags: ["take-notes", "AI", "Engineering"]in the frontmatter;take-notes key-point AI Engineeringin the Anki column, whitespace hyphenated since Anki splits that field on spaces.Still open:
/take-notes --add-tagand a full end-to-end run against a real source, both of which need the skill symlink pointing at this branch.🤖 Generated with Claude Code