Switch mobile editor back to minimal Tiptap
This commit is contained in:
1 parent
abfaeb3d85
commit
03e6f887dc
4 files changed
+204
-340
No files matched your search
+141
-214
@@ -1,32 +1,29 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import MDEditor, { commands } from "@uiw/react-md-editor/nohighlight";
|
||||
import type { PreviewType } from "@uiw/react-md-editor";
|
||||
import "@uiw/react-md-editor/markdown-editor.css";
|
||||
import { docToMarkdown, type MemoDetail, type Resource, type TiptapDoc } from "@edgeever/shared";
|
||||
import { EditorContent, useEditor } from "@tiptap/react";
|
||||
import StarterKit from "@tiptap/starter-kit";
|
||||
import Image from "@tiptap/extension-image";
|
||||
import Placeholder from "@tiptap/extension-placeholder";
|
||||
import { docToMarkdown, emptyDoc, markdownToDoc, type MemoDetail, type TiptapDoc } from "@edgeever/shared";
|
||||
import "./styles/mobile-markdown-editor.css";
|
||||
|
||||
const AUTO_SAVE_DELAY_MS = 1200;
|
||||
const LEAVE_SAVE_TIMEOUT_MS = 1600;
|
||||
const DRAFT_STORAGE_PREFIX = "edgeever-mobile-edit-draft:";
|
||||
const DRAFT_STORAGE_PREFIX = "edgeever-mobile-tiptap-draft:";
|
||||
const DEFAULT_MEMO_TITLE = "无标题笔记";
|
||||
|
||||
type MemoResponse = {
|
||||
memo: MemoDetail;
|
||||
};
|
||||
|
||||
type ResourceResponse = {
|
||||
resource: Resource;
|
||||
};
|
||||
|
||||
type MobileDraft = {
|
||||
title: string;
|
||||
tagsText: string;
|
||||
body: string;
|
||||
contentJson: TiptapDoc;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
type SaveState = "loading" | "idle" | "dirty" | "saving" | "saved" | "uploading" | "error" | "local-draft" | "leaving";
|
||||
type SaveState = "loading" | "idle" | "dirty" | "saving" | "saved" | "error" | "local-draft" | "leaving";
|
||||
|
||||
const getParams = () => new URLSearchParams(window.location.hash ? window.location.hash.slice(1) : window.location.search);
|
||||
|
||||
@@ -36,14 +33,12 @@ const parseTags = (value: string) =>
|
||||
.map((tag) => tag.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
const escapeMarkdownLabel = (value: string) => value.replace(/\]/g, "\\]");
|
||||
|
||||
const safeReturnPath = (value: string | null) => (value?.startsWith("/") ? value : "/");
|
||||
|
||||
const requestJson = async <T,>(path: string, init?: RequestInit): Promise<T> => {
|
||||
const headers = new Headers(init?.headers);
|
||||
|
||||
if (!(init?.body instanceof FormData) && !headers.has("Content-Type")) {
|
||||
if (!headers.has("Content-Type")) {
|
||||
headers.set("Content-Type", "application/json");
|
||||
}
|
||||
|
||||
@@ -65,42 +60,19 @@ const requestJson = async <T,>(path: string, init?: RequestInit): Promise<T> =>
|
||||
return response.json() as Promise<T>;
|
||||
};
|
||||
|
||||
const uploadResource = async (memoId: string, file: File) => {
|
||||
const form = new FormData();
|
||||
form.append("file", file);
|
||||
|
||||
return requestJson<ResourceResponse>(`/api/v1/memos/${encodeURIComponent(memoId)}/resources`, {
|
||||
method: "POST",
|
||||
body: form,
|
||||
});
|
||||
};
|
||||
|
||||
const markdownForResource = (resource: Resource) => {
|
||||
const filename = resource.filename || "附件";
|
||||
|
||||
if (resource.kind === "image") {
|
||||
return ``;
|
||||
const normalizeDoc = (memo: MemoDetail): TiptapDoc => {
|
||||
if (memo.contentJson && typeof memo.contentJson === "object") {
|
||||
return memo.contentJson as TiptapDoc;
|
||||
}
|
||||
|
||||
return `附件:${filename}\n${resource.url}`;
|
||||
if (memo.contentMarkdown) {
|
||||
return markdownToDoc(memo.contentMarkdown);
|
||||
}
|
||||
|
||||
return emptyDoc();
|
||||
};
|
||||
|
||||
const insertAtCursor = (value: string, insertion: string) => {
|
||||
const textarea = document.querySelector<HTMLTextAreaElement>(".edgeever-mobile-md-editor textarea");
|
||||
const start = textarea?.selectionStart ?? value.length;
|
||||
const end = textarea?.selectionEnd ?? start;
|
||||
const nextValue = `${value.slice(0, start)}${insertion}${value.slice(end)}`;
|
||||
const nextCursor = start + insertion.length;
|
||||
|
||||
window.setTimeout(() => {
|
||||
textarea?.focus();
|
||||
textarea?.setSelectionRange(nextCursor, nextCursor);
|
||||
}, 0);
|
||||
|
||||
return nextValue;
|
||||
};
|
||||
|
||||
const MobileMarkdownEditorApp = () => {
|
||||
const MobileTiptapEditorApp = () => {
|
||||
const params = useMemo(() => getParams(), []);
|
||||
const memoId = params.get("memoId");
|
||||
const returnTo = safeReturnPath(params.get("returnTo"));
|
||||
@@ -111,10 +83,9 @@ const MobileMarkdownEditorApp = () => {
|
||||
const titleRef = useRef("");
|
||||
const [tagsText, setTagsText] = useState("");
|
||||
const tagsTextRef = useRef("");
|
||||
const [markdown, setMarkdown] = useState("");
|
||||
const markdownRef = useRef("");
|
||||
const [preview, setPreview] = useState<PreviewType>("edit");
|
||||
const contentJsonRef = useRef<TiptapDoc>(emptyDoc());
|
||||
const [saveState, setSaveState] = useState<SaveState>("loading");
|
||||
const saveStateRef = useRef<SaveState>("loading");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const dirtyRef = useRef(false);
|
||||
const leavingRef = useRef(false);
|
||||
@@ -122,31 +93,22 @@ const MobileMarkdownEditorApp = () => {
|
||||
const saveTimerRef = useRef<number | null>(null);
|
||||
const currentSavePromiseRef = useRef<Promise<boolean> | null>(null);
|
||||
const lastSavedSnapshotRef = useRef("");
|
||||
const imageInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
memoRef.current = memo;
|
||||
}, [memo]);
|
||||
const setSaveStateStable = useCallback((nextState: SaveState) => {
|
||||
if (saveStateRef.current === nextState) {
|
||||
return;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
titleRef.current = title;
|
||||
}, [title]);
|
||||
|
||||
useEffect(() => {
|
||||
tagsTextRef.current = tagsText;
|
||||
}, [tagsText]);
|
||||
|
||||
useEffect(() => {
|
||||
markdownRef.current = markdown;
|
||||
}, [markdown]);
|
||||
saveStateRef.current = nextState;
|
||||
setSaveState(nextState);
|
||||
}, []);
|
||||
|
||||
const currentSnapshot = useCallback(
|
||||
() =>
|
||||
JSON.stringify({
|
||||
title: titleRef.current,
|
||||
tagsText: tagsTextRef.current,
|
||||
body: markdownRef.current,
|
||||
contentJson: contentJsonRef.current,
|
||||
}),
|
||||
[]
|
||||
);
|
||||
@@ -159,7 +121,7 @@ const MobileMarkdownEditorApp = () => {
|
||||
const draft: MobileDraft = {
|
||||
title: titleRef.current,
|
||||
tagsText: tagsTextRef.current,
|
||||
body: markdownRef.current,
|
||||
contentJson: contentJsonRef.current,
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
localStorage.setItem(draftKey, JSON.stringify(draft));
|
||||
@@ -178,6 +140,61 @@ const MobileMarkdownEditorApp = () => {
|
||||
}
|
||||
}, [draftKey]);
|
||||
|
||||
const editor = useEditor({
|
||||
extensions: [
|
||||
StarterKit,
|
||||
Image.configure({
|
||||
allowBase64: false,
|
||||
inline: false,
|
||||
}),
|
||||
Placeholder.configure({
|
||||
placeholder: "开始记录...",
|
||||
}),
|
||||
],
|
||||
content: emptyDoc(),
|
||||
editorProps: {
|
||||
attributes: {
|
||||
class: "edgeever-mobile-tiptap-content",
|
||||
autocapitalize: "sentences",
|
||||
autocomplete: "on",
|
||||
autocorrect: "on",
|
||||
inputmode: "text",
|
||||
spellcheck: "true",
|
||||
},
|
||||
},
|
||||
onUpdate: ({ editor: activeEditor }) => {
|
||||
contentJsonRef.current = activeEditor.getJSON() as TiptapDoc;
|
||||
dirtyRef.current = true;
|
||||
persistLocalDraft();
|
||||
|
||||
if (saveStateRef.current !== "dirty") {
|
||||
setSaveStateStable("dirty");
|
||||
}
|
||||
|
||||
if (saveTimerRef.current !== null) {
|
||||
window.clearTimeout(saveTimerRef.current);
|
||||
}
|
||||
saveTimerRef.current = window.setTimeout(() => {
|
||||
saveTimerRef.current = null;
|
||||
void saveNowRef.current();
|
||||
}, AUTO_SAVE_DELAY_MS);
|
||||
},
|
||||
});
|
||||
|
||||
const saveNowRef = useRef<({ keepalive }?: { keepalive?: boolean }) => Promise<boolean>>(async () => false);
|
||||
|
||||
useEffect(() => {
|
||||
memoRef.current = memo;
|
||||
}, [memo]);
|
||||
|
||||
useEffect(() => {
|
||||
titleRef.current = title;
|
||||
}, [title]);
|
||||
|
||||
useEffect(() => {
|
||||
tagsTextRef.current = tagsText;
|
||||
}, [tagsText]);
|
||||
|
||||
const saveNow = useCallback(
|
||||
async ({ keepalive = false }: { keepalive?: boolean } = {}) => {
|
||||
const currentMemo = memoRef.current;
|
||||
@@ -200,17 +217,18 @@ const MobileMarkdownEditorApp = () => {
|
||||
}
|
||||
|
||||
savingRef.current = true;
|
||||
setSaveState("saving");
|
||||
setSaveStateStable("saving");
|
||||
setError(null);
|
||||
|
||||
currentSavePromiseRef.current = (async () => {
|
||||
const nextContentJson = contentJsonRef.current;
|
||||
const data = await requestJson<MemoResponse>(`/api/v1/memos/${encodeURIComponent(currentMemo.id)}`, {
|
||||
method: "PATCH",
|
||||
keepalive,
|
||||
body: JSON.stringify({
|
||||
expectedRevision: currentMemo.revision,
|
||||
title: titleRef.current,
|
||||
contentMarkdown: markdownRef.current,
|
||||
contentJson: nextContentJson,
|
||||
tags: parseTags(tagsTextRef.current),
|
||||
}),
|
||||
});
|
||||
@@ -221,10 +239,10 @@ const MobileMarkdownEditorApp = () => {
|
||||
if (draftKey) {
|
||||
localStorage.removeItem(draftKey);
|
||||
}
|
||||
setSaveState("saved");
|
||||
setSaveStateStable("saved");
|
||||
window.setTimeout(() => {
|
||||
if (!dirtyRef.current && !savingRef.current && !leavingRef.current) {
|
||||
setSaveState("idle");
|
||||
setSaveStateStable("idle");
|
||||
}
|
||||
}, 1200);
|
||||
return true;
|
||||
@@ -235,20 +253,24 @@ const MobileMarkdownEditorApp = () => {
|
||||
} catch (saveError) {
|
||||
persistLocalDraft();
|
||||
setError(saveError instanceof Error ? saveError.message : "保存失败,已保留本地草稿");
|
||||
setSaveState("error");
|
||||
setSaveStateStable("error");
|
||||
return false;
|
||||
} finally {
|
||||
savingRef.current = false;
|
||||
currentSavePromiseRef.current = null;
|
||||
}
|
||||
},
|
||||
[currentSnapshot, draftKey, persistLocalDraft]
|
||||
[currentSnapshot, draftKey, persistLocalDraft, setSaveStateStable]
|
||||
);
|
||||
|
||||
const scheduleSave = useCallback(() => {
|
||||
useEffect(() => {
|
||||
saveNowRef.current = saveNow;
|
||||
}, [saveNow]);
|
||||
|
||||
const scheduleMetadataSave = useCallback(() => {
|
||||
dirtyRef.current = true;
|
||||
persistLocalDraft();
|
||||
setSaveState("dirty");
|
||||
setSaveStateStable("dirty");
|
||||
|
||||
if (saveTimerRef.current !== null) {
|
||||
window.clearTimeout(saveTimerRef.current);
|
||||
@@ -257,7 +279,7 @@ const MobileMarkdownEditorApp = () => {
|
||||
saveTimerRef.current = null;
|
||||
void saveNow();
|
||||
}, AUTO_SAVE_DELAY_MS);
|
||||
}, [persistLocalDraft, saveNow]);
|
||||
}, [persistLocalDraft, saveNow, setSaveStateStable]);
|
||||
|
||||
const navigateBack = useCallback(() => {
|
||||
window.location.replace(returnTo);
|
||||
@@ -274,62 +296,33 @@ const MobileMarkdownEditorApp = () => {
|
||||
window.clearTimeout(saveTimerRef.current);
|
||||
saveTimerRef.current = null;
|
||||
}
|
||||
setSaveState("leaving");
|
||||
setSaveStateStable("leaving");
|
||||
|
||||
await Promise.race([
|
||||
saveNow({ keepalive: true }),
|
||||
new Promise((resolve) => window.setTimeout(resolve, LEAVE_SAVE_TIMEOUT_MS)),
|
||||
]);
|
||||
navigateBack();
|
||||
}, [navigateBack, persistLocalDraft, saveNow]);
|
||||
}, [navigateBack, persistLocalDraft, saveNow, setSaveStateStable]);
|
||||
|
||||
const handleTitleChange = (nextTitle: string) => {
|
||||
setTitle(nextTitle);
|
||||
titleRef.current = nextTitle;
|
||||
scheduleSave();
|
||||
scheduleMetadataSave();
|
||||
};
|
||||
|
||||
const handleTagsChange = (nextTagsText: string) => {
|
||||
setTagsText(nextTagsText);
|
||||
tagsTextRef.current = nextTagsText;
|
||||
scheduleSave();
|
||||
};
|
||||
|
||||
const handleMarkdownChange = (nextMarkdown?: string) => {
|
||||
const value = nextMarkdown ?? "";
|
||||
setMarkdown(value);
|
||||
markdownRef.current = value;
|
||||
scheduleSave();
|
||||
};
|
||||
|
||||
const handleUpload = async (file?: File | null) => {
|
||||
const currentMemo = memoRef.current;
|
||||
if (!currentMemo || !file) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSaveState("uploading");
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const data = await uploadResource(currentMemo.id, file);
|
||||
const insertion = `\n\n${markdownForResource(data.resource)}\n\n`;
|
||||
const nextMarkdown = insertAtCursor(markdownRef.current, insertion);
|
||||
setMarkdown(nextMarkdown);
|
||||
markdownRef.current = nextMarkdown;
|
||||
dirtyRef.current = true;
|
||||
persistLocalDraft();
|
||||
void saveNow();
|
||||
} catch (uploadError) {
|
||||
setError(uploadError instanceof Error ? uploadError.message : "上传失败");
|
||||
setSaveState("error");
|
||||
}
|
||||
scheduleMetadataSave();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!memoId) {
|
||||
setError("缺少 memoId");
|
||||
setSaveState("error");
|
||||
if (!memoId || !editor) {
|
||||
if (!memoId) {
|
||||
setError("缺少 memoId");
|
||||
setSaveStateStable("error");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -344,7 +337,7 @@ const MobileMarkdownEditorApp = () => {
|
||||
|
||||
const nextTitle = data.memo.title || "";
|
||||
const nextTagsText = Array.isArray(data.memo.tags) ? data.memo.tags.join(", ") : "";
|
||||
const nextMarkdown = data.memo.contentMarkdown || docToMarkdown(data.memo.contentJson as TiptapDoc);
|
||||
const nextContentJson = normalizeDoc(data.memo);
|
||||
const draft = readLocalDraft();
|
||||
const useDraft = Boolean(draft && Date.parse(draft.updatedAt || "") >= Date.parse(data.memo.updatedAt || ""));
|
||||
|
||||
@@ -355,45 +348,39 @@ const MobileMarkdownEditorApp = () => {
|
||||
titleRef.current = draft.title || "";
|
||||
setTagsText(draft.tagsText || "");
|
||||
tagsTextRef.current = draft.tagsText || "";
|
||||
setMarkdown(draft.body || "");
|
||||
markdownRef.current = draft.body || "";
|
||||
contentJsonRef.current = draft.contentJson || emptyDoc();
|
||||
editor.commands.setContent(contentJsonRef.current, { emitUpdate: false });
|
||||
dirtyRef.current = true;
|
||||
setSaveState("local-draft");
|
||||
scheduleSave();
|
||||
setSaveStateStable("local-draft");
|
||||
scheduleMetadataSave();
|
||||
} else {
|
||||
setTitle(nextTitle);
|
||||
titleRef.current = nextTitle;
|
||||
setTagsText(nextTagsText);
|
||||
tagsTextRef.current = nextTagsText;
|
||||
setMarkdown(nextMarkdown);
|
||||
markdownRef.current = nextMarkdown;
|
||||
contentJsonRef.current = nextContentJson;
|
||||
editor.commands.setContent(nextContentJson, { emitUpdate: false });
|
||||
lastSavedSnapshotRef.current = JSON.stringify({
|
||||
title: nextTitle,
|
||||
tagsText: nextTagsText,
|
||||
body: nextMarkdown,
|
||||
contentJson: nextContentJson,
|
||||
});
|
||||
dirtyRef.current = false;
|
||||
setSaveState("idle");
|
||||
}
|
||||
|
||||
for (const delay of [0, 120, 500]) {
|
||||
window.setTimeout(() => {
|
||||
document.querySelector<HTMLTextAreaElement>(".edgeever-mobile-md-editor textarea")?.focus();
|
||||
}, delay);
|
||||
setSaveStateStable("idle");
|
||||
}
|
||||
} catch (loadError) {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
setError(loadError instanceof Error ? loadError.message : "加载失败");
|
||||
setSaveState("error");
|
||||
setSaveStateStable("error");
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [memoId, readLocalDraft, scheduleSave]);
|
||||
}, [editor, memoId, readLocalDraft, scheduleMetadataSave, setSaveStateStable]);
|
||||
|
||||
useEffect(() => {
|
||||
window.history.replaceState({ edgeeverMobileEditor: true }, "");
|
||||
@@ -434,22 +421,22 @@ const MobileMarkdownEditorApp = () => {
|
||||
? "加载中"
|
||||
: saveState === "saving"
|
||||
? "保存中"
|
||||
: saveState === "uploading"
|
||||
? "上传中"
|
||||
: saveState === "dirty"
|
||||
? "未保存"
|
||||
: saveState === "saved"
|
||||
? "已保存"
|
||||
: saveState === "local-draft"
|
||||
? "本地草稿"
|
||||
: saveState === "leaving"
|
||||
? "返回中"
|
||||
: saveState === "error"
|
||||
? "保存失败"
|
||||
: "已保存";
|
||||
: saveState === "dirty"
|
||||
? "未保存"
|
||||
: saveState === "saved"
|
||||
? "已保存"
|
||||
: saveState === "local-draft"
|
||||
? "本地草稿"
|
||||
: saveState === "leaving"
|
||||
? "返回中"
|
||||
: saveState === "error"
|
||||
? "保存失败"
|
||||
: "已保存";
|
||||
|
||||
const statusClassName =
|
||||
saveState === "error" ? "error" : saveState === "dirty" || saveState === "saving" || saveState === "uploading" || saveState === "leaving" ? "active" : "";
|
||||
saveState === "error" ? "error" : saveState === "dirty" || saveState === "saving" || saveState === "leaving" ? "active" : "";
|
||||
|
||||
const fallbackMarkdown = memo ? docToMarkdown(contentJsonRef.current) : "";
|
||||
|
||||
return (
|
||||
<div className="mobile-editor-shell">
|
||||
@@ -486,76 +473,16 @@ const MobileMarkdownEditorApp = () => {
|
||||
onChange={(event) => handleTagsChange(event.target.value)}
|
||||
/>
|
||||
|
||||
<div className="mobile-editor-tool-row">
|
||||
<button type="button" onClick={() => imageInputRef.current?.click()} disabled={!memo || saveState === "uploading"}>
|
||||
图片
|
||||
</button>
|
||||
<button type="button" onClick={() => fileInputRef.current?.click()} disabled={!memo || saveState === "uploading"}>
|
||||
附件
|
||||
</button>
|
||||
<div className="mobile-editor-preview-toggle" role="group" aria-label="编辑预览切换">
|
||||
<button type="button" aria-pressed={preview === "edit"} onClick={() => setPreview("edit")}>
|
||||
编辑
|
||||
</button>
|
||||
<button type="button" aria-pressed={preview === "preview"} onClick={() => setPreview("preview")}>
|
||||
预览
|
||||
</button>
|
||||
</div>
|
||||
<div className="edgeever-mobile-tiptap-editor">
|
||||
<EditorContent editor={editor} />
|
||||
</div>
|
||||
<input
|
||||
ref={imageInputRef}
|
||||
type="file"
|
||||
accept="image/png,image/jpeg,image/gif,image/webp,image/avif"
|
||||
hidden
|
||||
onChange={(event) => {
|
||||
const file = event.target.files?.[0];
|
||||
event.target.value = "";
|
||||
void handleUpload(file);
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
hidden
|
||||
onChange={(event) => {
|
||||
const file = event.target.files?.[0];
|
||||
event.target.value = "";
|
||||
void handleUpload(file);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="edgeever-mobile-md-editor" data-color-mode="light">
|
||||
<MDEditor
|
||||
value={markdown}
|
||||
preview={preview}
|
||||
height="calc(100dvh - 15.75rem)"
|
||||
minHeight={360}
|
||||
visibleDragbar={false}
|
||||
enableScroll
|
||||
autoFocus
|
||||
autoFocusEnd
|
||||
textareaProps={{
|
||||
autoComplete: "on",
|
||||
autoCorrect: "on",
|
||||
inputMode: "text",
|
||||
enterKeyHint: "enter",
|
||||
spellCheck: true,
|
||||
placeholder: "开始记录...",
|
||||
"aria-label": "笔记正文",
|
||||
}}
|
||||
commands={[
|
||||
commands.bold,
|
||||
commands.italic,
|
||||
commands.heading,
|
||||
commands.quote,
|
||||
commands.unorderedListCommand,
|
||||
commands.orderedListCommand,
|
||||
commands.link,
|
||||
]}
|
||||
extraCommands={[]}
|
||||
onChange={handleMarkdownChange}
|
||||
/>
|
||||
</div>
|
||||
{saveState === "error" && fallbackMarkdown && (
|
||||
<details className="mobile-editor-fallback">
|
||||
<summary>查看当前正文 Markdown 备份</summary>
|
||||
<pre>{fallbackMarkdown}</pre>
|
||||
</details>
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
@@ -569,6 +496,6 @@ if (!root) {
|
||||
|
||||
createRoot(root).render(
|
||||
<React.StrictMode>
|
||||
<MobileMarkdownEditorApp />
|
||||
<MobileTiptapEditorApp />
|
||||
</React.StrictMode>
|
||||
);
|
||||
@@ -24,8 +24,7 @@ body {
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
textarea {
|
||||
input {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
@@ -49,9 +48,7 @@ textarea {
|
||||
}
|
||||
|
||||
.mobile-editor-back,
|
||||
.mobile-editor-done,
|
||||
.mobile-editor-tool-row button,
|
||||
.mobile-editor-preview-toggle button {
|
||||
.mobile-editor-done {
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: transparent;
|
||||
@@ -82,8 +79,7 @@ textarea {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.mobile-editor-done:disabled,
|
||||
.mobile-editor-tool-row button:disabled {
|
||||
.mobile-editor-done:disabled {
|
||||
background: #e2e8f0;
|
||||
color: #64748b;
|
||||
}
|
||||
@@ -152,81 +148,72 @@ textarea {
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.mobile-editor-tool-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
.edgeever-mobile-tiptap-editor {
|
||||
min-height: calc(100dvh - 14rem);
|
||||
border-top: 1px solid #f1f5f9;
|
||||
margin: 4px -12px 0;
|
||||
}
|
||||
|
||||
.mobile-editor-tool-row > button {
|
||||
border: 1px solid #cbd5e1;
|
||||
background: #fff;
|
||||
color: #334155;
|
||||
font-size: 14px;
|
||||
min-height: 34px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.mobile-editor-preview-toggle {
|
||||
display: inline-flex;
|
||||
overflow: hidden;
|
||||
border: 1px solid #cbd5e1;
|
||||
border-radius: 999px;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.mobile-editor-preview-toggle button {
|
||||
border-radius: 0;
|
||||
color: #475569;
|
||||
font-size: 14px;
|
||||
min-height: 34px;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
.mobile-editor-preview-toggle button[aria-pressed="true"] {
|
||||
background: #0f172a;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.edgeever-mobile-md-editor .w-md-editor {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.edgeever-mobile-md-editor .w-md-editor-toolbar {
|
||||
border-color: #e2e8f0;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.edgeever-mobile-md-editor .w-md-editor-toolbar li > button {
|
||||
height: 30px;
|
||||
min-width: 30px;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.edgeever-mobile-md-editor .w-md-editor-text-input,
|
||||
.edgeever-mobile-md-editor .w-md-editor-text-pre > code,
|
||||
.edgeever-mobile-md-editor .w-md-editor-text-pre {
|
||||
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important;
|
||||
font-size: 17px !important;
|
||||
line-height: 1.65 !important;
|
||||
}
|
||||
|
||||
.edgeever-mobile-md-editor .w-md-editor-text-input {
|
||||
caret-color: auto;
|
||||
}
|
||||
|
||||
.edgeever-mobile-md-editor .wmde-markdown {
|
||||
background: #fff;
|
||||
.edgeever-mobile-tiptap-content {
|
||||
min-height: calc(100dvh - 14rem);
|
||||
outline: none;
|
||||
padding: 18px 12px 32px;
|
||||
color: #0f172a;
|
||||
font-size: 16px;
|
||||
line-height: 1.65;
|
||||
font-size: 17px;
|
||||
line-height: 1.7;
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
caret-color: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.edgeever-mobile-md-editor .wmde-markdown img {
|
||||
.edgeever-mobile-tiptap-content p {
|
||||
margin: 0 0 0.85em;
|
||||
}
|
||||
|
||||
.edgeever-mobile-tiptap-content h1,
|
||||
.edgeever-mobile-tiptap-content h2,
|
||||
.edgeever-mobile-tiptap-content h3 {
|
||||
line-height: 1.25;
|
||||
margin: 0.9em 0 0.45em;
|
||||
}
|
||||
|
||||
.edgeever-mobile-tiptap-content ul,
|
||||
.edgeever-mobile-tiptap-content ol {
|
||||
margin: 0 0 0.85em;
|
||||
padding-left: 1.4em;
|
||||
}
|
||||
|
||||
.edgeever-mobile-tiptap-content blockquote {
|
||||
border-left: 3px solid #cbd5e1;
|
||||
color: #475569;
|
||||
margin: 0 0 0.85em;
|
||||
padding-left: 0.9em;
|
||||
}
|
||||
|
||||
.edgeever-mobile-tiptap-content img {
|
||||
height: auto;
|
||||
max-width: 100%;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.edgeever-mobile-tiptap-content p.is-editor-empty:first-child::before {
|
||||
content: attr(data-placeholder);
|
||||
float: left;
|
||||
height: 0;
|
||||
color: #94a3b8;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mobile-editor-fallback {
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
color: #475569;
|
||||
font-size: 13px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.mobile-editor-fallback pre {
|
||||
overflow-x: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
@@ -21,7 +21,6 @@
|
||||
"@tiptap/extension-placeholder": "latest",
|
||||
"@tiptap/react": "latest",
|
||||
"@tiptap/starter-kit": "latest",
|
||||
"@uiw/react-md-editor": "^4.1.1",
|
||||
"class-variance-authority": "latest",
|
||||
"clsx": "latest",
|
||||
"cmdk": "^1.1.1",
|
||||
@@ -856,8 +855,6 @@
|
||||
|
||||
"@types/node": ["@types/node@26.0.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw=="],
|
||||
|
||||
"@types/prismjs": ["@types/prismjs@1.26.6", "", {}, "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw=="],
|
||||
|
||||
"@types/react": ["@types/react@19.2.17", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw=="],
|
||||
|
||||
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
|
||||
@@ -876,12 +873,6 @@
|
||||
|
||||
"@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="],
|
||||
|
||||
"@uiw/copy-to-clipboard": ["@uiw/copy-to-clipboard@1.0.21", "", {}, "sha512-apdzZJyJC/IEj21N22ry1H022pgpSA+FwNKxmvOGQ9rUMdyRbHf1nZq2UwqnfGOuTr7iOKLzNgWsCJtAwyAZpw=="],
|
||||
|
||||
"@uiw/react-markdown-preview": ["@uiw/react-markdown-preview@5.2.1", "", { "dependencies": { "@babel/runtime": "^7.17.2", "@uiw/copy-to-clipboard": "~1.0.12", "react-markdown": "~10.1.0", "rehype-attr": "~4.0.0", "rehype-autolink-headings": "~7.1.0", "rehype-ignore": "^2.0.0", "rehype-prism-plus": "~2.0.0", "rehype-raw": "^7.0.0", "rehype-rewrite": "~4.0.0", "rehype-slug": "~6.0.0", "remark-gfm": "~4.0.0", "remark-github-blockquote-alert": "^1.0.0", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-JjvcHveT6glhlJYJx1XGBZij6wkw+VwREV6Z6m/GpsjPPdLjF1x8nlPBSB/ATyUF4lD7C8ttMkCqVH9N9XMgEA=="],
|
||||
|
||||
"@uiw/react-md-editor": ["@uiw/react-md-editor@4.1.1", "", { "dependencies": { "@babel/runtime": "^7.14.6", "@uiw/react-markdown-preview": "^5.2.0", "rehype": "~13.0.0", "rehype-prism-plus": "~2.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-yZqV5twN/sSfpce4cO/1bqy16o7v2oW324VNh2gcnsSpzOr2jEHpchM+ElD1y+ivUmFXcDZ5Ky5TOuPZg4qL6g=="],
|
||||
|
||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.2", "", {}, "sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA=="],
|
||||
|
||||
"@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.3", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg=="],
|
||||
@@ -976,8 +967,6 @@
|
||||
|
||||
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.40", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw=="],
|
||||
|
||||
"bcp-47-match": ["bcp-47-match@2.0.3", "", {}, "sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ=="],
|
||||
|
||||
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
||||
|
||||
"blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="],
|
||||
@@ -1078,8 +1067,6 @@
|
||||
|
||||
"css-select": ["css-select@5.2.2", "", { "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" } }, "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw=="],
|
||||
|
||||
"css-selector-parser": ["css-selector-parser@3.3.0", "", {}, "sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g=="],
|
||||
|
||||
"css-tree": ["css-tree@3.2.1", "", { "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" } }, "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA=="],
|
||||
|
||||
"css-what": ["css-what@6.2.2", "", {}, "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA=="],
|
||||
@@ -1128,8 +1115,6 @@
|
||||
|
||||
"diff": ["diff@8.0.4", "", {}, "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw=="],
|
||||
|
||||
"direction": ["direction@2.0.1", "", { "bin": { "direction": "cli.js" } }, "sha512-9S6m9Sukh1cZNknO1CWAr2QAWsbKLafQiyM5gZ7VgXHeuaoUwffKN4q6NC4A/Mf9iiPlOXQEKW/Mv/mh9/3YFA=="],
|
||||
|
||||
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
|
||||
|
||||
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||
@@ -1316,18 +1301,12 @@
|
||||
|
||||
"hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="],
|
||||
|
||||
"hast-util-has-property": ["hast-util-has-property@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA=="],
|
||||
|
||||
"hast-util-heading-rank": ["hast-util-heading-rank@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA=="],
|
||||
|
||||
"hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="],
|
||||
|
||||
"hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="],
|
||||
|
||||
"hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="],
|
||||
|
||||
"hast-util-select": ["hast-util-select@6.0.4", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "bcp-47-match": "^2.0.0", "comma-separated-tokens": "^2.0.0", "css-selector-parser": "^3.0.0", "devlop": "^1.0.0", "direction": "^2.0.0", "hast-util-has-property": "^3.0.0", "hast-util-to-string": "^3.0.0", "hast-util-whitespace": "^3.0.0", "nth-check": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-RqGS1ZgI0MwxLaKLDxjprynNzINEkRHY2i8ln4DDjgv9ZhcYVIHN9rlpiYsqtFwrgpYU361SyWDQcGNIBVu3lw=="],
|
||||
|
||||
"hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-attach-comments": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="],
|
||||
|
||||
"hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
|
||||
@@ -1336,8 +1315,6 @@
|
||||
|
||||
"hast-util-to-parse5": ["hast-util-to-parse5@8.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA=="],
|
||||
|
||||
"hast-util-to-string": ["hast-util-to-string@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A=="],
|
||||
|
||||
"hast-util-to-text": ["hast-util-to-text@4.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "hast-util-is-element": "^3.0.0", "unist-util-find-after": "^5.0.0" } }, "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A=="],
|
||||
|
||||
"hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
|
||||
@@ -1350,8 +1327,6 @@
|
||||
|
||||
"html-parse-stringify": ["html-parse-stringify@3.0.1", "", { "dependencies": { "void-elements": "3.1.0" } }, "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg=="],
|
||||
|
||||
"html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="],
|
||||
|
||||
"html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
|
||||
|
||||
"htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="],
|
||||
@@ -1730,8 +1705,6 @@
|
||||
|
||||
"parse-latin": ["parse-latin@7.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0", "@types/unist": "^3.0.0", "nlcst-to-string": "^4.0.0", "unist-util-modify-children": "^4.0.0", "unist-util-visit-children": "^3.0.0", "vfile": "^6.0.0" } }, "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ=="],
|
||||
|
||||
"parse-numeric-range": ["parse-numeric-range@1.3.0", "", {}, "sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ=="],
|
||||
|
||||
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
|
||||
|
||||
"parse5-htmlparser2-tree-adapter": ["parse5-htmlparser2-tree-adapter@7.1.0", "", { "dependencies": { "domhandler": "^5.0.3", "parse5": "^7.0.0" } }, "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g=="],
|
||||
@@ -1840,8 +1813,6 @@
|
||||
|
||||
"react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
||||
|
||||
"react-markdown": ["react-markdown@10.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ=="],
|
||||
|
||||
"react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="],
|
||||
|
||||
"react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="],
|
||||
@@ -1866,8 +1837,6 @@
|
||||
|
||||
"reflect.getprototypeof": ["reflect.getprototypeof@1.0.10", "", { "dependencies": { "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.9", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.7", "get-proto": "^1.0.1", "which-builtin-type": "^1.2.1" } }, "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw=="],
|
||||
|
||||
"refractor": ["refractor@5.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/prismjs": "^1.0.0", "hastscript": "^9.0.0", "parse-entities": "^4.0.0" } }, "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw=="],
|
||||
|
||||
"regenerate": ["regenerate@1.4.2", "", {}, "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A=="],
|
||||
|
||||
"regenerate-unicode-properties": ["regenerate-unicode-properties@10.2.2", "", { "dependencies": { "regenerate": "^1.4.2" } }, "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g=="],
|
||||
@@ -1888,30 +1857,16 @@
|
||||
|
||||
"rehype": ["rehype@13.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "rehype-parse": "^9.0.0", "rehype-stringify": "^10.0.0", "unified": "^11.0.0" } }, "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A=="],
|
||||
|
||||
"rehype-attr": ["rehype-attr@4.0.2", "", { "dependencies": { "unified": "~11.0.0", "unist-util-visit": "~5.0.0" } }, "sha512-v4+gw7pvUVLbG/dUpLgBE6r3TWTBYJ7z+sfAH3zapmM5CKzk5+CopFQgr4gMR6OBSKl/qpI6HR7gv1Cbig0uow=="],
|
||||
|
||||
"rehype-autolink-headings": ["rehype-autolink-headings@7.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-is-element": "^3.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw=="],
|
||||
|
||||
"rehype-ignore": ["rehype-ignore@2.0.3", "", { "dependencies": { "hast-util-select": "^6.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-IzhP6/u/6sm49sdktuYSmeIuObWB+5yC/5eqVws8BhuGA9kY25/byz6uCy/Ravj6lXUShEd2ofHM5MyAIj86Sg=="],
|
||||
|
||||
"rehype-parse": ["rehype-parse@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="],
|
||||
|
||||
"rehype-prism-plus": ["rehype-prism-plus@2.0.2", "", { "dependencies": { "hast-util-to-string": "^3.0.1", "parse-numeric-range": "^1.3.0", "refractor": "^5.0.0", "rehype-parse": "^9.0.1", "unist-util-filter": "^5.0.1", "unist-util-visit": "^5.1.0" } }, "sha512-jTHb8ZtQHd2VWAAKeCINgv/8zNEF0+LesmwJak69GemoPVN9/8fGEARTvqOpKqmN57HwaM9z8UKBVNVJe8zggw=="],
|
||||
|
||||
"rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="],
|
||||
|
||||
"rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="],
|
||||
|
||||
"rehype-rewrite": ["rehype-rewrite@4.0.4", "", { "dependencies": { "hast-util-select": "^6.0.0", "unified": "^11.0.3", "unist-util-visit": "^5.0.0" } }, "sha512-L/FO96EOzSA6bzOam4DVu61/PB3AGKcSPXpa53yMIozoxH4qg1+bVZDF8zh1EsuxtSauAhzt5cCnvoplAaSLrw=="],
|
||||
|
||||
"rehype-slug": ["rehype-slug@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "github-slugger": "^2.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-to-string": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A=="],
|
||||
|
||||
"rehype-stringify": ["rehype-stringify@10.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-to-html": "^9.0.0", "unified": "^11.0.0" } }, "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA=="],
|
||||
|
||||
"remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
|
||||
|
||||
"remark-github-blockquote-alert": ["remark-github-blockquote-alert@1.3.1", "", { "dependencies": { "unist-util-visit": "^5.0.0" } }, "sha512-OPNnimcKeozWN1w8KVQEuHOxgN3L4rah8geMOLhA5vN9wITqU4FWD+G26tkEsCGHiOVDbISx+Se5rGZ+D1p0Jg=="],
|
||||
|
||||
"remark-mdx": ["remark-mdx@3.1.1", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Pjj2IYlUY3+D8x00UJsIOg5BEvfMyeI+2uLPn9VO9Wg4MEtN/VTIq2NEJQfde9PnX15KgtHyl9S0BcTnWrIuWg=="],
|
||||
|
||||
"remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="],
|
||||
@@ -2134,8 +2089,6 @@
|
||||
|
||||
"unique-string": ["unique-string@2.0.0", "", { "dependencies": { "crypto-random-string": "^2.0.0" } }, "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg=="],
|
||||
|
||||
"unist-util-filter": ["unist-util-filter@5.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-pHx7D4Zt6+TsfwylH9+lYhBhzyhEnCXs/lbq/Hstxno5z4gVdyc2WEW0asfjGKPyG4pEKrnBv5hdkO6+aRnQJw=="],
|
||||
|
||||
"unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="],
|
||||
|
||||
"unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="],
|
||||
@@ -2428,8 +2381,6 @@
|
||||
|
||||
"readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
|
||||
"rehype-attr/unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="],
|
||||
|
||||
"sitemap/@types/node": ["@types/node@24.13.2", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA=="],
|
||||
|
||||
"source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
@@ -58,7 +58,6 @@
|
||||
"@tiptap/extension-placeholder": "latest",
|
||||
"@tiptap/react": "latest",
|
||||
"@tiptap/starter-kit": "latest",
|
||||
"@uiw/react-md-editor": "^4.1.1",
|
||||
"class-variance-authority": "latest",
|
||||
"clsx": "latest",
|
||||
"cmdk": "^1.1.1",
|
||||
|
||||
Reference in new issue
Block a user