diff --git a/apps/mobile/src/screens/WorkspaceScreen.tsx b/apps/mobile/src/screens/WorkspaceScreen.tsx index 3d9c69b2..aca35796 100644 --- a/apps/mobile/src/screens/WorkspaceScreen.tsx +++ b/apps/mobile/src/screens/WorkspaceScreen.tsx @@ -39,6 +39,7 @@ import { Image as RNImage, Linking, Modal, + Platform, Pressable, RefreshControl, ScrollView, @@ -53,6 +54,7 @@ import { useSession } from "../lib/session"; const ALL_NOTES_ID = "all"; const DEFAULT_MEMO_TITLE = "无标题笔记"; +const MOBILE_APP_VERSION = "0.1.2"; const MEMO_TEMPLATES: MemoTemplate[] = [ { id: "quick-note", @@ -128,6 +130,8 @@ export const WorkspaceScreen = () => { const [tagsManagerOpen, setTagsManagerOpen] = useState(false); const [resourcesOpen, setResourcesOpen] = useState(false); const [apiTokensOpen, setApiTokensOpen] = useState(false); + const [evernoteGuideOpen, setEvernoteGuideOpen] = useState(false); + const [systemInfoOpen, setSystemInfoOpen] = useState(false); const [revisionMemo, setRevisionMemo] = useState(null); const [selectedMemoIds, setSelectedMemoIds] = useState>(() => new Set()); const [selectionMoveOpen, setSelectionMoveOpen] = useState(false); @@ -487,8 +491,10 @@ export const WorkspaceScreen = () => { notebookCount={notebooks.length} memoCount={memoCount} onOpenApiTokens={() => setApiTokensOpen(true)} + onOpenEvernoteGuide={() => setEvernoteGuideOpen(true)} onOpenNotebookManager={() => setNotebookManagerOpen(true)} onOpenResources={() => setResourcesOpen(true)} + onOpenSystemInfo={() => setSystemInfoOpen(true)} onOpenTagsManager={() => setTagsManagerOpen(true)} onOpenTemplates={() => setTemplatesOpen(true)} /> @@ -525,6 +531,8 @@ export const WorkspaceScreen = () => { setTagsManagerOpen(false)} visible={tagsManagerOpen} /> setResourcesOpen(false)} visible={resourcesOpen} /> setApiTokensOpen(false)} visible={apiTokensOpen} /> + setEvernoteGuideOpen(false)} visible={evernoteGuideOpen} /> + setSystemInfoOpen(false)} visible={systemInfoOpen} /> setRevisionMemo(null)} @@ -847,16 +855,20 @@ const SettingsView = ({ memoCount, notebookCount, onOpenApiTokens, + onOpenEvernoteGuide, onOpenNotebookManager, onOpenResources, + onOpenSystemInfo, onOpenTagsManager, onOpenTemplates, }: { memoCount: number; notebookCount: number; onOpenApiTokens: () => void; + onOpenEvernoteGuide: () => void; onOpenNotebookManager: () => void; onOpenResources: () => void; + onOpenSystemInfo: () => void; onOpenTagsManager: () => void; onOpenTemplates: () => void; }) => ( @@ -877,6 +889,12 @@ const SettingsView = ({ + + + + + + @@ -1642,6 +1660,119 @@ const ApiTokenRow = ({ ); }; +const EvernoteGuideModal = ({ onClose, visible }: { onClose: () => void; visible: boolean }) => ( + + + + + + + Evernote 导入指引 + + + + + + + 推荐通过 AI 编程助手 + EdgeEver MCP 自动迁移 + 该流程用于从 Evernote/印象笔记导出 ENEX,并通过 MCP 批量导入 EdgeEver。 + + + + + + + + + 可直接复制给 AI 助手的 Prompt + + 你是 AI 编程助手。请帮我把本地的印象笔记全量迁移到我当前部署的 EdgeEver 实例中:{"\n"} + 1. 检查并使用 `pipx install evernote-backup` 自动安装备份工具。{"\n"} + 2. 提示我输入印象笔记的用户名和密码并初始化数据库,随后同步数据并导出到 `./evernote-export`。{"\n"} + 3. 从 GitHub 下载最新版迁移脚本 `scripts/import-evernote-enex-via-mcp.mjs`。{"\n"} + 4. 安装 `sharp` 和 `fast-xml-parser`。{"\n"} + 5. 使用已配置的 MCP URL 和 Token 运行脚本完成迁移。 + + + + + +); + +const GuideStep = ({ body, title }: { body: string; title: string }) => ( + + {title} + {body} + +); + +const SystemInfoModal = ({ + baseUrl, + memoCount, + notebookCount, + onClose, + visible, +}: { + baseUrl: string; + memoCount: number; + notebookCount: number; + onClose: () => void; + visible: boolean; +}) => { + const [copied, setCopied] = useState(false); + const infoItems = [ + { label: "版本", value: `v${MOBILE_APP_VERSION}` }, + { label: "平台", value: Platform.OS }, + { label: "平台版本", value: String(Platform.Version) }, + { label: "实例地址", value: baseUrl || "未连接" }, + { label: "笔记本数量", value: String(notebookCount) }, + { label: "笔记总数", value: String(memoCount) }, + { label: "时区", value: Intl.DateTimeFormat().resolvedOptions().timeZone || "未知" }, + { label: "语言", value: Intl.DateTimeFormat().resolvedOptions().locale || "未知" }, + ]; + + const copySystemInfo = async () => { + await Clipboard.setStringAsync(infoItems.map((item) => `${item.label}: ${item.value}`).join("\n")); + setCopied(true); + setTimeout(() => setCopied(false), 1600); + }; + + return ( + + + + + + + 系统信息 + + {copied ? : } + + + + + 用于排查客户端、实例连接和多端环境问题。 + {infoItems.map((item) => ( + + ))} + + + + ); +}; + type ResourceFilter = "all" | "image" | "document" | "other"; const DOCUMENT_MIME_TYPES = new Set([ @@ -3324,6 +3455,22 @@ const styles = StyleSheet.create({ gap: 8, padding: 22, }, + guideHero: { + backgroundColor: "#ecfdf5", + borderColor: "#a7f3d0", + borderRadius: 8, + borderWidth: 1, + gap: 8, + padding: 14, + }, + guideStep: { + backgroundColor: "#ffffff", + borderColor: "#e2e8f0", + borderRadius: 8, + borderWidth: 1, + gap: 6, + padding: 14, + }, templateCard: { alignItems: "center", backgroundColor: "#ffffff",