Files
edgeever/packages/shared/src/sharing.ts
T
tianma-if e7eab6e187 feat(web): add Zhuque Fangsong and LXGW Neo ZhiSong
Note text and the interface can use a fangsong and a screen song. Both built-in faces can be published with a shared note.
2026-09-23 22:06:45 +08:00

89 lines
2.9 KiB
TypeScript

import type { TiptapDoc, TiptapNode, TiptapTextNode } from "./content";
import { parseMemoLinkHref } from "./note-links";
export type MemoShare = {
memoId: string;
token: string;
createdAt: string;
updatedAt: string;
passwordProtected: boolean;
/** Plaintext is returned only immediately after the owner enables or regenerates a password. */
password?: string;
};
export const PUBLISHED_NOTE_BODY_FONTS = [
"wenkai",
"wenkai-screen",
"zhuque",
"source-han-serif",
"neo-zhi-song",
"source-han-sans",
"source-serif",
] as const;
export type PublishedNoteBodyFont = (typeof PUBLISHED_NOTE_BODY_FONTS)[number];
export const parsePublishedNoteBodyFont = (value: unknown): PublishedNoteBodyFont | null =>
typeof value === "string" && (PUBLISHED_NOTE_BODY_FONTS as readonly string[]).includes(value)
? value as PublishedNoteBodyFont
: null;
export type PublicMemoShare = {
title: string | null;
contentJson: TiptapDoc;
contentMarkdown: string;
tags: string[];
updatedAt: string;
memoShareTokens: Record<string, string>;
/** Built-in face chosen by the author. Null keeps the system font and downloads nothing. */
bodyFont: PublishedNoteBodyFont | null;
};
const RESOURCE_URL_PATTERN = /^\/api\/v1\/resources\/([^/?#]+)\/blob(?:[?#].*)?$/;
const rewriteAttributeValue = (
value: unknown,
token: string,
memoShareTokens: Readonly<Record<string, string>>,
) => {
if (typeof value !== "string") return value;
const match = value.match(RESOURCE_URL_PATTERN);
if (match) {
return `/api/public/shares/${encodeURIComponent(token)}/resources/${encodeURIComponent(match[1])}/blob`;
}
const memoId = parseMemoLinkHref(value);
const shareToken = memoId ? memoShareTokens[memoId] : null;
return shareToken ? `/share/${encodeURIComponent(shareToken)}` : value;
};
/** Rewrites EdgeEver-owned resources and publicly shared memo links for anonymous viewers. */
export const rewriteMemoResourcesForShare = (
doc: TiptapDoc,
token: string,
memoShareTokens: Readonly<Record<string, string>> = {},
): TiptapDoc => {
const visit = (node: TiptapNode | TiptapTextNode): TiptapNode | TiptapTextNode => {
const attrs = "attrs" in node && node.attrs
? Object.fromEntries(Object.entries(node.attrs).map(([key, value]) => [key, rewriteAttributeValue(value, token, memoShareTokens)]))
: undefined;
const marks = "marks" in node && node.marks
? node.marks.map((mark) => ({
...mark,
attrs: mark.attrs
? Object.fromEntries(Object.entries(mark.attrs).map(([key, value]) => [key, rewriteAttributeValue(value, token, memoShareTokens)]))
: undefined,
}))
: undefined;
return {
...node,
...(attrs ? { attrs } : {}),
...(marks ? { marks } : {}),
...("content" in node && node.content ? { content: node.content.map(visit) } : {}),
} as TiptapNode | TiptapTextNode;
};
return visit(doc) as TiptapDoc;
};