mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Use Obsidian link parsing for wikilinks
This commit is contained in:
parent
41a96482c7
commit
1959bf8c0d
5 changed files with 44 additions and 25 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import type { SkillMetadata } from "../../../generated/app-server/v2/SkillMetadata";
|
||||
import type { UserInput } from "../../../generated/app-server/v2/UserInput";
|
||||
import { parseObsidianWikiLink } from "../../../shared/obsidian/wikilinks";
|
||||
|
||||
export interface ParsedWikiLink {
|
||||
raw: string;
|
||||
|
|
@ -76,24 +77,8 @@ export function parsedSkillReferences(text: string): string[] {
|
|||
}
|
||||
|
||||
function parseWikiLink(raw: string): ParsedWikiLink | null {
|
||||
const separator = raw.indexOf("|");
|
||||
const linkPart = (separator === -1 ? raw : raw.slice(0, separator)).trim();
|
||||
const display = separator === -1 ? "" : raw.slice(separator + 1).trim();
|
||||
if (!linkPart) return null;
|
||||
|
||||
const subpathStart = firstSubpathIndex(linkPart);
|
||||
const target = subpathStart === -1 ? linkPart : linkPart.slice(0, subpathStart).trim();
|
||||
const subpath = subpathStart === -1 ? "" : linkPart.slice(subpathStart).trim();
|
||||
if (!target) return null;
|
||||
return { raw, target, subpath, display };
|
||||
}
|
||||
|
||||
function firstSubpathIndex(linkPart: string): number {
|
||||
const headingIndex = linkPart.indexOf("#");
|
||||
const blockIndex = linkPart.indexOf("^");
|
||||
if (headingIndex === -1) return blockIndex;
|
||||
if (blockIndex === -1) return headingIndex;
|
||||
return Math.min(headingIndex, blockIndex);
|
||||
const parsed = parseObsidianWikiLink(raw);
|
||||
return parsed ? { raw, ...parsed } : null;
|
||||
}
|
||||
|
||||
function firstEnabledSkillByName(skills: SkillMetadata[]): Map<string, SkillMetadata> {
|
||||
|
|
|
|||
23
src/shared/obsidian/wikilinks.ts
Normal file
23
src/shared/obsidian/wikilinks.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { parseLinktext } from "obsidian";
|
||||
|
||||
export interface ParsedObsidianWikiLink {
|
||||
target: string;
|
||||
subpath: string;
|
||||
display: string;
|
||||
}
|
||||
|
||||
export function parseObsidianWikiLink(raw: string): ParsedObsidianWikiLink | null {
|
||||
const trimmed = raw.trim();
|
||||
if (!trimmed) return null;
|
||||
|
||||
const separator = trimmed.indexOf("|");
|
||||
const linktext = (separator === -1 ? trimmed : trimmed.slice(0, separator)).trim();
|
||||
const display = separator === -1 ? "" : trimmed.slice(separator + 1).trim();
|
||||
if (!linktext) return null;
|
||||
|
||||
const parsed = parseLinktext(linktext);
|
||||
const target = parsed.path.trim();
|
||||
const subpath = parsed.subpath.trim();
|
||||
if (!target) return null;
|
||||
return { target, subpath, display };
|
||||
}
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
import { parseObsidianWikiLink } from "../obsidian/wikilinks";
|
||||
|
||||
export function shortSignature(value: string): string {
|
||||
let hash = 0;
|
||||
for (let index = 0; index < value.length; index += 1) {
|
||||
|
|
@ -19,13 +21,13 @@ export function renderTextWithWikiLinks(parent: HTMLElement, text: string, openL
|
|||
const rawLink = match[1];
|
||||
const rawMatch = match[0];
|
||||
if (rawLink === undefined) continue;
|
||||
const separator = rawLink.indexOf("|");
|
||||
const target = (separator === -1 ? rawLink : rawLink.slice(0, separator)).trim();
|
||||
const label = (separator === -1 ? rawLink : rawLink.slice(separator + 1)).trim() || target;
|
||||
const parsed = parseObsidianWikiLink(rawLink);
|
||||
|
||||
if (target.length === 0) {
|
||||
if (!parsed) {
|
||||
parent.appendChild(doc.createTextNode(rawMatch));
|
||||
} else {
|
||||
const target = `${parsed.target}${parsed.subpath}`;
|
||||
const label = parsed.display || target;
|
||||
const link = parent.createEl("a", {
|
||||
cls: "internal-link codex-panel__wikilink",
|
||||
text: label,
|
||||
|
|
|
|||
|
|
@ -24,15 +24,15 @@ describe("view DOM helpers", () => {
|
|||
const parent = document.createElement("div");
|
||||
const openLink = vi.fn();
|
||||
|
||||
renderTextWithWikiLinks(parent, "See [[Target Note|label]] and [[Other]].", openLink);
|
||||
renderTextWithWikiLinks(parent, "See [[Target Note#Heading|label]] and [[Other]].", openLink);
|
||||
|
||||
const links = parent.querySelectorAll<HTMLAnchorElement>("a.internal-link");
|
||||
const firstLink = expectPresent(links[0]);
|
||||
expect(links).toHaveLength(2);
|
||||
expect(firstLink.textContent).toBe("label");
|
||||
expect(firstLink.getAttribute("href")).toBe("Target Note");
|
||||
expect(firstLink.getAttribute("href")).toBe("Target Note#Heading");
|
||||
|
||||
firstLink.click();
|
||||
expect(openLink).toHaveBeenCalledWith("Target Note");
|
||||
expect(openLink).toHaveBeenCalledWith("Target Note#Heading");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -89,6 +89,15 @@ export function sortSearchResults(results: { match: { score: number } }[]): void
|
|||
results.sort((a, b) => b.match.score - a.match.score);
|
||||
}
|
||||
|
||||
export function parseLinktext(linktext: string): { path: string; subpath: string } {
|
||||
const headingIndex = linktext.indexOf("#");
|
||||
const blockIndex = linktext.indexOf("^");
|
||||
const subpathStart = headingIndex === -1 ? blockIndex : blockIndex === -1 ? headingIndex : Math.min(headingIndex, blockIndex);
|
||||
return subpathStart === -1
|
||||
? { path: linktext, subpath: "" }
|
||||
: { path: linktext.slice(0, subpathStart), subpath: linktext.slice(subpathStart) };
|
||||
}
|
||||
|
||||
export class Modal {
|
||||
readonly contentEl: HTMLElement;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue