Fix apostrophe rendering: prevent HTML entities from matching as tags

The TAG_RE regex in linkifyTags() was matching #39 inside ' as a
tag, breaking the HTML entity and preventing apostrophe rendering.
Added (?<!&) negative lookbehind so # preceded by & is skipped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-10 21:59:02 -05:00
parent 38b1ac933e
commit de899ebc50
2 changed files with 5 additions and 4 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
const CODE_FENCE_RE = /```[\s\S]*?```|`[^`\n]+`/g;
const TAG_RE = /(?<!\w)#([\w]+(?:\/[\w]+)*)/g;
const TAG_RE = /(?<!\w)(?<!&)#([\w]+(?:\/[\w]+)*)/g;
function escapeHtmlAttr(s: string): string {
return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
+4 -3
View File
@@ -43,7 +43,8 @@ for AI-assisted features.
compatible) over HTTP.
- **Inline tag extraction:** Tags are extracted from note/task body text using
`#tag` syntax (Obsidian-style), not manually entered. Backend is source of truth
for tag extraction.
for tag extraction. The `TAG_RE` regex uses `(?<!&)` negative lookbehind to avoid
matching HTML entities like `&#39;` as tags.
- **Hierarchical tags:** `#project/webapp` stored as `"project/webapp"`. Filtering
by `project` matches both `project` and `project/*` children via SQL `unnest` +
`LIKE` prefix.
@@ -216,7 +217,7 @@ fabledassistant/
│ │ ├── settings.ts # AppSettings interface, ModelInfo interface (name, description, size, bestFor, category)
│ │ └── task.ts # Task = re-export of Note; TaskListResponse
│ ├── utils/
│ │ ├── tags.ts # extractTags(), linkifyTags(), linkifyWikilinks()
│ │ ├── tags.ts # extractTags(), linkifyTags() (with (?<!&) to skip HTML entities), linkifyWikilinks()
│ │ └── markdown.ts # renderMarkdown() (full) + renderPreview() (strips links/images) — decodes HTML entities before marked, replaces &#39; after sanitization
│ ├── views/
│ │ ├── ChatView.vue # Dedicated /chat page: sidebar + bubble-style messages (user right, assistant left) + floating dark input bar + auto-focus
@@ -445,7 +446,7 @@ When adding a new migration, follow these conventions:
- [x] **Chat bubble layout:** User messages right-aligned (primary color bg), assistant messages left-aligned (card bg), speech bubble tails
- [x] **Floating dark input bar:** `#1c1c1e` background, rounded corners, circular send button with arrow
- [x] **Auto-focus input:** Chat input auto-focuses on mount, conversation switch, and after sending
- [x] **HTML entity fix:** Three-layer approach — decode entities before marked, DOMPurify sanitization, explicit `&#39;` → `'` replacement after sanitization
- [x] **HTML entity fix:** `linkifyTags` regex now uses `(?<!&)` lookbehind to avoid matching `#39` inside `&#39;` as a tag. Additional layers: decode entities before marked, DOMPurify sanitization, explicit `&#39;` → `'` replacement after sanitization
- [x] **New chat navigation fix:** `fetchConversation()` called before `router.push()` so `currentConversation` is set immediately
- [x] **Recent chats on home page:** 3 most recent conversations shown as clickable cards + "New Chat" button