feat(workspace): links to single bullets
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m8s

- copy a link to a bullet from its action bubble or pick a bullet in the
  link dialog; [[Note#^id|Note › excerpt]] opens the note, jumps to and
  highlights the bullet, or says it is gone
- wiki links render inline with their label and are clickable; the
  duplicate chip below the text is gone

test(e2e): run each suite with its own container, image tag and port, so
parallel runs from another working copy no longer remove our server

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-10-30 20:42:14 +01:00
1 parent d408e7d307
commit 1a49e6b304
12 files changed
+284 -45

No files matched your search

+33
View File
@@ -0,0 +1,33 @@
// Wiki links: [[Titel]], [[Titel|Anzeigetext]] and links to a single bullet [[Titel#^blockId|Anzeigetext]].
// A missing bullet just opens the note (links are not updated when a bullet moves to another note).
export interface WikiLink {
title: string;
blockId?: string;
label?: string;
}
/** Parses the inside of [[…]]. */
export function parseWikiLink(inner: string): WikiLink {
const [target, ...rest] = inner.split('|');
const label = rest.join('|').trim() || undefined;
const hash = target.indexOf('#');
const title = (hash === -1 ? target : target.slice(0, hash)).trim();
const anchor = hash === -1 ? '' : target.slice(hash + 1).trim();
return { title, blockId: anchor.startsWith('^') ? anchor.slice(1) || undefined : undefined, label };
}
/** Text shown for a link: its label, else the note title (with » for a bullet). */
export function wikiLinkText(link: WikiLink): string {
if (link.label) return link.label;
return link.blockId ? `${link.title} »` : link.title;
}
/** Link to one bullet; the label is a short excerpt of the bullet so the reader knows what it points to. */
export function buildBlockLink(title: string, blockId: string, blockContent: string): string {
const clean = (s: string) => s.replace(/[[\]|]/g, '').replace(/\s+/g, ' ').trim();
let excerpt = clean(blockContent);
if (excerpt.length > 40) excerpt = `${excerpt.slice(0, 39).trimEnd()}…`;
const label = excerpt ? `${clean(title)} › ${excerpt}` : clean(title);
return `[[${clean(title)}#^${blockId}|${label}]]`;
}