Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions src/core/text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,98 @@ export function formatLineRange(
lines: result,
};
}

const kLastPunctuationRegex = /([\S\s]*)[\.\?\!]/;
function trimSentence(text: string) {
const match = text.match(kLastPunctuationRegex);
if (match) {
return {
text: match[0],
trimmed: true,
};
} else {
return {
text,
trimmed: false,
};
}
}

function trimLength(text: string, length: number) {
if (text.length < length) {
return {
text,
trimmed: false,
};
} else {
return {
text: text.substring(0, length),
trimmed: true,
};
}
}

function trimSpace(text: string) {
const lastSpace = text.lastIndexOf(" ");
if (lastSpace > 0) {
return {
text: text.substring(0, lastSpace),
trimmed: true,
};
} else {
return {
text,
trimmed: false,
};
}
}

export function truncateText(
text: string,
length: number,
breakAt: "space" | "punctuation",
) {
const trimEnd = (text: string) => {
if ([",", "/", ":"].includes(text.charAt(text.length - 1))) {
return text.substring(0, text.length - 1);
} else {
return text;
}
};

const trimAtSpace = (text: string) => {
console.log(text);
const spaceResult = trimSpace(
text.substring(0, text.length - 1),
);
console.log(spaceResult.text);
return trimEnd(spaceResult.text) + "…";
};

const trimPunc = (text: string) => {
const puncResult = trimSentence(text);
if (puncResult.trimmed) {
return puncResult.text;
} else {
return trimAtSpace(puncResult.text);
}
};

const lengthResult = trimLength(text, length);

if (lengthResult.trimmed) {
// This was shortened
if (breakAt === "punctuation") {
return trimPunc(lengthResult.text);
} else {
return trimAtSpace(lengthResult.text);
}
} else {
// This wasn't shortened
if (breakAt === "punctuation") {
return trimPunc(lengthResult.text);
} else {
return trimEnd(lengthResult.text);
}
}
}
27 changes: 8 additions & 19 deletions src/project/types/website/listing/website-listing-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
import { resourcePath } from "../../../../core/resources.ts";
import { localizedString } from "../../../../config/localization.ts";
import { formatDate, parsePandocDate } from "../../../../core/date.ts";
import { truncateText } from "../../../../core/text.ts";

export const kDateFormat = "date-format";

Expand Down Expand Up @@ -111,7 +112,11 @@ export function templateMarkdownHandler(
const maxDescLength = listing[kMaxDescLength] as number ||
-1;
if (maxDescLength > 0) {
record.description = truncateText(item.description, maxDescLength);
record.description = truncateText(
item.description,
maxDescLength,
"space",
);
}
}

Expand Down Expand Up @@ -198,14 +203,14 @@ export function templateMarkdownHandler(
if (content) {
content.appendChild(listingEl);
} else {
// Custom page layout doesn't have a main.content, so
// Custom page layout doesn't have a main.content, so
// just use the quarto-content div directly
const customContent = doc.querySelector("#quarto-content");
if (customContent) {
customContent.appendChild(listingEl);
} else {
// Couldn't find anywhere to put the listing el, just
// stick at the bottom of the body
// stick at the bottom of the body
doc.body.appendChild(listingEl);
}
}
Expand Down Expand Up @@ -513,19 +518,3 @@ export function templateJsScript(
`;
return jsScript;
}

function truncateText(text: string, length: number) {
if (text.length < length) {
return text;
} else {
// Since we'll insert elips, trim an extra space
const clipLength = length - 1;
const clipped = text.substring(0, clipLength);
const lastSpace = clipped.lastIndexOf(" ");
if (lastSpace > 0) {
return clipped.substring(0, lastSpace) + "…";
} else {
return clipped + "…";
}
}
}
13 changes: 13 additions & 0 deletions src/project/types/website/util/discover-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ const kMdPreviewClassRegex = RegExp(kPreviewClassPattern);
const kMdNamedImageRegex = RegExp(kMdNamedImagePattern);
const kMarkdownImg = /!\[[^\]]*\]\((.*?)(?:\".*\")?\)(?:\{(?:[^\|]*)\})?/;

export function findDescription(doc: Document): string | undefined {
const paras = doc.querySelectorAll(
"main.content > p, main.content > section > p",
);
for (const para of paras) {
const paraEl = para as Element;
if (paraEl.innerText) {
return paraEl.innerText;
}
}
return undefined;
}

export function findPreviewImg(
doc: Document,
): string | undefined {
Expand Down
34 changes: 32 additions & 2 deletions src/project/types/website/website-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import {
createMarkdownPipeline,
MarkdownPipeline,
} from "./website-pipeline-md.ts";
import { findPreviewImg } from "./util/discover-meta.ts";
import { findDescription, findPreviewImg } from "./util/discover-meta.ts";
import { isAbsoluteRef } from "../../../core/http.ts";
import {
kHtmlEmptyPostProcessResult,
Expand All @@ -45,6 +45,7 @@ import { HtmlPostProcessResult } from "../../../command/render/types.ts";
import { imageSize } from "../../../core/image.ts";
import { writeMetaTag } from "../../../format/html/format-html-shared.ts";
import { joinUrl } from "../../../core/url.ts";
import { truncateText } from "../../../core/text.ts";

const kCard = "card";

Expand All @@ -53,6 +54,7 @@ interface SocialMetadataProvider {
prefix: string;
metadata: Metadata;
filter?: (key: string) => string;
resolveValue?: (key: string, value: string) => string;
resolveDefaults?: (finalMetadata: Metadata) => void;
}

Expand Down Expand Up @@ -101,6 +103,14 @@ export function metadataHtmlPostProcessor(
}
return key;
},
resolveValue: (key: string, value: string) => {
// Limit to 300 chars for Open Graph
if ([kDescription].includes(key)) {
return truncateText(value, 200, "punctuation");
}

return value;
},
};

// The twitter card provider
Expand All @@ -122,6 +132,14 @@ export function metadataHtmlPostProcessor(

return key;
},
resolveValue: (key: string, value: string) => {
// Limit to 200 chars for Twitter
if ([kDescription].includes(key)) {
return truncateText(value, 200, "punctuation");
}

return value;
},
resolveDefaults: (finalMetadata: Metadata) => {
if (finalMetadata[kCardStyle] === undefined) {
finalMetadata[kCardStyle] = finalMetadata[kImage]
Expand Down Expand Up @@ -156,6 +174,11 @@ export function metadataHtmlPostProcessor(
metadata[kImage] = findPreviewImg(doc);
}

// cook up a description if one is not provided
if (metadata[kDescription] === undefined) {
metadata[kDescription] = findDescription(doc);
}

// Convert image to absolute href and add height and width
resolveImageMetadata(source, project, format, metadata);

Expand All @@ -167,11 +190,18 @@ export function metadataHtmlPostProcessor(
// Append the metadata
Object.keys(metadata).forEach((key) => {
if (metadata[key] !== undefined) {
// Resolve the value
const data = metadata[key] as string;
const value = provider.resolveValue
? provider.resolveValue(key, data)
: data;

// Filter the key
if (provider.filter) {
key = provider.filter(key);
}
writeMetaTag(`${provider.prefix}:${key}`, data, doc);

writeMetaTag(`${provider.prefix}:${key}`, value, doc);
}
});
});
Expand Down