From 47aadde2b2a1aedddce8fe0f9b1a5848c218cceb Mon Sep 17 00:00:00 2001 From: Jacob Schmidt Date: Sat, 28 Feb 2026 13:36:48 -0600 Subject: [PATCH] Refine markdown tag preview and fragment metadata formatting --- Journal.App/src/lib/backend/auth.ts | 1 - .../components/editor/FragmentEditor.svelte | 19 +++++++++++++++ .../components/editor/MarkdownEditor.svelte | 19 +++++++++++++++ Journal.App/src/lib/stores/fragments.ts | 6 ++--- Journal.App/src/lib/utils/markdown.ts | 23 +++++++++++++++++++ 5 files changed, 64 insertions(+), 4 deletions(-) diff --git a/Journal.App/src/lib/backend/auth.ts b/Journal.App/src/lib/backend/auth.ts index f1c3639..516d50f 100644 --- a/Journal.App/src/lib/backend/auth.ts +++ b/Journal.App/src/lib/backend/auth.ts @@ -1,6 +1,5 @@ import { sendCommand } from "./client"; import { pickCase } from "./normalize"; - export function hydrateWorkspace(password: string): Promise { return sendCommand({ action: "db.hydrate_workspace", diff --git a/Journal.App/src/lib/components/editor/FragmentEditor.svelte b/Journal.App/src/lib/components/editor/FragmentEditor.svelte index aa3b3c3..ba34034 100644 --- a/Journal.App/src/lib/components/editor/FragmentEditor.svelte +++ b/Journal.App/src/lib/components/editor/FragmentEditor.svelte @@ -376,6 +376,25 @@ font-size: 0.82rem; } + .fragment-view :global(.markdown-tag-list) { + display: inline-flex; + flex-wrap: wrap; + gap: 6px; + vertical-align: middle; + } + + .fragment-view :global(.markdown-tag-chip) { + display: inline-flex; + align-items: center; + border: 1px solid var(--border-soft); + border-radius: 999px; + padding: 1px 8px; + font-size: 0.74rem; + line-height: 1.35; + color: var(--text-muted); + background: color-mix(in srgb, var(--surface-2) 90%, var(--bg-editor) 10%); + } + .fragment-form h2 { font-size: 1rem; font-weight: 600; diff --git a/Journal.App/src/lib/components/editor/MarkdownEditor.svelte b/Journal.App/src/lib/components/editor/MarkdownEditor.svelte index 59a2cd0..65df234 100644 --- a/Journal.App/src/lib/components/editor/MarkdownEditor.svelte +++ b/Journal.App/src/lib/components/editor/MarkdownEditor.svelte @@ -860,6 +860,25 @@ text-decoration: underline; } + .markdown-preview :global(.markdown-tag-list) { + display: inline-flex; + flex-wrap: wrap; + gap: 6px; + vertical-align: middle; + } + + .markdown-preview :global(.markdown-tag-chip) { + display: inline-flex; + align-items: center; + border: 1px solid var(--border-soft); + border-radius: 999px; + padding: 1px 8px; + font-size: 0.74rem; + line-height: 1.35; + color: var(--text-muted); + background: color-mix(in srgb, var(--surface-2) 90%, var(--bg-editor) 10%); + } + @media (max-width: 980px) { .editor-workspace { padding: 4px 8px 10px; diff --git a/Journal.App/src/lib/stores/fragments.ts b/Journal.App/src/lib/stores/fragments.ts index c8f5ca0..96f8d2d 100644 --- a/Journal.App/src/lib/stores/fragments.ts +++ b/Journal.App/src/lib/stores/fragments.ts @@ -102,7 +102,7 @@ export function serializeFragment(payload: ParsedFragment): string { ? payload.tags.map((tag) => `#${tag}`).join(" ") : "(none)"; const body = payload.body.trim() || "Add details for this fragment."; - return `# ${title}\n\nType: ${type}\nTags: ${tagsLine}\n\n${body}`; + return `# ${title}\n\nType: ${type}\n\nTags: ${tagsLine}\n\n${body}`; } export function parseFragmentContent( @@ -112,7 +112,7 @@ export function parseFragmentContent( const headingMatch = content.match(/^#\s+(.+)$/m); const typeMatch = content.match(/^Type:\s*(.+)$/m); const tagsMatch = content.match(/^Tags:\s*(.+)$/m); - const bodyMatch = content.match(/^#.*\n\nType:.*\nTags:.*\n\n([\s\S]*)$/); + const bodyMatch = content.match(/^#.*\n\nType:.*\n\nTags:.*\n\n([\s\S]*)$/); const rawTags = tagsMatch?.[1]?.trim() ?? "(none)"; const tags = @@ -136,7 +136,7 @@ export function createFragmentDraft(): FragmentItem { return { id, label: "New Fragment", - initialContent: "# New Fragment\n\nType: \nTags: (none)\n\n", + initialContent: "# New Fragment\n\nType: \n\nTags: (none)\n\n", }; } diff --git a/Journal.App/src/lib/utils/markdown.ts b/Journal.App/src/lib/utils/markdown.ts index 11574b7..8850a75 100644 --- a/Journal.App/src/lib/utils/markdown.ts +++ b/Journal.App/src/lib/utils/markdown.ts @@ -9,6 +9,29 @@ export function escapeHtml(input: string): string { export function parseInline(input: string): string { let value = escapeHtml(input); + + // Render tag token groups like [[work, vibe]] as visual chips in preview. + value = value.replace(/\[\[([^[\]]+)\]\]/g, (match, rawGroup: string) => { + const tags = rawGroup + .split(",") + .map((tag) => tag.trim()) + .filter(Boolean); + + if (!tags.length) return match; + + const chips = tags + .map((tag) => `${tag}`) + .join(""); + return `${chips}`; + }); + + // Render hashtag-style tags (#Work) as chips in preview. + value = value.replace( + /(^|\s)#([A-Za-z0-9][A-Za-z0-9_-]*)\b/g, + (_, leading: string, tag: string) => + `${leading}#${tag}`, + ); + value = value.replace(/`([^`]+)`/g, "$1"); value = value.replace(/\*\*([^*]+)\*\*/g, "$1"); value = value.replace(/\+\+([^+]+)\+\+/g, "$1");