Skip to content

feat(tags): preserve tag case with case-insensitive identity#689

Merged
h4yfans merged 5 commits into
mainfrom
tags-case-preserving
Jul 4, 2026
Merged

feat(tags): preserve tag case with case-insensitive identity#689
h4yfans merged 5 commits into
mainfrom
tags-case-preserving

Conversation

@h4yfans

@h4yfans h4yfans commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Tags now keep the capitalization you type — #Work stays #Work — while identity stays case-insensitive: #Work and #work are the same tag with one color and one combined count (Obsidian-style). Previously every layer force-lowercased tags, including Obsidian imports.

Changes

Schema + migrations

  • New nocaseText custom type (text COLLATE NOCASE) on note_tags.tag, task_tags.tag, inbox_item_tags.tag, tag_definitions.name
  • Index DB migration 0019 (generated), data DB migration 0034_tag_nocase.sql (hand-written, matching existing data-DB migration convention). Data preserved; INSERT OR IGNORE collapses case-duplicate rows

Main process

  • Frontmatter + inline #tag extraction preserve case, dedupe case-insensitively (first spelling wins) — this fixes the Obsidian import path
  • setNoteTags / setTaskTags / sync task handler / inbox bulk-tag / app-core inbox / Raindrop importer stop lowercasing on write
  • Rename/merge write the new name as typed, match case-insensitively; renameTag child rewrite is now per-row (SQL replace() is case-sensitive)
  • Tag list aggregation keys by lowercase, display name keeps first-seen casing
  • tag_definitions rows and sync item ids stay lowercase (stable identity keys)

Renderer

  • Editor hash-tag plugins no longer lowercase while typing
  • normalizeHashTags / extractInlineTags preserve case with case-insensitive dedupe
  • Tag color/icon maps keyed by lowercase; all lookups lowercase the key, so case variants share one color
  • Autocomplete, tag manager search, and duplicate checks compare case-insensitively

Behavior notes

  • Notes previously saved in-app keep their lowercased frontmatter until re-edited or renamed (case-only renames now work in Tag Manager)
  • Untouched Obsidian-vault files regain their original casing on reindex

Documentation

  • apps/docs/src/user-guide/notes/properties-tags.md documents the new behavior; pnpm docs:impact --strict and pnpm docs:build pass

Test plan

  • Desktop main suite: 3335 passed (runs real migrations, so 0034 is exercised)
  • Desktop renderer suite: 5062 passed
  • app-core: 7 passed
  • typecheck:node + typecheck:web clean, ESLint 0 errors, Prettier clean
  • New regression tests: case-preserve + ci-dedupe in frontmatter.test.ts, ci identity in notes.test.ts (Work+work collapse; findNotesByTag('work') finds WORK)
  • Manual smoke: type #Work in editor, import an Obsidian vault with mixed-case tags

h4yfans added 4 commits July 5, 2026 02:42
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.
@github-actions github-actions Bot added documentation Improvements or additions to documentation enhancement New feature or request test labels Jul 4, 2026
@github-actions

github-actions Bot commented Jul 4, 2026

Copy link
Copy Markdown

React Doctor found no issues. 🎉

⚠️ Warning: .github/workflows/react-doctor.yml is configured incorrectly. See below to fix.

React Doctor compares against main to report only the issues this pull request introduces. This run couldn't complete that comparison (usually a shallow CI checkout with no merge base), so it listed every issue in the changed files, including ones that already existed on main.

Add fetch-depth: 0 to the actions/checkout step in .github/workflows/react-doctor.yml so the checkout includes the history React Doctor needs:

 jobs:
   react-doctor:
     steps:
       - uses: actions/checkout@v5
+        with:
+          fetch-depth: 0

       - uses: millionco/react-doctor@v2

To silence this warning, set silence-missing-baseline-warning: true on the React Doctor action.

Reviewed by React Doctor for commit ab95229.

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.
@h4yfans h4yfans marked this pull request as ready for review July 4, 2026 23:59
@h4yfans h4yfans merged commit a9b3f52 into main Jul 4, 2026
9 of 11 checks passed
@h4yfans h4yfans deleted the tags-case-preserving branch July 4, 2026 23:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation enhancement New feature or request test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant