feat(web): redesign Knowledge as an editable wiki
Replaces the read-only stats dashboard with a three-pane wiki: a navigator tree (group by folder/type/tag/entity), a reader/editor with bare-slug auto-linking and revision history + diff, and a context rail for backlinks and related notes. Adds a Cleanup mode for the drift tools (duplicates, tag manager, orphans, trash) and a Cmd+K quick-open. Also: - Adds a real landing view (hero count, KPI row, Nomos-share meter, recently-updated, busiest tags) in place of the old "Select a note" empty state, and extends the design pass across the tree/reader/rail (kind icons instead of repeated text badges, accent-bar selection, constrained prose measure). - Guards every note-selection path behind a confirm when there's an unsaved edit in progress, so switching notes can no longer silently discard a draft. - Extracts the markdown-rendering CSS duplicated across ChatThread, EntityDetailContent, and the new WikiReader into a shared .markdown-body class in app.css, with ChatThread keeping only its decorative deltas. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -274,7 +274,9 @@
|
||||
/>
|
||||
{/if}
|
||||
{#if msg.text}
|
||||
<div class="prose-chat max-w-none text-sm leading-relaxed assistant-msg">
|
||||
<div
|
||||
class="markdown-body prose-chat max-w-none text-sm leading-relaxed assistant-msg"
|
||||
>
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags — sanitized via DOMPurify -->
|
||||
{@html render(msg.text)}
|
||||
{#if isLast && streaming}
|
||||
@@ -416,50 +418,36 @@
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* Prose overrides */
|
||||
.prose-chat :global(p) {
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
.prose-chat :global(p:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.prose-chat :global(ul),
|
||||
.prose-chat :global(ol) {
|
||||
margin: 0 0 0.5rem;
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
.prose-chat :global(ul) {
|
||||
list-style-type: disc;
|
||||
}
|
||||
.prose-chat :global(ol) {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
/* Prose overrides — deltas on top of the shared .markdown-body base
|
||||
(app.css) only. The template applies both classes together
|
||||
(class="markdown-body prose-chat ..."); everything below either adds a
|
||||
look .markdown-body doesn't have (li::marker, the pre/blockquote
|
||||
::before ornaments, hr, strong, the table-wrapper, the code-copy
|
||||
button) or overrides a .markdown-body value that this "Art Nouveau"
|
||||
chat treatment wants different (code/pre padding, heading size, th/td
|
||||
padding, blockquote border color, link underline style). Anywhere a
|
||||
value is actually overridden, the selector is
|
||||
`.markdown-body.prose-chat` rather than `.prose-chat` alone —
|
||||
:global() selectors from two different <style> blocks land in the same
|
||||
stylesheet with no scoping to arbitrate between them, so equal
|
||||
specificity would leave the winner to injection order (unreliable
|
||||
across dev/build). The two-class selector's higher specificity wins
|
||||
deterministically regardless. */
|
||||
.prose-chat :global(li) {
|
||||
margin-bottom: 0.125rem;
|
||||
padding-left: 0.25rem;
|
||||
}
|
||||
.prose-chat :global(li::marker) {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.prose-chat :global(code) {
|
||||
background: var(--muted);
|
||||
.markdown-body.prose-chat :global(code) {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 0.15em 0.4em;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.85em;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.prose-chat :global(pre) {
|
||||
background: var(--muted);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
.markdown-body.prose-chat :global(pre) {
|
||||
padding: 0.75rem 0.875rem;
|
||||
overflow-x: auto;
|
||||
margin: 0 0 0.5rem;
|
||||
position: relative;
|
||||
}
|
||||
.prose-chat :global(pre)::before {
|
||||
@@ -473,34 +461,28 @@
|
||||
opacity: 0.4;
|
||||
}
|
||||
.prose-chat :global(pre code) {
|
||||
background: none;
|
||||
padding: 0;
|
||||
font-size: 0.8125rem;
|
||||
color: inherit;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* Section headings — serif (Inknut) with a short accent rule. Extra top
|
||||
margin separates sections; the first heading in a message doesn't. */
|
||||
.prose-chat :global(h1) {
|
||||
.markdown-body.prose-chat :global(h1) {
|
||||
font-size: 1.15em;
|
||||
font-weight: 600;
|
||||
margin: 1.15rem 0 0.4rem;
|
||||
letter-spacing: 0.01em;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.prose-chat :global(h2) {
|
||||
.markdown-body.prose-chat :global(h2) {
|
||||
font-size: 1.08em;
|
||||
font-weight: 600;
|
||||
margin: 1.15rem 0 0.4rem;
|
||||
letter-spacing: 0.01em;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.prose-chat :global(h3) {
|
||||
.markdown-body.prose-chat :global(h3) {
|
||||
font-size: 1.02em;
|
||||
font-weight: 600;
|
||||
margin: 1.15rem 0 0.4rem;
|
||||
letter-spacing: 0.01em;
|
||||
position: relative;
|
||||
@@ -524,11 +506,6 @@
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.prose-chat :global(table) {
|
||||
border-collapse: collapse;
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
.prose-chat :global(.table-wrapper) {
|
||||
overflow-x: auto;
|
||||
margin: 0 0 0.5rem;
|
||||
@@ -540,18 +517,13 @@
|
||||
background: var(--muted);
|
||||
font-weight: 600;
|
||||
}
|
||||
.prose-chat :global(th),
|
||||
.prose-chat :global(td) {
|
||||
border: 1px solid var(--border);
|
||||
.markdown-body.prose-chat :global(th),
|
||||
.markdown-body.prose-chat :global(td) {
|
||||
padding: 0.3rem 0.6rem;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.prose-chat :global(blockquote) {
|
||||
.markdown-body.prose-chat :global(blockquote) {
|
||||
border-left: 3px solid var(--primary);
|
||||
padding-left: 0.75rem;
|
||||
color: var(--muted-foreground);
|
||||
margin: 0 0 0.5rem;
|
||||
font-style: italic;
|
||||
position: relative;
|
||||
}
|
||||
@@ -587,8 +559,7 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.prose-chat :global(a) {
|
||||
color: var(--primary);
|
||||
.markdown-body.prose-chat :global(a) {
|
||||
text-decoration: underline;
|
||||
text-decoration-style: dotted;
|
||||
text-underline-offset: 2px;
|
||||
|
||||
Reference in New Issue
Block a user