@meowdown/core 0.69.0 → 0.70.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +50 -62
- package/dist/index.js +197 -202
- package/dist/style.css +454 -20
- package/package.json +8 -2
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,9 @@ import { EditorView } from "@prosekit/pm/view";
|
|
|
8
8
|
import { EditorNode, Mark, ProseMirrorNode } from "@prosekit/pm/model";
|
|
9
9
|
import { ListAttrs } from "@prosekit/extensions/list";
|
|
10
10
|
import { HorizontalRuleExtension } from "@prosekit/extensions/horizontal-rule";
|
|
11
|
+
import { Resolver } from "@post-embed/elements/x";
|
|
11
12
|
import { render } from "katex";
|
|
13
|
+
import { XPost, YouTubeVideo } from "@post-embed/types";
|
|
12
14
|
import { VirtualElement } from "@floating-ui/dom";
|
|
13
15
|
//#region src/converters/check-roundtrip.d.ts
|
|
14
16
|
/**
|
|
@@ -202,6 +204,10 @@ interface MdImageAttrs {
|
|
|
202
204
|
* Display height in CSS pixels from the trailing comment, or `null`.
|
|
203
205
|
*/
|
|
204
206
|
height: number | null;
|
|
207
|
+
/**
|
|
208
|
+
* The saved post-embed snapshot from the trailing comment, or `null`.
|
|
209
|
+
*/
|
|
210
|
+
snapshot: object | null;
|
|
205
211
|
/**
|
|
206
212
|
* `wikiEmbed` when the source is `![[target]]`; otherwise `null`.
|
|
207
213
|
*/
|
|
@@ -1034,62 +1040,6 @@ export declare const codeBlockLanguages: ReadonlyArray<LanguageItem>;
|
|
|
1034
1040
|
*/
|
|
1035
1041
|
export declare function defineEmbedPaste(): PlainExtension;
|
|
1036
1042
|
//#endregion
|
|
1037
|
-
//#region src/extensions/embed-types.d.ts
|
|
1038
|
-
/**
|
|
1039
|
-
* A framework-agnostic description of an embed iframe. Both the editor's DOM
|
|
1040
|
-
* mark view and the static React renderer build their own `<iframe>` from it, so
|
|
1041
|
-
* the URL/attribute logic lives in one place while each side renders natively.
|
|
1042
|
-
*/
|
|
1043
|
-
interface EmbedDescriptor {
|
|
1044
|
-
readonly kind: 'tweet' | 'youtube';
|
|
1045
|
-
/**
|
|
1046
|
-
* Stable identity for the widget, unique per rendered embed (e.g. the video or
|
|
1047
|
-
* tweet id). Keeps ProseMirror from rebuilding the iframe on every edit, and
|
|
1048
|
-
* keys the React element, so the embed never reloads.
|
|
1049
|
-
*/
|
|
1050
|
-
readonly key: string;
|
|
1051
|
-
/**
|
|
1052
|
-
* The iframe `src`.
|
|
1053
|
-
*/
|
|
1054
|
-
readonly src: string;
|
|
1055
|
-
/**
|
|
1056
|
-
* The iframe `title`.
|
|
1057
|
-
*/
|
|
1058
|
-
readonly title: string;
|
|
1059
|
-
/**
|
|
1060
|
-
* The iframe `class`, e.g. `md-embed md-embed-tweet`.
|
|
1061
|
-
*/
|
|
1062
|
-
readonly className: string;
|
|
1063
|
-
/**
|
|
1064
|
-
* The iframe `data-testid`.
|
|
1065
|
-
*/
|
|
1066
|
-
readonly testid: string;
|
|
1067
|
-
/**
|
|
1068
|
-
* The iframe `allow` policy, when the embed needs one.
|
|
1069
|
-
*/
|
|
1070
|
-
readonly allow?: string;
|
|
1071
|
-
/**
|
|
1072
|
-
* Whether the iframe is fullscreen-capable.
|
|
1073
|
-
*/
|
|
1074
|
-
readonly allowFullscreen?: boolean;
|
|
1075
|
-
}
|
|
1076
|
-
//#endregion
|
|
1077
|
-
//#region src/extensions/tweet.d.ts
|
|
1078
|
-
/**
|
|
1079
|
-
* `Tweet.html` reports its rendered height via `postMessage`; size the iframe to
|
|
1080
|
-
* fit and pass each reported height to `onHeight`. Returns a cleanup that
|
|
1081
|
-
* removes the listener. The cleanup also runs once the iframe leaves the DOM, so
|
|
1082
|
-
* the editor's DOM mark view (which has no destroy hook) is covered, while a
|
|
1083
|
-
* React caller can call it on unmount.
|
|
1084
|
-
*/
|
|
1085
|
-
export declare function listenForTweetHeight(iframe: HTMLIFrameElement, onHeight?: (height: number) => void): () => void;
|
|
1086
|
-
//#endregion
|
|
1087
|
-
//#region src/extensions/embed.d.ts
|
|
1088
|
-
/**
|
|
1089
|
-
* Detect a tweet/YouTube embed in an image `src`, or `undefined` for a plain image.
|
|
1090
|
-
*/
|
|
1091
|
-
export declare function matchEmbed(src: string): EmbedDescriptor | undefined;
|
|
1092
|
-
//#endregion
|
|
1093
1043
|
//#region src/extensions/exit-boundary.d.ts
|
|
1094
1044
|
/**
|
|
1095
1045
|
* Payload for {@link ExitBoundaryHandler}.
|
|
@@ -1363,6 +1313,39 @@ interface FollowLinkHandlers {
|
|
|
1363
1313
|
*/
|
|
1364
1314
|
export declare function defineFollowLinkHandler(handlers: FollowLinkHandlers): PlainExtension;
|
|
1365
1315
|
//#endregion
|
|
1316
|
+
//#region src/extensions/post-embed.d.ts
|
|
1317
|
+
type XPostResolver = Resolver<XPost>;
|
|
1318
|
+
type YouTubeVideoResolver = Resolver<YouTubeVideo>;
|
|
1319
|
+
type PostEmbedKind = 'x-post' | 'youtube-video';
|
|
1320
|
+
/**
|
|
1321
|
+
* The post id of an X status URL, or `undefined` for any other `src`.
|
|
1322
|
+
*/
|
|
1323
|
+
export declare function parseXPostId(src: string): string | undefined;
|
|
1324
|
+
/**
|
|
1325
|
+
* The post-embed card an image `src` renders as, or `undefined` for a plain image.
|
|
1326
|
+
*/
|
|
1327
|
+
export declare function matchPostEmbed(src: string): PostEmbedKind | undefined;
|
|
1328
|
+
export declare const defaultResolveXPost: XPostResolver;
|
|
1329
|
+
export declare const defaultResolveYouTubeVideo: YouTubeVideoResolver;
|
|
1330
|
+
/**
|
|
1331
|
+
* The `snapshot` field of an image's magic comment: the card kind and the
|
|
1332
|
+
* data post-embed renders. The kind is stored explicitly, so a saved snapshot
|
|
1333
|
+
* is validated once, against the schema it names.
|
|
1334
|
+
*/
|
|
1335
|
+
type PostEmbedSnapshot = {
|
|
1336
|
+
kind: 'x-post';
|
|
1337
|
+
data: XPost;
|
|
1338
|
+
} | {
|
|
1339
|
+
kind: 'youtube-video';
|
|
1340
|
+
data: YouTubeVideo;
|
|
1341
|
+
};
|
|
1342
|
+
/**
|
|
1343
|
+
* The snapshot a card renders from a saved JSON object, or `undefined` when
|
|
1344
|
+
* the object does not validate. Unknown fields are dropped, so a persisted
|
|
1345
|
+
* snapshot is exactly what the card needs.
|
|
1346
|
+
*/
|
|
1347
|
+
export declare function parsePostEmbedSnapshot(value: unknown): PostEmbedSnapshot | undefined;
|
|
1348
|
+
//#endregion
|
|
1366
1349
|
//#region src/extensions/image.d.ts
|
|
1367
1350
|
type ImageUrlResolver = (src: string) => string | undefined;
|
|
1368
1351
|
/**
|
|
@@ -1375,12 +1358,17 @@ interface ImageOptions {
|
|
|
1375
1358
|
*/
|
|
1376
1359
|
resolveImageUrl?: ImageUrlResolver;
|
|
1377
1360
|
/**
|
|
1378
|
-
*
|
|
1379
|
-
*
|
|
1380
|
-
*
|
|
1381
|
-
|
|
1361
|
+
* Resolve the data behind an X post URL, rendered as a `post-embed-x-post`
|
|
1362
|
+
* card. Defaults to `defaultResolveXPost`, which fetches through
|
|
1363
|
+
* react-tweet's hosted proxy.
|
|
1364
|
+
*/
|
|
1365
|
+
resolveXPost?: XPostResolver;
|
|
1366
|
+
/**
|
|
1367
|
+
* Resolve the data behind a YouTube video URL, rendered as a
|
|
1368
|
+
* `post-embed-youtube-video` card. Defaults to `defaultResolveYouTubeVideo`,
|
|
1369
|
+
* which reads YouTube's oEmbed endpoint.
|
|
1382
1370
|
*/
|
|
1383
|
-
|
|
1371
|
+
resolveYouTubeVideo?: YouTubeVideoResolver;
|
|
1384
1372
|
}
|
|
1385
1373
|
/**
|
|
1386
1374
|
* Show an `src` as-is when it is an http(s) URL, otherwise skip rendering it.
|
|
@@ -1641,4 +1629,4 @@ export declare function getSelectedText(state: EditorState): string;
|
|
|
1641
1629
|
*/
|
|
1642
1630
|
export declare function getVirtualElementFromRange(view: EditorView, range: PositionRange): VirtualElement;
|
|
1643
1631
|
//#endregion
|
|
1644
|
-
export { type AcceptPendingReplacementOptions, type CheckRoundTripOptions, type CodeBlockAttrs, type CodeBlockFenceStyle, type CodeToken, type DocToMarkdownOptions, type EditorExtension, type EditorExtensionOptions, type
|
|
1632
|
+
export { type AcceptPendingReplacementOptions, type CheckRoundTripOptions, type CodeBlockAttrs, type CodeBlockFenceStyle, type CodeToken, type DocToMarkdownOptions, type EditorExtension, type EditorExtensionOptions, type ExitBoundaryHandler, type ExitBoundaryOptions, type FileClickHandler, type FileClickPayload, type FileInfo, type FileInfoResolver, type FileLinkOptions, type FileLinkPayload, type FileLinkResolver, type FilePasteHandler, type FilePasteOptions, type FileSaveErrorHandler, type FileViewOptions, type FollowLinkHandlers, type ImageClickHandler, type ImageClickPayload, type ImageOptions, type InlineMarkContext, type InlineMarkOptions, type KaTeXRender, type LanguageItem, type LinkAttrs, type LinkClickHandler, type LinkClickPayload, type LinkCopyHandler, type LinkCopyPayload, type LinkEditHandler, type LinkEditOptions, type LinkHoverHandler, type LinkHoverOptions, type LinkPreview, type LinkPreviewResolver, type LinkUnit, type ListMarker, type MarkChunk, type MarkMode, type MarkName, type MarkdownToDocOptions, type MdFileAttrs, type MdImageAttrs, type MdLinkTextAttrs, type MdMathAttrs, type MdWikilinkAttrs, type MeowdownCodeBlockAttrs, type MeowdownHTMLCommentAttrs, type MeowdownListAttrs, type MeowdownTableCellAttrs, type NodeName, type ParsedWikiEmbed, type PendingReplacement, type PendingReplacementEvent, type PendingReplacementHandler, type PendingReplacementMode, type PendingReplacementOutcome, type PlaceholderOptions, type PositionRange, type PostEmbedKind, type PostEmbedSnapshot, Priority, type ReferenceDefinition, type ReferenceDefinitionIndex, type ReferenceDefinitions, type RoundTripFidelity, type SearchStatus, type SearchStatusHandler, type StartPendingReplacementOptions, type TableColumnAlign, type TagClickHandler, type TagClickPayload, type TypedEditor, type TypedMarkBuilders, type VirtualElement, type WikiEmbedOptions, type WikiEmbedResolution, type WikiEmbedResolver, type WikilinkClickHandler, type WikilinkClickPayload, type WikilinkHoverHandler, type WikilinkHoverHit, type WikilinkOptions, type WikilinkPayload, type WikilinkResolution, type WikilinkResolver, type XPostResolver, type YouTubeVideoResolver, defineCodeBlockPreviewPlugin, definePlaceholder, defineReadonly, defineSearchStatusHandler, getSearchStatus, isCodeBlockPreviewHiddenDecoration, withPriority };
|
package/dist/index.js
CHANGED
|
@@ -40,6 +40,12 @@ import { normalizeIdentifier } from "micromark-util-normalize-identifier";
|
|
|
40
40
|
import { createListRenderingPlugin, createSafariInputMethodWorkaroundPlugin, createToggleCollapsedCommand, defaultAttributesGetter, enterCommand, findCheckboxInListItem, handleListMarkerMouseDown, joinListElements, listToDOM, unwrapListSlice, wrappingListInputRule } from "prosemirror-flat-list";
|
|
41
41
|
import { defineTableCellSpec, defineTableCommands, defineTableDropIndicator, defineTableEditingPlugin, defineTableHeaderCellSpec, defineTableRowSpec, defineTableSpec, deleteTable, isCellSelection } from "@prosekit/extensions/table";
|
|
42
42
|
import { closeHistory } from "@prosekit/pm/history";
|
|
43
|
+
import { fromSyndication } from "@post-embed/exporter/x/syndication";
|
|
44
|
+
import { XPostSchema, YouTubeVideoSchema } from "@post-embed/schema";
|
|
45
|
+
import { createLRU } from "lru.min";
|
|
46
|
+
import * as v from "valibot";
|
|
47
|
+
import { registerXPost } from "@post-embed/elements/x";
|
|
48
|
+
import { registerYouTubeVideo } from "@post-embed/elements/youtube";
|
|
43
49
|
import { registerResizableHandleElement, registerResizableRootElement } from "@prosekit/web/resizable";
|
|
44
50
|
import { InputRule } from "@prosekit/pm/inputrules";
|
|
45
51
|
|
|
@@ -2844,8 +2850,8 @@ const emptyBatchSetMarkStep = new BatchSetMarkStep([]);
|
|
|
2844
2850
|
|
|
2845
2851
|
//#endregion
|
|
2846
2852
|
//#region src/extensions/magic-comment.ts
|
|
2847
|
-
const MAGIC_COMMENT_RE = /^<!--\s*(\{[
|
|
2848
|
-
const TRAILING_MAGIC_COMMENT_RE = /(?:<!--\s*\{[
|
|
2853
|
+
const MAGIC_COMMENT_RE = /^<!--\s*(\{[\s\S]*?\})\s*-->/;
|
|
2854
|
+
const TRAILING_MAGIC_COMMENT_RE = /(?:<!--\s*\{[\s\S]*?\}\s*-->)+$/;
|
|
2849
2855
|
/**
|
|
2850
2856
|
* Read the metadata out of a `<!-- {...} -->` comment, or `undefined` when the
|
|
2851
2857
|
* text is not a comment carrying at least one recognized field.
|
|
@@ -2863,21 +2869,28 @@ function parseMagicComment(comment) {
|
|
|
2863
2869
|
const width = toPositiveNumber(data.width);
|
|
2864
2870
|
const height = toPositiveNumber(data.height);
|
|
2865
2871
|
const noLink = data.noLink === true ? true : void 0;
|
|
2866
|
-
|
|
2872
|
+
const snapshot = isObject(data.snapshot) && !Array.isArray(data.snapshot) ? data.snapshot : void 0;
|
|
2873
|
+
if (!width && !height && !noLink && !snapshot) return;
|
|
2867
2874
|
return {
|
|
2868
2875
|
width,
|
|
2869
2876
|
height,
|
|
2870
|
-
noLink
|
|
2877
|
+
noLink,
|
|
2878
|
+
snapshot
|
|
2871
2879
|
};
|
|
2872
2880
|
}
|
|
2873
2881
|
function toPositiveNumber(value) {
|
|
2874
2882
|
if (typeof value === "number" && Number.isFinite(value) && value > 0) return Math.round(value);
|
|
2875
2883
|
}
|
|
2876
2884
|
/**
|
|
2877
|
-
* The canonical comment meowdown writes for the metadata.
|
|
2885
|
+
* The canonical comment meowdown writes for the metadata. `--` inside a JSON
|
|
2886
|
+
* string becomes `--`: the inline comment rule of `@lezer/markdown`
|
|
2887
|
+
* (CommonMark 0.30) ends a comment at any `--`, so a snapshot text holding a
|
|
2888
|
+
* double dash would otherwise cut the comment short. Outside strings JSON
|
|
2889
|
+
* only writes `-` as a number sign, never doubled, so the escape keeps the
|
|
2890
|
+
* JSON valid and `JSON.parse` restores the text.
|
|
2878
2891
|
*/
|
|
2879
2892
|
function formatMagicComment(magic) {
|
|
2880
|
-
return `<!-- ${JSON.stringify(magic)} -->`;
|
|
2893
|
+
return `<!-- ${JSON.stringify(magic).replaceAll("--", String.raw`-\u002d`)} -->`;
|
|
2881
2894
|
}
|
|
2882
2895
|
/**
|
|
2883
2896
|
* Drop the trailing run of magic comments from the source text.
|
|
@@ -3491,6 +3504,7 @@ function walkImage(node, parentMarks, text, marks, out, options, context, traili
|
|
|
3491
3504
|
const title = resolution.title;
|
|
3492
3505
|
const width = trailing?.magic.width ?? null;
|
|
3493
3506
|
const height = trailing?.magic.height ?? null;
|
|
3507
|
+
const snapshot = trailing?.magic.snapshot ?? null;
|
|
3494
3508
|
const to = trailing?.to ?? node.to;
|
|
3495
3509
|
emit(out, node.from, to, [
|
|
3496
3510
|
...parentMarks,
|
|
@@ -3501,6 +3515,7 @@ function walkImage(node, parentMarks, text, marks, out, options, context, traili
|
|
|
3501
3515
|
title,
|
|
3502
3516
|
width,
|
|
3503
3517
|
height,
|
|
3518
|
+
snapshot,
|
|
3504
3519
|
syntax: null,
|
|
3505
3520
|
wikiTarget: null
|
|
3506
3521
|
})
|
|
@@ -3574,6 +3589,7 @@ function walkWikiEmbed(node, parentMarks, text, marks, out, options) {
|
|
|
3574
3589
|
title: "",
|
|
3575
3590
|
width: embed.width,
|
|
3576
3591
|
height: embed.height,
|
|
3592
|
+
snapshot: null,
|
|
3577
3593
|
syntax: "wikiEmbed",
|
|
3578
3594
|
wikiTarget: embed.target
|
|
3579
3595
|
})
|
|
@@ -3872,6 +3888,7 @@ function defineMdImage() {
|
|
|
3872
3888
|
title: { default: "" },
|
|
3873
3889
|
width: { default: null },
|
|
3874
3890
|
height: { default: null },
|
|
3891
|
+
snapshot: { default: null },
|
|
3875
3892
|
syntax: { default: null },
|
|
3876
3893
|
wikiTarget: { default: null }
|
|
3877
3894
|
},
|
|
@@ -6896,105 +6913,38 @@ const codeBlockLanguages = languages.map((language) => ({
|
|
|
6896
6913
|
})).filter((language) => !excludeLanguages.has(language.label)).concat(extraLanguages).sort((a, b) => a.label.localeCompare(b.label));
|
|
6897
6914
|
|
|
6898
6915
|
//#endregion
|
|
6899
|
-
//#region src/
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
function prefersDarkColorScheme() {
|
|
6905
|
-
if (typeof window === "undefined") return false;
|
|
6906
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
6916
|
+
//#region src/extensions/paste.ts
|
|
6917
|
+
function getPastedText(event, slice) {
|
|
6918
|
+
const fromClipboard = event.clipboardData?.getData("text/plain");
|
|
6919
|
+
if (fromClipboard) return fromClipboard;
|
|
6920
|
+
return slice.content.textBetween(0, slice.content.size, "\n");
|
|
6907
6921
|
}
|
|
6908
6922
|
|
|
6909
6923
|
//#endregion
|
|
6910
|
-
//#region src/extensions/
|
|
6911
|
-
const
|
|
6924
|
+
//#region src/extensions/post-embed.ts
|
|
6925
|
+
const X_POST_HOSTS = /^(?:www\.|mobile\.)?(?:twitter\.com|x\.com)$/i;
|
|
6912
6926
|
const STATUS_ID = /\/status(?:es)?\/(\d+)/;
|
|
6913
|
-
|
|
6914
|
-
|
|
6927
|
+
const YOUTUBE_HOSTS = /^(?:www\.|m\.)?(?:youtube\.com|youtube-nocookie\.com)$/i;
|
|
6928
|
+
const YOUTU_BE_HOST = /^(?:www\.)?youtu\.be$/i;
|
|
6929
|
+
const VIDEO_ID = /^[\w-]{11}$/;
|
|
6930
|
+
function parseURL(src) {
|
|
6915
6931
|
try {
|
|
6916
|
-
|
|
6932
|
+
return new URL(src);
|
|
6917
6933
|
} catch {
|
|
6918
6934
|
return;
|
|
6919
6935
|
}
|
|
6920
|
-
if (!TWEET_HOSTS.test(url.hostname)) return void 0;
|
|
6921
|
-
return STATUS_ID.exec(url.pathname)?.[1];
|
|
6922
6936
|
}
|
|
6923
|
-
const matchTweet = (src) => {
|
|
6924
|
-
const tweetId = parseTweetId(src);
|
|
6925
|
-
if (!tweetId) return;
|
|
6926
|
-
const theme = prefersDarkColorScheme() ? "dark" : "light";
|
|
6927
|
-
return {
|
|
6928
|
-
kind: "tweet",
|
|
6929
|
-
key: `tweet:${tweetId}`,
|
|
6930
|
-
src: `https://platform.twitter.com/embed/Tweet.html?id=${tweetId}&theme=${theme}&dnt=true`,
|
|
6931
|
-
title: "Tweet",
|
|
6932
|
-
className: "md-embed md-embed-tweet",
|
|
6933
|
-
testid: "tweet-embed"
|
|
6934
|
-
};
|
|
6935
|
-
};
|
|
6936
6937
|
/**
|
|
6937
|
-
*
|
|
6938
|
-
* fit and pass each reported height to `onHeight`. Returns a cleanup that
|
|
6939
|
-
* removes the listener. The cleanup also runs once the iframe leaves the DOM, so
|
|
6940
|
-
* the editor's DOM mark view (which has no destroy hook) is covered, while a
|
|
6941
|
-
* React caller can call it on unmount.
|
|
6938
|
+
* The post id of an X status URL, or `undefined` for any other `src`.
|
|
6942
6939
|
*/
|
|
6943
|
-
function
|
|
6944
|
-
const
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
const height = event.data?.["twttr.embed"]?.params?.[0]?.height;
|
|
6948
|
-
if (typeof height === "number" && height > 0) {
|
|
6949
|
-
applyTweetHeight(iframe, height);
|
|
6950
|
-
onHeight?.(height);
|
|
6951
|
-
}
|
|
6952
|
-
} catch (error) {
|
|
6953
|
-
console.warn("[meowdown] failed to parse tweet resize message:", error);
|
|
6954
|
-
}
|
|
6955
|
-
};
|
|
6956
|
-
window.addEventListener("message", onMessage);
|
|
6957
|
-
let cleaned = false;
|
|
6958
|
-
const cleanup = () => {
|
|
6959
|
-
if (cleaned) return;
|
|
6960
|
-
cleaned = true;
|
|
6961
|
-
window.removeEventListener("message", onMessage);
|
|
6962
|
-
observer.disconnect();
|
|
6963
|
-
};
|
|
6964
|
-
const observer = new MutationObserver(() => {
|
|
6965
|
-
if (!iframe.isConnected) cleanup();
|
|
6966
|
-
});
|
|
6967
|
-
observer.observe(document.body, {
|
|
6968
|
-
childList: true,
|
|
6969
|
-
subtree: true
|
|
6970
|
-
});
|
|
6971
|
-
return cleanup;
|
|
6972
|
-
}
|
|
6973
|
-
/**
|
|
6974
|
-
* Size a tweet iframe.
|
|
6975
|
-
*/
|
|
6976
|
-
function applyTweetHeight(iframe, height) {
|
|
6977
|
-
if (height && height > 0) {
|
|
6978
|
-
iframe.style.height = `${height}px`;
|
|
6979
|
-
iframe.dataset.sized = "";
|
|
6980
|
-
}
|
|
6940
|
+
function parseXPostId(src) {
|
|
6941
|
+
const url = parseURL(src);
|
|
6942
|
+
if (!url || !X_POST_HOSTS.test(url.hostname)) return void 0;
|
|
6943
|
+
return STATUS_ID.exec(url.pathname)?.[1];
|
|
6981
6944
|
}
|
|
6982
|
-
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
const YOUTUBE_HOSTS = /^(?:www\.|m\.)?(?:youtube\.com|youtube-nocookie\.com)$/i;
|
|
6986
|
-
const YOUTU_BE_HOST = /^(?:www\.)?youtu\.be$/i;
|
|
6987
|
-
const VIDEO_ID = /^[\w-]{11}$/;
|
|
6988
|
-
/**
|
|
6989
|
-
* Extract `{ videoId, startSeconds? }` from any watch/shorts/embed/live/`youtu.be` URL.
|
|
6990
|
-
*/
|
|
6991
|
-
function parseYouTube(src) {
|
|
6992
|
-
let url;
|
|
6993
|
-
try {
|
|
6994
|
-
url = new URL(src);
|
|
6995
|
-
} catch {
|
|
6996
|
-
return;
|
|
6997
|
-
}
|
|
6945
|
+
function isYouTubeVideo(src) {
|
|
6946
|
+
const url = parseURL(src);
|
|
6947
|
+
if (!url) return false;
|
|
6998
6948
|
let videoId = null;
|
|
6999
6949
|
if (YOUTU_BE_HOST.test(url.hostname)) videoId = url.pathname.slice(1);
|
|
7000
6950
|
else if (YOUTUBE_HOSTS.test(url.hostname)) {
|
|
@@ -7002,58 +6952,68 @@ function parseYouTube(src) {
|
|
|
7002
6952
|
if (url.pathname === "/watch") videoId = url.searchParams.get("v");
|
|
7003
6953
|
else if (firstSegment === "shorts" || firstSegment === "embed" || firstSegment === "live") videoId = secondSegment ?? null;
|
|
7004
6954
|
}
|
|
7005
|
-
|
|
7006
|
-
const timeParam = url.searchParams.get("start") ?? url.searchParams.get("t");
|
|
7007
|
-
const startSeconds = timeParam ? parseStartSeconds(timeParam) : void 0;
|
|
7008
|
-
return {
|
|
7009
|
-
videoId,
|
|
7010
|
-
startSeconds
|
|
7011
|
-
};
|
|
6955
|
+
return videoId !== null && VIDEO_ID.test(videoId);
|
|
7012
6956
|
}
|
|
7013
6957
|
/**
|
|
7014
|
-
*
|
|
6958
|
+
* The post-embed card an image `src` renders as, or `undefined` for a plain image.
|
|
7015
6959
|
*/
|
|
7016
|
-
function
|
|
7017
|
-
if (
|
|
7018
|
-
|
|
7019
|
-
if (!matched || !matched[1] && !matched[2] && !matched[3]) return void 0;
|
|
7020
|
-
return Number(matched[1] ?? 0) * 3600 + Number(matched[2] ?? 0) * 60 + Number(matched[3] ?? 0);
|
|
6960
|
+
function matchPostEmbed(src) {
|
|
6961
|
+
if (parseXPostId(src) !== void 0) return "x-post";
|
|
6962
|
+
if (isYouTubeVideo(src)) return "youtube-video";
|
|
7021
6963
|
}
|
|
7022
|
-
const matchYouTube = (src) => {
|
|
7023
|
-
const parsed = parseYouTube(src);
|
|
7024
|
-
if (!parsed) return;
|
|
7025
|
-
const query = parsed.startSeconds ? `?start=${parsed.startSeconds}` : "";
|
|
7026
|
-
return {
|
|
7027
|
-
kind: "youtube",
|
|
7028
|
-
key: `youtube:${parsed.videoId}:${parsed.startSeconds ?? 0}`,
|
|
7029
|
-
src: `https://www.youtube-nocookie.com/embed/${parsed.videoId}${query}`,
|
|
7030
|
-
title: "YouTube video",
|
|
7031
|
-
className: "md-embed md-embed-youtube",
|
|
7032
|
-
testid: "youtube-embed",
|
|
7033
|
-
allow: "accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen",
|
|
7034
|
-
allowFullscreen: true
|
|
7035
|
-
};
|
|
7036
|
-
};
|
|
7037
|
-
|
|
7038
|
-
//#endregion
|
|
7039
|
-
//#region src/extensions/embed.ts
|
|
7040
|
-
const EMBED_MATCHERS = [matchYouTube, matchTweet];
|
|
7041
6964
|
/**
|
|
7042
|
-
*
|
|
6965
|
+
* Wrap a loader in a 64-entry LRU keyed by URL. A settled URL answers
|
|
6966
|
+
* synchronously, so a revisited document renders its cards in the first
|
|
6967
|
+
* frame; the first visit returns the in-flight load.
|
|
7043
6968
|
*/
|
|
7044
|
-
function
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
if (
|
|
7048
|
-
|
|
6969
|
+
function cached(load) {
|
|
6970
|
+
const cache = createLRU({ max: 64 });
|
|
6971
|
+
return (url) => {
|
|
6972
|
+
if (cache.has(url)) return cache.get(url);
|
|
6973
|
+
const pending = load(url).then((value) => {
|
|
6974
|
+
cache.set(url, value);
|
|
6975
|
+
return value;
|
|
6976
|
+
}, (error) => {
|
|
6977
|
+
cache.delete(url);
|
|
6978
|
+
throw error;
|
|
6979
|
+
});
|
|
6980
|
+
cache.set(url, pending);
|
|
6981
|
+
return pending;
|
|
6982
|
+
};
|
|
7049
6983
|
}
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
const
|
|
7055
|
-
if (
|
|
7056
|
-
|
|
6984
|
+
const X_POST_API = "https://react-tweet.vercel.app/api/tweet/";
|
|
6985
|
+
const defaultResolveXPost = cached(async (url) => {
|
|
6986
|
+
const id = parseXPostId(url);
|
|
6987
|
+
if (id === void 0) return;
|
|
6988
|
+
const response = await fetch(X_POST_API + id);
|
|
6989
|
+
if (!response.ok) return;
|
|
6990
|
+
const json = await response.json();
|
|
6991
|
+
return fromSyndication(json.data);
|
|
6992
|
+
});
|
|
6993
|
+
const YOUTUBE_OEMBED_API = "https://www.youtube.com/oembed?format=json&url=";
|
|
6994
|
+
const defaultResolveYouTubeVideo = cached(async (url) => {
|
|
6995
|
+
const response = await fetch(YOUTUBE_OEMBED_API + encodeURIComponent(url));
|
|
6996
|
+
if (!response.ok) return;
|
|
6997
|
+
return {
|
|
6998
|
+
url,
|
|
6999
|
+
...await response.json()
|
|
7000
|
+
};
|
|
7001
|
+
});
|
|
7002
|
+
const PostEmbedSnapshotSchema = v.variant("kind", [v.object({
|
|
7003
|
+
kind: v.literal("x-post"),
|
|
7004
|
+
data: XPostSchema
|
|
7005
|
+
}), v.object({
|
|
7006
|
+
kind: v.literal("youtube-video"),
|
|
7007
|
+
data: YouTubeVideoSchema
|
|
7008
|
+
})]);
|
|
7009
|
+
/**
|
|
7010
|
+
* The snapshot a card renders from a saved JSON object, or `undefined` when
|
|
7011
|
+
* the object does not validate. Unknown fields are dropped, so a persisted
|
|
7012
|
+
* snapshot is exactly what the card needs.
|
|
7013
|
+
*/
|
|
7014
|
+
function parsePostEmbedSnapshot(value) {
|
|
7015
|
+
const result = v.safeParse(PostEmbedSnapshotSchema, value);
|
|
7016
|
+
return result.success ? result.output : void 0;
|
|
7057
7017
|
}
|
|
7058
7018
|
|
|
7059
7019
|
//#endregion
|
|
@@ -7062,7 +7022,7 @@ const embedPasteKey = new PluginKey("meowdown-embed-paste");
|
|
|
7062
7022
|
function detectEmbedUrl(text) {
|
|
7063
7023
|
const trimmed = text.trim();
|
|
7064
7024
|
if (!trimmed || /\s/.test(trimmed)) return void 0;
|
|
7065
|
-
return
|
|
7025
|
+
return matchPostEmbed(trimmed) ? trimmed : void 0;
|
|
7066
7026
|
}
|
|
7067
7027
|
function insertEmbedFromPaste(view, url) {
|
|
7068
7028
|
const { from, to } = view.state.selection;
|
|
@@ -7794,28 +7754,6 @@ function defaultResolveImageUrl(src) {
|
|
|
7794
7754
|
*/
|
|
7795
7755
|
const MAX_DISPLAY_HEIGHT = 500;
|
|
7796
7756
|
/**
|
|
7797
|
-
* Build the iframe DOM for an embed descriptor and start its height listener.
|
|
7798
|
-
* A persisted tweet height seeds the iframe before `Tweet.html` reports the
|
|
7799
|
-
* real one, so a revisited tweet keeps its space instead of shifting layout.
|
|
7800
|
-
*/
|
|
7801
|
-
function buildEmbedIframe(embed, height, onHeight) {
|
|
7802
|
-
const iframe = document.createElement("iframe");
|
|
7803
|
-
iframe.src = embed.src;
|
|
7804
|
-
iframe.title = embed.title;
|
|
7805
|
-
iframe.className = embed.className;
|
|
7806
|
-
iframe.dataset.testid = embed.testid;
|
|
7807
|
-
iframe.loading = "lazy";
|
|
7808
|
-
iframe.referrerPolicy = "strict-origin-when-cross-origin";
|
|
7809
|
-
iframe.setAttribute("frameborder", "0");
|
|
7810
|
-
if (embed.allow) iframe.allow = embed.allow;
|
|
7811
|
-
if (embed.allowFullscreen) iframe.allowFullscreen = true;
|
|
7812
|
-
if (embed.kind === "tweet") {
|
|
7813
|
-
applyTweetHeight(iframe, height);
|
|
7814
|
-
listenForTweetHeight(iframe, onHeight);
|
|
7815
|
-
}
|
|
7816
|
-
return iframe;
|
|
7817
|
-
}
|
|
7818
|
-
/**
|
|
7819
7757
|
* Write a persisted display size onto a resizable resizable root.
|
|
7820
7758
|
*/
|
|
7821
7759
|
function applySize(root, width, height) {
|
|
@@ -7883,40 +7821,59 @@ function commitImageSize(view, content, rawWidth, rawHeight) {
|
|
|
7883
7821
|
}, true);
|
|
7884
7822
|
}
|
|
7885
7823
|
/**
|
|
7886
|
-
*
|
|
7887
|
-
*
|
|
7888
|
-
* writing those back would churn the document on every open.
|
|
7824
|
+
* Persist a resized width into the trailing magic comment. A card's height is
|
|
7825
|
+
* its content's, so any persisted height is dropped rather than carried over.
|
|
7889
7826
|
*/
|
|
7890
|
-
|
|
7827
|
+
function commitEmbedWidth(view, content, rawWidth) {
|
|
7828
|
+
const pos = view.posAtDOM(content, 0);
|
|
7829
|
+
const range = getMarkRangeAt(view.state, pos, "mdImage");
|
|
7830
|
+
if (!range) return;
|
|
7831
|
+
rewriteMagicComment(view, range, {
|
|
7832
|
+
width: Math.round(rawWidth),
|
|
7833
|
+
height: void 0
|
|
7834
|
+
}, true);
|
|
7835
|
+
}
|
|
7891
7836
|
/**
|
|
7892
|
-
* Persist
|
|
7893
|
-
*
|
|
7894
|
-
*
|
|
7837
|
+
* Persist a resolved snapshot into the trailing magic comment. The whole
|
|
7838
|
+
* `<!-- ... -->` range is rewritten, not just the comment: the
|
|
7839
|
+
* transaction stays out of history, and undoing the edit that inserted the
|
|
7840
|
+
* image must delete the snapshot with it. prosemirror-history maps the
|
|
7841
|
+
* inverse deletion through this step, and a position at the end of a
|
|
7842
|
+
* replaced range maps to the end of the replacement, so the mapped deletion
|
|
7843
|
+
* covers the comment. An insertion at the range end would survive that undo
|
|
7844
|
+
* as an orphan comment rendered as plain text.
|
|
7895
7845
|
*/
|
|
7896
|
-
function
|
|
7897
|
-
if (!view.editable || !content.isConnected) return;
|
|
7846
|
+
function commitSnapshot(view, content, src, snapshot) {
|
|
7898
7847
|
const pos = view.posAtDOM(content, 0);
|
|
7899
7848
|
const range = getMarkRangeAt(view.state, pos, "mdImage");
|
|
7900
7849
|
if (!range) return;
|
|
7901
|
-
|
|
7902
|
-
|
|
7903
|
-
|
|
7850
|
+
if (range.mark.attrs.src !== src) return;
|
|
7851
|
+
const current = view.state.doc.textBetween(range.from, range.to);
|
|
7852
|
+
const base = stripMagicComment(current);
|
|
7853
|
+
const comment = formatMagicComment({
|
|
7854
|
+
...parseMagicComment(current.slice(base.length)),
|
|
7855
|
+
snapshot
|
|
7856
|
+
});
|
|
7857
|
+
if (base + comment === current) return;
|
|
7858
|
+
view.dispatch(view.state.tr.insertText(base + comment, range.from, range.to).setMeta("addToHistory", false));
|
|
7904
7859
|
}
|
|
7905
7860
|
var ImageMarkView = class {
|
|
7906
7861
|
#dom;
|
|
7907
7862
|
#contentDOM;
|
|
7908
7863
|
#view;
|
|
7909
7864
|
#resolveImageUrl;
|
|
7910
|
-
#
|
|
7865
|
+
#resolveXPost;
|
|
7866
|
+
#resolveYouTubeVideo;
|
|
7911
7867
|
#attrs;
|
|
7912
7868
|
#resizableRoot;
|
|
7913
7869
|
#image;
|
|
7914
|
-
#
|
|
7870
|
+
#destroyed = false;
|
|
7915
7871
|
constructor(mark, view, options) {
|
|
7916
7872
|
this.#attrs = mark.attrs;
|
|
7917
7873
|
this.#view = view;
|
|
7918
7874
|
this.#resolveImageUrl = options.resolveImageUrl;
|
|
7919
|
-
this.#
|
|
7875
|
+
this.#resolveXPost = options.resolveXPost ?? defaultResolveXPost;
|
|
7876
|
+
this.#resolveYouTubeVideo = options.resolveYouTubeVideo ?? defaultResolveYouTubeVideo;
|
|
7920
7877
|
this.#dom = document.createElement("span");
|
|
7921
7878
|
this.#dom.className = "md-image-view md-atom-view";
|
|
7922
7879
|
this.#contentDOM = document.createElement("span");
|
|
@@ -7939,67 +7896,105 @@ var ImageMarkView = class {
|
|
|
7939
7896
|
update(mark) {
|
|
7940
7897
|
const next = mark.attrs;
|
|
7941
7898
|
const previous = this.#attrs;
|
|
7942
|
-
if (next.src !== previous.src) return false;
|
|
7899
|
+
if (next.src !== previous.src || next.snapshot !== previous.snapshot) return false;
|
|
7943
7900
|
this.#attrs = next;
|
|
7944
7901
|
if (this.#image && next.alt !== previous.alt) this.#image.alt = next.alt;
|
|
7945
7902
|
if (this.#resizableRoot && (next.width !== previous.width || next.height !== previous.height)) {
|
|
7946
7903
|
if (this.#image) applyImageDisplaySize(this.#resizableRoot, this.#image, next.width, next.height);
|
|
7947
7904
|
else applySize(this.#resizableRoot, next.width, next.height);
|
|
7948
7905
|
}
|
|
7949
|
-
if (this.#tweetIframe && next.height !== previous.height) applyTweetHeight(this.#tweetIframe, next.height);
|
|
7950
7906
|
return true;
|
|
7951
7907
|
}
|
|
7952
7908
|
ignoreMutation(mutation) {
|
|
7953
7909
|
return !this.#contentDOM.contains(mutation.target);
|
|
7954
7910
|
}
|
|
7911
|
+
destroy() {
|
|
7912
|
+
this.#destroyed = true;
|
|
7913
|
+
}
|
|
7955
7914
|
/**
|
|
7956
|
-
* Build the inline preview for the image `src`:
|
|
7915
|
+
* Build the inline preview for the image `src`: a post-embed card or a
|
|
7916
|
+
* resizable `<img>`.
|
|
7957
7917
|
*/
|
|
7958
7918
|
#renderPreview() {
|
|
7959
7919
|
const { src } = this.#attrs;
|
|
7960
|
-
const
|
|
7961
|
-
|
|
7962
|
-
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
|
|
7966
|
-
} : void 0;
|
|
7967
|
-
const iframe = buildEmbedIframe(embed, this.#attrs.height, onHeight);
|
|
7968
|
-
if (embed.kind === "tweet") this.#tweetIframe = iframe;
|
|
7969
|
-
wrapper.appendChild(embed.kind === "youtube" ? this.#buildResizableEmbed(iframe) : iframe);
|
|
7920
|
+
const wrapper = document.createElement("span");
|
|
7921
|
+
wrapper.className = "md-image-view-preview md-atom-view-preview";
|
|
7922
|
+
const kind = matchPostEmbed(src);
|
|
7923
|
+
if (kind) {
|
|
7924
|
+
wrapper.dataset.testid = `${kind}-embed`;
|
|
7925
|
+
wrapper.appendChild(this.#buildPostEmbed(kind, src));
|
|
7970
7926
|
return wrapper;
|
|
7971
7927
|
}
|
|
7972
7928
|
const url = (this.#resolveImageUrl ?? defaultResolveImageUrl)(src);
|
|
7973
7929
|
if (!url) return void 0;
|
|
7974
|
-
const wrapper = document.createElement("span");
|
|
7975
|
-
wrapper.className = "md-image-view-preview md-atom-view-preview";
|
|
7976
7930
|
wrapper.dataset.testid = "image-preview";
|
|
7977
7931
|
wrapper.appendChild(this.#buildResizableImage(url));
|
|
7978
7932
|
return wrapper;
|
|
7979
7933
|
}
|
|
7980
7934
|
/**
|
|
7981
|
-
* A
|
|
7982
|
-
*
|
|
7983
|
-
*
|
|
7984
|
-
*
|
|
7935
|
+
* A post-embed card renders a saved snapshot as is. Without one (or with
|
|
7936
|
+
* one that does not validate or names another kind) it loads through the
|
|
7937
|
+
* resolver, rendering its own pending and unavailable states, and the first
|
|
7938
|
+
* answer is written back into the source so the next open renders in the
|
|
7939
|
+
* first frame with no request.
|
|
7985
7940
|
*/
|
|
7986
|
-
#
|
|
7941
|
+
#buildPostEmbed(kind, src) {
|
|
7942
|
+
const saved = this.#attrs.snapshot == null ? void 0 : parsePostEmbedSnapshot(this.#attrs.snapshot);
|
|
7943
|
+
if (kind === "x-post") {
|
|
7944
|
+
registerXPost();
|
|
7945
|
+
const element = document.createElement("post-embed-x-post");
|
|
7946
|
+
element.data = saved?.kind === "x-post" ? saved.data : null;
|
|
7947
|
+
element.resolver = this.#persisting(kind, this.#resolveXPost);
|
|
7948
|
+
element.url = src;
|
|
7949
|
+
return element;
|
|
7950
|
+
}
|
|
7951
|
+
registerYouTubeVideo();
|
|
7952
|
+
const element = document.createElement("post-embed-youtube-video");
|
|
7953
|
+
element.playback = "inline";
|
|
7954
|
+
element.data = saved?.kind === "youtube-video" ? saved.data : null;
|
|
7955
|
+
element.resolver = this.#persisting(kind, this.#resolveYouTubeVideo);
|
|
7956
|
+
element.url = src;
|
|
7957
|
+
return this.#buildResizableVideo(element);
|
|
7958
|
+
}
|
|
7959
|
+
/**
|
|
7960
|
+
* Wrap a resolver so its first valid answer is persisted under `kind`.
|
|
7961
|
+
* post-embed only calls it while `data` is null, and logs a rejection
|
|
7962
|
+
* itself.
|
|
7963
|
+
*/
|
|
7964
|
+
#persisting(kind, resolver) {
|
|
7965
|
+
return (url) => {
|
|
7966
|
+
const result = resolver(url);
|
|
7967
|
+
Promise.resolve(result).then((value) => {
|
|
7968
|
+
if (this.#destroyed || value == null) return;
|
|
7969
|
+
const snapshot = parsePostEmbedSnapshot({
|
|
7970
|
+
kind,
|
|
7971
|
+
data: value
|
|
7972
|
+
});
|
|
7973
|
+
if (snapshot) commitSnapshot(this.#view, this.#contentDOM, url, snapshot);
|
|
7974
|
+
}, () => {});
|
|
7975
|
+
return result;
|
|
7976
|
+
};
|
|
7977
|
+
}
|
|
7978
|
+
/**
|
|
7979
|
+
* A resizable video card: the same resizable web component as images, but
|
|
7980
|
+
* only the dragged width is persisted, as `<!-- {"width":N} -->`; the card
|
|
7981
|
+
* keeps its content height.
|
|
7982
|
+
*/
|
|
7983
|
+
#buildResizableVideo(element) {
|
|
7987
7984
|
registerResizableRootElement();
|
|
7988
7985
|
registerResizableHandleElement();
|
|
7989
7986
|
const root = document.createElement("prosekit-resizable-root");
|
|
7990
7987
|
root.className = "md-embed-resizable";
|
|
7991
7988
|
root.dataset.testid = "embed-resizable";
|
|
7992
|
-
root.
|
|
7993
|
-
|
|
7994
|
-
root.appendChild(iframe);
|
|
7989
|
+
applySize(root, this.#attrs.width, null);
|
|
7990
|
+
root.appendChild(element);
|
|
7995
7991
|
const handle = document.createElement("prosekit-resizable-handle");
|
|
7996
7992
|
handle.className = "md-image-resize-handle";
|
|
7997
7993
|
handle.setAttribute("position", "bottom-right");
|
|
7998
7994
|
handle.addEventListener("click", (event) => event.stopPropagation());
|
|
7999
7995
|
root.appendChild(handle);
|
|
8000
7996
|
root.addEventListener("resizeEnd", (event) => {
|
|
8001
|
-
|
|
8002
|
-
commitImageSize(this.#view, this.#contentDOM, nextWidth, nextHeight);
|
|
7997
|
+
commitEmbedWidth(this.#view, this.#contentDOM, event.detail.width);
|
|
8003
7998
|
});
|
|
8004
7999
|
this.#resizableRoot = root;
|
|
8005
8000
|
return root;
|
|
@@ -8864,4 +8859,4 @@ function getVirtualElementFromRange(view, range) {
|
|
|
8864
8859
|
}
|
|
8865
8860
|
|
|
8866
8861
|
//#endregion
|
|
8867
|
-
export { EDITOR_KEY_BINDINGS, Priority, buildFileMarkdown, checkRoundTrip, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineCodeBlockSyntaxHighlight, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLComment, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkCommands, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMath, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, docToMarkdown, formatFileSize, formatSizedWikiEmbed, getCodeTokens, getFileKind, getIsComposing, getLinkText, getLinkUnitAt, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunks, inlineTextToMarkChunksWithContext, insertLink, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isMarkOfType, isModEvent, isNodeOfType, isReferenceDefinitionNode, isSelectionInTableCell,
|
|
8862
|
+
export { EDITOR_KEY_BINDINGS, Priority, buildFileMarkdown, checkRoundTrip, codeBlockLanguages, collectReferenceDefinitions, defaultResolveImageUrl, defaultResolveXPost, defaultResolveYouTubeVideo, defineBulletAfterHeading, defineCodeBlockPreviewPlugin, defineCodeBlockSyntaxHighlight, defineEditorExtension, defineEmbedPaste, defineExitBoundaryHandler, defineFileClickHandler, defineFilePaste, defineFileView, defineFollowLinkHandler, defineHTMLComment, defineImage, defineImageClickHandler, defineLinkClickHandler, defineLinkCommands, defineLinkEditKeymap, defineLinkHoverHandler, defineLinkPaste, defineMath, definePendingReplacementHandler, definePlaceholder, defineReadonly, defineSearchStatusHandler, defineSubstitution, defineTagClickHandler, defineViewAttributes, defineVirtualCaret, defineWikilinkClickHandler, defineWikilinkHoverHandler, defineWikilinkTrigger, docToMarkdown, formatFileSize, formatSizedWikiEmbed, getCodeTokens, getFileKind, getIsComposing, getLinkText, getLinkUnitAt, getMarkBuilders, getPendingReplacement, getSearchStatus, getSelectedText, getTableColumnAlign, getTextblockDisplayText, getVirtualElementFromRange, inlineTextToMarkChunks, inlineTextToMarkChunksWithContext, insertLink, isCodeBlockPreviewHiddenDecoration, isLinkTextForHref, isMarkOfType, isModEvent, isNodeOfType, isReferenceDefinitionNode, isSelectionInTableCell, loadKaTeX, markdownToDoc, matchPostEmbed, normalizeHref, parsePostEmbedSnapshot, parseWikiEmbed, parseXPostId, removeLink, renderMathInto, updateLink, wikiEmbedBasename, withPriority };
|
package/dist/style.css
CHANGED
|
@@ -147,6 +147,454 @@
|
|
|
147
147
|
.prosemirror-flat-list[data-list-kind="toggle"][data-list-collapsable][data-list-collapsed] > .list-content > :nth-child(n+2) {
|
|
148
148
|
display: none;
|
|
149
149
|
}
|
|
150
|
+
|
|
151
|
+
@property --post-embed-color {
|
|
152
|
+
syntax: "*";
|
|
153
|
+
inherits: true
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@property --post-embed-background {
|
|
157
|
+
syntax: "*";
|
|
158
|
+
inherits: true
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
@property --post-embed-border-color {
|
|
162
|
+
syntax: "*";
|
|
163
|
+
inherits: true
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
@property --post-embed-radius {
|
|
167
|
+
syntax: "*";
|
|
168
|
+
inherits: true
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@property --post-embed-padding {
|
|
172
|
+
syntax: "*";
|
|
173
|
+
inherits: true
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@property --post-embed-muted-color {
|
|
177
|
+
syntax: "*";
|
|
178
|
+
inherits: true
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@property --post-embed-link-color {
|
|
182
|
+
syntax: "*";
|
|
183
|
+
inherits: true
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@layer post-embed {
|
|
187
|
+
:where([data-post-embed="x-post"]) {
|
|
188
|
+
--_hairline: var(--post-embed-border-color, color-mix(in srgb, currentColor 15%, transparent));
|
|
189
|
+
--_muted: var(--post-embed-muted-color, color-mix(in srgb, currentColor 58%, transparent));
|
|
190
|
+
--_link: var(--post-embed-link-color, #1d9bf0);
|
|
191
|
+
--_badge: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 1.5l2.4 2 3.1-.5 1.1 3 2.9 1.3-.5 3.1 2 2.4-2 2.4.5 3.1-2.9 1.3-1.1 3-3.1-.5-2.4 2-2.4-2-3.1.5-1.1-3-2.9-1.3.5-3.1L1 12l2-2.4-.5-3.1 2.9-1.3 1.1-3 3.1.5z'/%3E%3C/svg%3E");
|
|
192
|
+
--_check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M7.5 12.5l3 3 6-6.5' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
|
|
193
|
+
font: inherit;
|
|
194
|
+
color: var(--post-embed-color, inherit);
|
|
195
|
+
display: block;
|
|
196
|
+
|
|
197
|
+
& :where([data-root]) {
|
|
198
|
+
background: var(--post-embed-background, transparent);
|
|
199
|
+
border: 1px solid var(--_hairline);
|
|
200
|
+
border-radius: var(--post-embed-radius, 1rem);
|
|
201
|
+
padding: var(--post-embed-padding, 1rem);
|
|
202
|
+
overflow-wrap: anywhere;
|
|
203
|
+
line-height: 1.4;
|
|
204
|
+
position: relative;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
& :where([hidden]) {
|
|
208
|
+
display: none !important;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
& :where(a) {
|
|
212
|
+
color: var(--_link);
|
|
213
|
+
text-decoration: none;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
& :where(a:hover) {
|
|
217
|
+
text-underline-offset: .15em;
|
|
218
|
+
text-decoration: underline;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
& :where(a:focus-visible, button:focus-visible) {
|
|
222
|
+
outline: 2px solid var(--_link);
|
|
223
|
+
outline-offset: 2px;
|
|
224
|
+
border-radius: 4px;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
& :where([data-author]) {
|
|
228
|
+
align-items: center;
|
|
229
|
+
gap: .75rem;
|
|
230
|
+
display: flex;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
& :where([data-avatar]) {
|
|
234
|
+
object-fit: cover;
|
|
235
|
+
border-radius: 50%;
|
|
236
|
+
flex: none;
|
|
237
|
+
width: 3rem;
|
|
238
|
+
height: 3rem;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
& :where([data-avatar][data-shape="square"]) {
|
|
242
|
+
border-radius: .5rem;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
& :where([data-avatar][data-shape="hexagon"]) {
|
|
246
|
+
clip-path: polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0 50%);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
& :where([data-author-details]) {
|
|
250
|
+
min-width: 0;
|
|
251
|
+
color: var(--_muted);
|
|
252
|
+
flex: 1;
|
|
253
|
+
line-height: 1.3;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
& :where([data-author-name]) {
|
|
257
|
+
color: var(--post-embed-color, inherit);
|
|
258
|
+
flex-wrap: wrap;
|
|
259
|
+
align-items: center;
|
|
260
|
+
gap: .25rem;
|
|
261
|
+
font-weight: 700;
|
|
262
|
+
display: flex;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
& :where([data-author-details] > bdi > a) {
|
|
266
|
+
color: inherit;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
& :where([data-verified]) {
|
|
270
|
+
color: #0000;
|
|
271
|
+
--_badge-color: #1d9bf0;
|
|
272
|
+
width: 1.2em;
|
|
273
|
+
height: 1.2em;
|
|
274
|
+
display: inline-block;
|
|
275
|
+
position: relative;
|
|
276
|
+
overflow: hidden;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
& :where([data-verified="business"]) {
|
|
280
|
+
--_badge-color: #e2b719;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
& :where([data-verified="government"]) {
|
|
284
|
+
--_badge-color: #829aab;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
& :where([data-verified]):before {
|
|
288
|
+
content: "";
|
|
289
|
+
background: var(--_badge-color);
|
|
290
|
+
mask: var(--_badge) center / contain no-repeat;
|
|
291
|
+
position: absolute;
|
|
292
|
+
inset: 0;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
& :where([data-verified]):after {
|
|
296
|
+
content: "";
|
|
297
|
+
mask: var(--_check) center / contain no-repeat;
|
|
298
|
+
background: #fff;
|
|
299
|
+
position: absolute;
|
|
300
|
+
inset: 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
& :where([data-label]) {
|
|
304
|
+
color: var(--_muted);
|
|
305
|
+
align-items: center;
|
|
306
|
+
gap: .25rem;
|
|
307
|
+
font-size: .8em;
|
|
308
|
+
font-weight: 500;
|
|
309
|
+
display: inline-flex;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
& :where([data-label] img) {
|
|
313
|
+
border-radius: 2px;
|
|
314
|
+
width: 1.1em;
|
|
315
|
+
height: 1.1em;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
& :where([data-reply-to]) {
|
|
319
|
+
color: var(--_muted);
|
|
320
|
+
margin-block-start: .75rem;
|
|
321
|
+
font-size: .9375em;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
& :where([data-body]) {
|
|
325
|
+
margin-block: .75rem 0;
|
|
326
|
+
font-size: 1.0625em;
|
|
327
|
+
line-height: 1.45;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
& :where([data-text]) {
|
|
331
|
+
white-space: pre-wrap;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
& :where([data-media]) {
|
|
335
|
+
border: 1px solid var(--_hairline);
|
|
336
|
+
border-radius: 1rem;
|
|
337
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
338
|
+
gap: 2px;
|
|
339
|
+
margin-block-start: .75rem;
|
|
340
|
+
display: grid;
|
|
341
|
+
overflow: hidden;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
& :where([data-media][data-count="1"]) {
|
|
345
|
+
grid-template-columns: minmax(0, 1fr);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
& :where([data-media-item]) {
|
|
349
|
+
min-width: 0;
|
|
350
|
+
overflow: hidden;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
& :where([data-media-item] > a) {
|
|
354
|
+
height: 100%;
|
|
355
|
+
display: block;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
& :where([data-media-item] img, [data-media-item] video) {
|
|
359
|
+
object-fit: contain;
|
|
360
|
+
background: color-mix(in srgb, currentColor 6%, transparent);
|
|
361
|
+
width: 100%;
|
|
362
|
+
height: auto;
|
|
363
|
+
max-height: 36rem;
|
|
364
|
+
display: block;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
& :where([data-media]:not([data-count="1"]) [data-media-item] img) {
|
|
368
|
+
aspect-ratio: 1;
|
|
369
|
+
object-fit: cover;
|
|
370
|
+
height: 100%;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
& :where([data-media][data-count="3"] [data-media-item]:first-child) {
|
|
374
|
+
grid-row: span 2;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
& :where([data-media-unavailable], [data-media-error]) {
|
|
378
|
+
color: var(--_muted);
|
|
379
|
+
background: color-mix(in srgb, currentColor 4%, transparent);
|
|
380
|
+
padding: .75rem 1rem;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
& :where([data-quoted]) {
|
|
384
|
+
border: 1px solid var(--_hairline);
|
|
385
|
+
border-radius: 1rem;
|
|
386
|
+
margin-block-start: .75rem;
|
|
387
|
+
padding: .75rem;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
& :where([data-quoted] [data-author]) {
|
|
391
|
+
gap: .5rem;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
& :where([data-quoted] [data-avatar]) {
|
|
395
|
+
width: 1.5rem;
|
|
396
|
+
height: 1.5rem;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
& :where([data-quoted] [data-author-details]) {
|
|
400
|
+
flex-wrap: wrap;
|
|
401
|
+
align-items: baseline;
|
|
402
|
+
gap: 0 .35rem;
|
|
403
|
+
display: flex;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
& :where([data-quoted] [data-body]) {
|
|
407
|
+
margin-block-start: .375rem;
|
|
408
|
+
font-size: 1em;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
& :where([data-quoted] [data-media]) {
|
|
412
|
+
border-radius: .75rem;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
& :where([data-quoted] [data-footer]) {
|
|
416
|
+
margin-block-start: .5rem;
|
|
417
|
+
font-size: .875em;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
& :where([data-footer]) {
|
|
421
|
+
color: var(--_muted);
|
|
422
|
+
flex-wrap: wrap;
|
|
423
|
+
align-items: center;
|
|
424
|
+
gap: .25rem 0;
|
|
425
|
+
margin-block-start: .75rem;
|
|
426
|
+
font-size: .9375em;
|
|
427
|
+
display: flex;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
& :where([data-footer] > a) {
|
|
431
|
+
color: inherit;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
& :where([data-footer] > [data-edited]):before {
|
|
435
|
+
content: "";
|
|
436
|
+
vertical-align: middle;
|
|
437
|
+
background: currentColor;
|
|
438
|
+
border-radius: 50%;
|
|
439
|
+
width: 3px;
|
|
440
|
+
height: 3px;
|
|
441
|
+
margin-inline: .45rem;
|
|
442
|
+
display: inline-block;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
& :where([data-edited]) {
|
|
446
|
+
font-style: normal;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
& :where([data-fallback] [data-body]) {
|
|
450
|
+
color: var(--_muted);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
:where([data-post-embed="youtube-video"]) {
|
|
455
|
+
--_hairline: var(--post-embed-border-color, color-mix(in srgb, currentColor 15%, transparent));
|
|
456
|
+
--_muted: var(--post-embed-muted-color, color-mix(in srgb, currentColor 58%, transparent));
|
|
457
|
+
--_link: var(--post-embed-link-color, #1d9bf0);
|
|
458
|
+
--_radius: var(--post-embed-radius, 1rem);
|
|
459
|
+
--_page: var(--post-embed-background, Canvas);
|
|
460
|
+
--_play: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 68 48'%3E%3Cpath d='M66.52 7.74c-.78-2.93-2.49-5.41-5.42-6.19C55.79.13 34 0 34 0S12.21.13 6.9 1.55C3.97 2.33 2.27 4.81 1.48 7.74.06 13.05 0 24 0 24s.06 10.95 1.48 16.26c.78 2.93 2.49 5.41 5.42 6.19C12.21 47.87 34 48 34 48s21.79-.13 27.1-1.55c2.93-.78 4.64-3.26 5.42-6.19C67.94 34.95 68 24 68 24S67.94 13.05 66.52 7.74z' fill='%23f00'/%3E%3Cpath d='M45 24 27 14v20z' fill='%23fff'/%3E%3C/svg%3E");
|
|
461
|
+
font: inherit;
|
|
462
|
+
color: var(--post-embed-color, inherit);
|
|
463
|
+
display: block;
|
|
464
|
+
|
|
465
|
+
& :where([data-root]) {
|
|
466
|
+
overflow-wrap: anywhere;
|
|
467
|
+
line-height: 1.35;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
& :where([hidden]) {
|
|
471
|
+
display: none !important;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
& :where(a) {
|
|
475
|
+
color: var(--_link);
|
|
476
|
+
text-decoration: none;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
& :where(a:hover) {
|
|
480
|
+
text-underline-offset: .15em;
|
|
481
|
+
text-decoration: underline;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
& :where(a:focus-visible, button:focus-visible) {
|
|
485
|
+
outline: 2px solid var(--_link);
|
|
486
|
+
outline-offset: 2px;
|
|
487
|
+
border-radius: 4px;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
& :where(article) {
|
|
491
|
+
max-width: 100%;
|
|
492
|
+
position: relative;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
& :where([data-orientation="portrait"]) {
|
|
496
|
+
max-width: 22rem;
|
|
497
|
+
margin-inline: auto;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
& :where([data-poster], iframe) {
|
|
501
|
+
aspect-ratio: 16 / 9;
|
|
502
|
+
border-radius: var(--_radius);
|
|
503
|
+
background: color-mix(in srgb, currentColor 6%, transparent);
|
|
504
|
+
border: 0;
|
|
505
|
+
width: 100%;
|
|
506
|
+
display: block;
|
|
507
|
+
position: relative;
|
|
508
|
+
overflow: hidden;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
& :where([data-orientation="portrait"] [data-poster], [data-orientation="portrait"] iframe) {
|
|
512
|
+
aspect-ratio: 9 / 16;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
& :where(button[data-poster]) {
|
|
516
|
+
font: inherit;
|
|
517
|
+
color: inherit;
|
|
518
|
+
cursor: pointer;
|
|
519
|
+
padding: 0;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
& :where([data-poster] img) {
|
|
523
|
+
object-fit: cover;
|
|
524
|
+
width: 100%;
|
|
525
|
+
height: 100%;
|
|
526
|
+
position: absolute;
|
|
527
|
+
inset: 0;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
& :where([data-play]) {
|
|
531
|
+
background: var(--_play) center / contain no-repeat;
|
|
532
|
+
width: 68px;
|
|
533
|
+
height: 48px;
|
|
534
|
+
position: absolute;
|
|
535
|
+
inset: 50% auto auto 50%;
|
|
536
|
+
translate: -50% -50%;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
& :where([data-details]) {
|
|
540
|
+
pointer-events: none;
|
|
541
|
+
flex-direction: column;
|
|
542
|
+
align-items: flex-start;
|
|
543
|
+
gap: .125rem;
|
|
544
|
+
display: flex;
|
|
545
|
+
position: absolute;
|
|
546
|
+
inset: 0 0 auto;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
& :where([data-details] a, [data-details] span) {
|
|
550
|
+
pointer-events: auto;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
& :where([data-title]) {
|
|
554
|
+
color: inherit;
|
|
555
|
+
font-size: 1.0625em;
|
|
556
|
+
font-weight: 700;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
& :where([data-details] > a:has([data-author]), [data-author]) {
|
|
560
|
+
color: inherit;
|
|
561
|
+
font-size: .875em;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
& :where([data-fallback]) {
|
|
565
|
+
border: 1px solid var(--_hairline);
|
|
566
|
+
border-radius: var(--_radius);
|
|
567
|
+
padding: var(--post-embed-padding, 1rem);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
& :where([data-fallback] [data-details]) {
|
|
571
|
+
position: static;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
& :where([data-body]) {
|
|
575
|
+
color: var(--_muted);
|
|
576
|
+
margin-block: .5rem 0;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
& :where([data-footer]) {
|
|
580
|
+
color: var(--_muted);
|
|
581
|
+
margin-block-start: .75rem;
|
|
582
|
+
font-size: .9375em;
|
|
583
|
+
display: block;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
& :where([data-details]) {
|
|
587
|
+
border-radius: var(--_radius) var(--_radius) 0 0;
|
|
588
|
+
color: #fff;
|
|
589
|
+
background: linear-gradient(#000000b8, #0000);
|
|
590
|
+
padding: .875rem 1rem 2.5rem;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
& :where([data-details] > a:has([data-author]), [data-author]) {
|
|
594
|
+
color: #ffffffd1;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
150
598
|
}
|
|
151
599
|
|
|
152
600
|
@layer meowdown.theme {
|
|
@@ -640,39 +1088,25 @@
|
|
|
640
1088
|
padding: 2px 6px;
|
|
641
1089
|
}
|
|
642
1090
|
|
|
643
|
-
.ProseMirror .md-embed-youtube {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
border: 0;
|
|
647
|
-
width: 100%;
|
|
648
|
-
max-width: 100%;
|
|
649
|
-
display: block;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
.ProseMirror .md-embed-tweet {
|
|
653
|
-
border: 0;
|
|
654
|
-
width: 100%;
|
|
1091
|
+
.ProseMirror .md-image-view-preview post-embed-x-post, .ProseMirror .md-image-view-preview post-embed-youtube-video {
|
|
1092
|
+
-webkit-user-select: text;
|
|
1093
|
+
user-select: text;
|
|
655
1094
|
max-width: 550px;
|
|
656
1095
|
display: block;
|
|
657
1096
|
}
|
|
658
1097
|
|
|
659
|
-
.ProseMirror .md-embed-tweet:not([data-sized]) {
|
|
660
|
-
min-height: 250px;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
1098
|
.ProseMirror .md-embed-resizable {
|
|
664
1099
|
isolation: isolate;
|
|
665
1100
|
min-width: 2.5rem;
|
|
666
1101
|
max-width: 100%;
|
|
667
|
-
min-height: 2.5rem;
|
|
668
1102
|
display: block;
|
|
669
1103
|
position: relative;
|
|
1104
|
+
height: auto !important;
|
|
670
1105
|
}
|
|
671
1106
|
|
|
672
|
-
.ProseMirror .md-embed-resizable
|
|
673
|
-
aspect-ratio: auto;
|
|
1107
|
+
.ProseMirror .md-embed-resizable post-embed-youtube-video {
|
|
674
1108
|
width: 100%;
|
|
675
|
-
|
|
1109
|
+
max-width: none;
|
|
676
1110
|
}
|
|
677
1111
|
|
|
678
1112
|
.ProseMirror .md-embed-resizable[data-resizing] iframe {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meowdown/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.70.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -26,12 +26,17 @@
|
|
|
26
26
|
"@floating-ui/dom": "^1.8.0",
|
|
27
27
|
"@lezer/highlight": "^1.2.3",
|
|
28
28
|
"@ocavue/utils": "^1.8.0",
|
|
29
|
+
"@post-embed/elements": "^0.2.0",
|
|
30
|
+
"@post-embed/exporter": "^0.2.0",
|
|
31
|
+
"@post-embed/schema": "^0.2.0",
|
|
32
|
+
"@post-embed/types": "^0.2.0",
|
|
29
33
|
"@prosekit/core": "^0.13.2",
|
|
30
34
|
"@prosekit/extensions": "^0.18.2",
|
|
31
35
|
"@prosekit/pm": "^0.1.19",
|
|
32
36
|
"@prosekit/web": "^0.9.2",
|
|
33
37
|
"hast-util-to-mdast": "^10.1.2",
|
|
34
38
|
"katex": "^0.18.5",
|
|
39
|
+
"lru.min": "^1.1.5",
|
|
35
40
|
"mdast-util-to-markdown": "^2.1.2",
|
|
36
41
|
"micromark-util-decode-string": "^2.0.1",
|
|
37
42
|
"micromark-util-normalize-identifier": "^2.0.1",
|
|
@@ -43,7 +48,8 @@
|
|
|
43
48
|
"remark-stringify": "^11.0.0",
|
|
44
49
|
"unicode-by-name": "^0.2.0",
|
|
45
50
|
"unified": "^11.0.5",
|
|
46
|
-
"
|
|
51
|
+
"valibot": "^1.4.2",
|
|
52
|
+
"@meowdown/markdown": "^0.70.0"
|
|
47
53
|
},
|
|
48
54
|
"devDependencies": {
|
|
49
55
|
"@ocavue/tsconfig": "^0.7.1",
|