diff --git a/web/src/app.css b/web/src/app.css index 48e26ed..b11dd96 100644 --- a/web/src/app.css +++ b/web/src/app.css @@ -333,3 +333,92 @@ a:hover { border-left: 1px solid var(--border); cursor: col-resize; } + +/* Base markdown rendering — used by every {@html marked.parse(...)} output + (EntityDetailContent, the Knowledge wiki's WikiReader, and as the + foundation ChatThread's fuller "Art Nouveau" chat styling builds on top + of). Global rather than a per-component diff --git a/web/src/lib/components/knowledge/WikiCleanup.svelte b/web/src/lib/components/knowledge/WikiCleanup.svelte new file mode 100644 index 0000000..90287f9 --- /dev/null +++ b/web/src/lib/components/knowledge/WikiCleanup.svelte @@ -0,0 +1,570 @@ + + +
+ + + activate('duplicates')}> + Duplicates + + activate('tags')}> + Tags + + activate('orphans')}> + Orphans + + activate('trash')}> + Trash + + + + +

+ Notes with near-identical titles, grouped for review — not a verdict. Pick a target and the + sources to fold into it; sources are soft-deleted afterward and stay recoverable from Trash. +

+ {#if mergeError} +

+ {mergeError} +

+ {/if} + {#if duplicatesError} +

+ {duplicatesError} + +

+ {/if} + {#if clusters === null} +
+ {:else if clusters.length === 0} +

No likely duplicates found.

+ {:else} +
+ {#each clusters as c (c.members[0].slug)} + {@const key = c.members[0].slug} + {@const sourceCount = mergeSources[key]?.size ?? 0} +
+
+
+ {c.members.length} similar notes + + + {(c.top_similarity * 100).toFixed(0)}% +
+ +
+ +

+ + keep as target + + + fold into it + +

+
+ {#each c.members as m (m.slug)} + {@const isTarget = mergeTarget[key] === m.slug} + {@const Icon = kindMeta(m.kind).icon} + + {/each} +
+
+ {/each} +
+ {/if} +
+ + + {#if tagsError} +

+ {tagsError} + +

+ {/if} + {#if tags === null} +
+ {:else} + {@const maxUses = Math.max(1, ...tags.map((t) => t.uses))} + + + + + + + + + + + {#each tags as t (t.tag)} + + + + + + + + + {/each} + +
TagUsesVariants
+ {#if renaming === t.tag} +
+ + +
+ {:else} + + {/if} +
{t.uses} + + + + + + {#if t.split} + {t.variants.join(', ')} + {:else} + + {/if} + + {#if t.split} + + {/if} +
+ {/if} +
+ + +

+ Notes untagged, unlinked to any entity, or untouched for 90+ days — invisible to most + navigation paths and easy to lose track of. + {#if orphanCounts.untagged || orphanCounts.unlinked || orphanCounts.stale} + ({orphanCounts.untagged ?? 0} untagged · {orphanCounts.unlinked ?? 0} unlinked · {orphanCounts.stale ?? + 0} stale) + {/if} +

+ {#if orphansError} +

+ {orphansError} + +

+ {/if} + {#if orphans === null} +
+ {:else if orphans.length === 0} +

Nothing orphaned.

+ {:else} +
+ {#each orphans as o (o.slug)} + + {/each} +
+ {/if} +
+ + + {#if trashError} +

+ {trashError} + +

+ {/if} + {#if trash === null} +
+ {:else if trash.length === 0} +

Trash is empty.

+ {:else} +
+ {#each trash as t (t.slug)} +
+ {t.title} + + deleted {relativeTime(t.deleted_at)} by {t.deleted_by || 'unknown'} + + +
+ {/each} +
+ {/if} +
+
+
diff --git a/web/src/lib/components/knowledge/WikiContextRail.svelte b/web/src/lib/components/knowledge/WikiContextRail.svelte new file mode 100644 index 0000000..26f56b6 --- /dev/null +++ b/web/src/lib/components/knowledge/WikiContextRail.svelte @@ -0,0 +1,124 @@ + + +
+ {#if !item} +

Nothing selected.

+ {:else} + + {#if item.about.length === 0} +

Not linked to any entity.

+ {:else} +
+ {#each item.about as slug (slug)} + + {/each} +
+ {/if} +
+ + 0} + > + {#if related.length === 0} +

No other notes share a linked entity.

+ {:else} +
+ {#each related as it (it.slug)} + {@const Icon = kindMeta(it.kind).icon} + + {/each} +
+ {/if} +
+ + + 0 && tagNeighbours.length <= 6} + > + {#if tagNeighbours.length === 0} +

No other notes share a tag.

+ {:else} +
+ {#each tagNeighbours as it (it.slug)} + {@const Icon = kindMeta(it.kind).icon} + + {/each} +
+ {/if} +
+ {/if} +
diff --git a/web/src/lib/components/knowledge/WikiNewDialog.svelte b/web/src/lib/components/knowledge/WikiNewDialog.svelte new file mode 100644 index 0000000..91e4605 --- /dev/null +++ b/web/src/lib/components/knowledge/WikiNewDialog.svelte @@ -0,0 +1,139 @@ + + + + + + New knowledge note + + +
+ {#if error} +

+ {error} +

+ {/if} + + + +
+ Type +
+ {#each KINDS as k (k)} + + {/each} +
+
+ + + + + +