Fixes #621 with improved snippets for page links
parent
63eb99e0d3
commit
89e2e7a37c
|
@ -56,7 +56,7 @@ export async function indexHeaders({ name: pageName, tree }: IndexTreeEvent) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Found", headers, "headers(s)");
|
// console.log("Found", headers, "headers(s)");
|
||||||
await indexObjects(pageName, headers);
|
await indexObjects(pageName, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { ObjectValue } from "../../plug-api/types.ts";
|
||||||
import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
|
import { extractFrontmatter } from "$sb/lib/frontmatter.ts";
|
||||||
import { updateITags } from "$sb/lib/tags.ts";
|
import { updateITags } from "$sb/lib/tags.ts";
|
||||||
import { parsePageRef } from "$sb/lib/page_ref.ts";
|
import { parsePageRef } from "$sb/lib/page_ref.ts";
|
||||||
|
import { extractSnippetAroundIndex } from "./snippet_extractor.ts";
|
||||||
|
|
||||||
const pageRefRegex = /\[\[([^\]]+)\]\]/g;
|
const pageRefRegex = /\[\[([^\]]+)\]\]/g;
|
||||||
|
|
||||||
|
@ -20,30 +21,6 @@ export type LinkObject = ObjectValue<{
|
||||||
asTemplate: boolean;
|
asTemplate: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export function extractSnippet(text: string, pos: number): string {
|
|
||||||
let prefix = "";
|
|
||||||
for (let i = pos - 1; i > 0; i--) {
|
|
||||||
if (text[i] === "\n") {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
prefix = text[i] + prefix;
|
|
||||||
if (prefix.length > 25) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let suffix = "";
|
|
||||||
for (let i = pos; i < text.length; i++) {
|
|
||||||
if (text[i] === "\n") {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
suffix += text[i];
|
|
||||||
if (suffix.length > 25) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return prefix + suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function indexLinks({ name, tree }: IndexTreeEvent) {
|
export async function indexLinks({ name, tree }: IndexTreeEvent) {
|
||||||
const links: ObjectValue<LinkObject>[] = [];
|
const links: ObjectValue<LinkObject>[] = [];
|
||||||
// [[Style Links]]
|
// [[Style Links]]
|
||||||
|
@ -62,7 +39,7 @@ export async function indexLinks({ name, tree }: IndexTreeEvent) {
|
||||||
ref: `${name}@${pos}`,
|
ref: `${name}@${pos}`,
|
||||||
tag: "link",
|
tag: "link",
|
||||||
toPage: toPage,
|
toPage: toPage,
|
||||||
snippet: extractSnippet(pageText, pos),
|
snippet: extractSnippetAroundIndex(pageText, pos),
|
||||||
pos,
|
pos,
|
||||||
page: name,
|
page: name,
|
||||||
asTemplate: false,
|
asTemplate: false,
|
||||||
|
@ -97,7 +74,7 @@ export async function indexLinks({ name, tree }: IndexTreeEvent) {
|
||||||
tag: "link",
|
tag: "link",
|
||||||
toPage: pageRefName,
|
toPage: pageRefName,
|
||||||
page: name,
|
page: name,
|
||||||
snippet: extractSnippet(pageText, pos),
|
snippet: extractSnippetAroundIndex(pageText, pos),
|
||||||
pos: pos,
|
pos: pos,
|
||||||
asTemplate: true,
|
asTemplate: true,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { assertEquals } from "$lib/test_deps.ts";
|
||||||
|
import { extractSnippetAroundIndex } from "./snippet_extractor.ts";
|
||||||
|
|
||||||
|
Deno.test("SnippetExtractor", () => {
|
||||||
|
const testText = `# Ongoing things
|
||||||
|
This is all about [[Diplomas]], and stuff like that. More stuff.
|
||||||
|
`;
|
||||||
|
assertEquals(
|
||||||
|
extractSnippetAroundIndex(testText, testText.indexOf("[[Diplomas]]")),
|
||||||
|
"This is all about [[Diplomas]], and stuff like that.",
|
||||||
|
);
|
||||||
|
|
||||||
|
const testText2 =
|
||||||
|
`A much much much much much much much much much much much longer sentence [[Diplomas]], that just keeps and keeps and keeps and keeps and keeps going.
|
||||||
|
`;
|
||||||
|
assertEquals(
|
||||||
|
extractSnippetAroundIndex(testText2, testText2.indexOf("[[Diplomas]]")),
|
||||||
|
"...much much much much much much much longer sentence [[Diplomas]], that just keeps and keeps and keeps and...",
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,61 @@
|
||||||
|
export function extractSnippetAroundIndex(
|
||||||
|
text: string,
|
||||||
|
index: number,
|
||||||
|
maxSnippetLength: number = 100,
|
||||||
|
): string {
|
||||||
|
// Use Intl.Segmenter to segment the text into sentences
|
||||||
|
const sentenceSegmenter = new Intl.Segmenter("en", {
|
||||||
|
granularity: "sentence",
|
||||||
|
});
|
||||||
|
const sentences = [...sentenceSegmenter.segment(text)].map((segment) =>
|
||||||
|
segment.segment
|
||||||
|
);
|
||||||
|
|
||||||
|
// Find the sentence that contains the index
|
||||||
|
let currentLength = 0;
|
||||||
|
let targetSentence = "";
|
||||||
|
for (const sentence of sentences) {
|
||||||
|
if (index >= currentLength && index < currentLength + sentence.length) {
|
||||||
|
targetSentence = sentence;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentLength += sentence.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the target sentence is within the maxSnippetLength, return it
|
||||||
|
if (targetSentence.length <= maxSnippetLength) {
|
||||||
|
return targetSentence.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexInSentence = index - currentLength;
|
||||||
|
|
||||||
|
// Regex for checking if a character is a word character with unicode support
|
||||||
|
const isWordCharacter = /[\p{L}\p{N}_]/u;
|
||||||
|
|
||||||
|
// Find a reasonable word boundary to start the snippet
|
||||||
|
let snippetStartIndex = Math.max(indexInSentence - maxSnippetLength / 2, 0);
|
||||||
|
while (
|
||||||
|
snippetStartIndex > 0 &&
|
||||||
|
isWordCharacter.test(targetSentence[snippetStartIndex])
|
||||||
|
) {
|
||||||
|
snippetStartIndex--;
|
||||||
|
}
|
||||||
|
snippetStartIndex = Math.max(snippetStartIndex, 0);
|
||||||
|
|
||||||
|
// Find a reasonable word boundary to end the snippet
|
||||||
|
let snippetEndIndex = Math.min(
|
||||||
|
indexInSentence + maxSnippetLength / 2,
|
||||||
|
targetSentence.length,
|
||||||
|
);
|
||||||
|
while (
|
||||||
|
snippetEndIndex < targetSentence.length &&
|
||||||
|
isWordCharacter.test(targetSentence[snippetEndIndex])
|
||||||
|
) {
|
||||||
|
snippetEndIndex++;
|
||||||
|
}
|
||||||
|
snippetEndIndex = Math.min(snippetEndIndex, targetSentence.length);
|
||||||
|
|
||||||
|
// Extract and return the refined snippet
|
||||||
|
return "..." +
|
||||||
|
targetSentence.substring(snippetStartIndex, snippetEndIndex).trim() + "...";
|
||||||
|
}
|
|
@ -66,11 +66,11 @@ export async function renderTemplateWidgets(side: "top" | "bottom"): Promise<
|
||||||
rewritePageRefs(parsedMarkdown, template.ref);
|
rewritePageRefs(parsedMarkdown, template.ref);
|
||||||
renderedTemplate = renderToText(parsedMarkdown);
|
renderedTemplate = renderToText(parsedMarkdown);
|
||||||
|
|
||||||
// console.log("Rendering template", template.ref, renderedTemplate);
|
|
||||||
templateBits.push(renderedTemplate.trim());
|
templateBits.push(renderedTemplate.trim());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const summaryText = templateBits.join("\n");
|
const summaryText = templateBits.join("\n");
|
||||||
|
// console.log("Summary:", summaryText);
|
||||||
return {
|
return {
|
||||||
markdown: summaryText,
|
markdown: summaryText,
|
||||||
buttons: [
|
buttons: [
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { parse } from "$common/markdown_parser/parse_tree.ts";
|
||||||
import { parsePageRef } from "../../plug-api/lib/page_ref.ts";
|
import { parsePageRef } from "../../plug-api/lib/page_ref.ts";
|
||||||
import { extendedMarkdownLanguage } from "$common/markdown_parser/parser.ts";
|
import { extendedMarkdownLanguage } from "$common/markdown_parser/parser.ts";
|
||||||
import { tagPrefix } from "../../plugs/index/constants.ts";
|
import { tagPrefix } from "../../plugs/index/constants.ts";
|
||||||
|
import { renderToText } from "$sb/lib/tree.ts";
|
||||||
|
|
||||||
const activeWidgets = new Set<MarkdownWidget>();
|
const activeWidgets = new Set<MarkdownWidget>();
|
||||||
|
|
||||||
|
@ -75,6 +76,23 @@ export class MarkdownWidget extends WidgetType {
|
||||||
this.client.currentPage,
|
this.client.currentPage,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
const trimmedMarkdown = renderToText(mdTree).trim();
|
||||||
|
|
||||||
|
if (!trimmedMarkdown) {
|
||||||
|
// Net empty result after expansion
|
||||||
|
div.innerHTML = "";
|
||||||
|
this.client.setWidgetCache(
|
||||||
|
this.cacheKey,
|
||||||
|
{ height: div.clientHeight, html: "" },
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the markdown again after trimming
|
||||||
|
mdTree = parse(
|
||||||
|
extendedMarkdownLanguage,
|
||||||
|
trimmedMarkdown,
|
||||||
|
);
|
||||||
|
|
||||||
const html = renderMarkdownToHtml(mdTree, {
|
const html = renderMarkdownToHtml(mdTree, {
|
||||||
// Annotate every element with its position so we can use it to put
|
// Annotate every element with its position so we can use it to put
|
||||||
|
@ -92,7 +110,6 @@ export class MarkdownWidget extends WidgetType {
|
||||||
},
|
},
|
||||||
preserveAttributes: true,
|
preserveAttributes: true,
|
||||||
});
|
});
|
||||||
// console.log("Got html", html);
|
|
||||||
|
|
||||||
if (cachedHtml === html) {
|
if (cachedHtml === html) {
|
||||||
// HTML still same as in cache, no need to re-render
|
// HTML still same as in cache, no need to re-render
|
||||||
|
|
|
@ -7,7 +7,7 @@ hooks.bottom.where: 'true'
|
||||||
{{#if @linkedMentions}}
|
{{#if @linkedMentions}}
|
||||||
# Linked Mentions
|
# Linked Mentions
|
||||||
{{#each @linkedMentions}}
|
{{#each @linkedMentions}}
|
||||||
* [[{{ref}}]]: `{{snippet}}`
|
* [[{{ref}}]]: “{{snippet}}”
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
|
Loading…
Reference in New Issue