feat(tags): preserve tag case with case-insensitive identity#689
Merged
Conversation
Tag identity becomes case-insensitive at the DB level (note_tags.tag, task_tags.tag, inbox_item_tags.tag, tag_definitions.name) via a nocaseText customType. Migrations preserve existing data; INSERT OR IGNORE drops case-duplicate rows that would violate the now case-insensitive keys.
Stop lowercasing tags at every main-process write path: frontmatter and inline #tag extraction, note_tags/task_tags/inbox_item_tags inserts, sync task handler, inbox bulk-tag, app-core inbox, and the Raindrop importer. Values keep the case the user typed; writes dedupe case-insensitively (first spelling wins). Rename/merge write the new name as typed while matching case-insensitively. Tag list aggregation keys by lowercase and keeps first-seen casing for display. tag_definitions rows and sync item ids stay lowercase as stable identity keys.
Editor hash-tag plugins no longer lowercase while typing; normalizeHashTags and extractInlineTags keep typed case with case-insensitive dedupe. Tag color/icon maps are keyed by lowercase and all lookups lowercase the key, so case variants share one color. Autocomplete, tag manager search, and duplicate checks compare case-insensitively.
|
React Doctor found no issues. 🎉
|
Two sources of mismatched tag pill colors in the editor: - defaultTagColorName and the autocomplete fallback hashed the case-sensitive name, so #Test and #test fell back to different palette colors whenever the color map missed. Both now hash the lowercased name. - The editor's tagColorMap ignored pending colors of tags just created in the tags row, so an inline pill typed before reindex+refetch got the hashed fallback instead of the picked color. Pending colors now merge into the map. Also the autocomplete create footer no longer lowercases the tag it adds.
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.
Summary
Tags now keep the capitalization you type —
#Workstays#Work— while identity stays case-insensitive:#Workand#workare the same tag with one color and one combined count (Obsidian-style). Previously every layer force-lowercased tags, including Obsidian imports.Changes
Schema + migrations
nocaseTextcustom type (text COLLATE NOCASE) onnote_tags.tag,task_tags.tag,inbox_item_tags.tag,tag_definitions.name0019(generated), data DB migration0034_tag_nocase.sql(hand-written, matching existing data-DB migration convention). Data preserved;INSERT OR IGNOREcollapses case-duplicate rowsMain process
#tagextraction preserve case, dedupe case-insensitively (first spelling wins) — this fixes the Obsidian import pathsetNoteTags/setTaskTags/ sync task handler / inbox bulk-tag / app-core inbox / Raindrop importer stop lowercasing on writerenameTagchild rewrite is now per-row (SQLreplace()is case-sensitive)tag_definitionsrows and sync item ids stay lowercase (stable identity keys)Renderer
normalizeHashTags/extractInlineTagspreserve case with case-insensitive dedupeBehavior notes
Documentation
apps/docs/src/user-guide/notes/properties-tags.mddocuments the new behavior;pnpm docs:impact --strictandpnpm docs:buildpassTest plan
0034is exercised)typecheck:node+typecheck:webclean, ESLint 0 errors, Prettier cleanfrontmatter.test.ts, ci identity innotes.test.ts(Work+workcollapse;findNotesByTag('work')findsWORK)#Workin editor, import an Obsidian vault with mixed-case tags