Emdash source with visual editor image upload fix
Fixes: 1. media.ts: wrap placeholder generation in try-catch 2. toolbar.ts: check r.ok, display error message in popover
This commit is contained in:
66
demos/postgres/src/utils/reading-time.ts
Normal file
66
demos/postgres/src/utils/reading-time.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import type { PortableTextBlock } from "emdash";
|
||||
|
||||
const WORDS_PER_MINUTE = 200;
|
||||
const CJK_CHARACTERS_PER_MINUTE = 500;
|
||||
const WHITESPACE_REGEX = /\s+/;
|
||||
const CJK_CHARACTER_REGEX =
|
||||
/\p{Script=Han}|\p{Script=Hangul}|\p{Script=Hiragana}|\p{Script=Katakana}/gu;
|
||||
|
||||
type PortableTextSpan = {
|
||||
_type: string;
|
||||
text?: string;
|
||||
};
|
||||
|
||||
type PortableTextTextBlock = PortableTextBlock & {
|
||||
_type: "block";
|
||||
children: PortableTextSpan[];
|
||||
};
|
||||
|
||||
function isTextBlock(block: PortableTextBlock): block is PortableTextTextBlock {
|
||||
return block._type === "block" && Array.isArray(block.children);
|
||||
}
|
||||
|
||||
function countWords(text: string): number {
|
||||
return text.split(WHITESPACE_REGEX).filter(Boolean).length;
|
||||
}
|
||||
|
||||
function countCjkCharacters(text: string): number {
|
||||
return text.match(CJK_CHARACTER_REGEX)?.length ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract plain text from Portable Text blocks
|
||||
*/
|
||||
export function extractText(blocks: PortableTextBlock[] | undefined): string {
|
||||
if (!blocks || !Array.isArray(blocks)) return "";
|
||||
|
||||
return blocks
|
||||
.filter(isTextBlock)
|
||||
.map((block) =>
|
||||
block.children
|
||||
.filter((child) => child._type === "span" && typeof child.text === "string")
|
||||
.map((span) => span.text)
|
||||
.join(""),
|
||||
)
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate reading time in minutes from Portable Text content
|
||||
*/
|
||||
export function getReadingTime(content: PortableTextBlock[] | undefined): number {
|
||||
const text = extractText(content);
|
||||
const cjkCharacterCount = countCjkCharacters(text);
|
||||
const wordCount = countWords(text.replace(CJK_CHARACTER_REGEX, " "));
|
||||
const minutes = Math.ceil(
|
||||
wordCount / WORDS_PER_MINUTE + cjkCharacterCount / CJK_CHARACTERS_PER_MINUTE,
|
||||
);
|
||||
return Math.max(1, minutes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format reading time for display
|
||||
*/
|
||||
export function formatReadingTime(minutes: number): string {
|
||||
return `${minutes} min read`;
|
||||
}
|
||||
Reference in New Issue
Block a user