├── .node-version ├── versions.json ├── manifest.json ├── .gitignore ├── tsconfig.json ├── scripts ├── translate_commit.py └── update-version.js ├── esbuild.config.mjs ├── src ├── lang │ ├── locale │ │ ├── zh-tw.ts │ │ ├── zh-cn.ts │ │ ├── ja.ts │ │ ├── ko.ts │ │ ├── he.ts │ │ ├── th.ts │ │ ├── ar.ts │ │ ├── en.ts │ │ ├── tr.ts │ │ ├── vi.ts │ │ ├── da.ts │ │ ├── nb.ts │ │ ├── ms.ts │ │ ├── pl.ts │ │ ├── nl.ts │ │ ├── id.ts │ │ ├── ne.ts │ │ ├── pt-br.ts │ │ ├── be.ts │ │ ├── pt.ts │ │ ├── sq.ts │ │ ├── uk.ts │ │ ├── de.ts │ │ ├── es.ts │ │ ├── ru.ts │ │ ├── ro.ts │ │ ├── it.ts │ │ ├── hu.ts │ │ ├── ca.ts │ │ └── fr.ts │ └── lang.ts ├── lib │ ├── helps.ts │ ├── websocket.ts │ ├── fs.ts │ └── icons.ts ├── styles.scss ├── views │ └── settings-view.tsx ├── main.ts └── setting.tsx ├── package.json ├── styles.css ├── docs └── README.zh-CN.md ├── update-version.js ├── README.md ├── .github └── workflows │ ├── pre-release.yml │ └── release.yml ├── LICENSE └── pnpm-lock.yaml /.node-version: -------------------------------------------------------------------------------- 1 | v22.11.0 -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "0.6.0": "1.4.5" 3 | } -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "fast-note-sync", 3 | "name": "Fast Note Sync", 4 | "version": "0.6.26", 5 | "minAppVersion": "1.6.5", 6 | "description": "Privately deployable real-time multi-device note synchronization, supporting desktop and mobile devices.", 7 | "author": "HaierKeys", 8 | "authorUrl": "https://blog.diybeta.com", 9 | "isDesktopOnly": false, 10 | "fundingUrl": "https://ko-fi.com/haierkeys" 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Intellij 5 | *.iml 6 | .idea 7 | 8 | # npm 9 | node_modules 10 | 11 | # Don't include the compiled main.js file in the repo. 12 | # They should be uploaded to GitHub releases instead. 13 | main.js 14 | 15 | # Exclude sourcemaps 16 | *.map 17 | 18 | # obsidian 19 | data.json 20 | 21 | # Exclude macOS Finder (System Explorer) View States 22 | .DS_Store 23 | /src/dist 24 | /src/views/dist 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "allowSyntheticDefaultImports": true, 5 | "baseUrl": ".", 6 | "importHelpers": true, 7 | "inlineSourceMap": true, 8 | "inlineSources": true, 9 | "isolatedModules": true, 10 | "jsx": "react-jsx", 11 | "lib": ["DOM", "ES5", "ES6", "ES7", "ESNext"], 12 | "module": "ESNext", 13 | "moduleResolution": "node", 14 | "noImplicitAny": true, 15 | "strictNullChecks": true, 16 | "target": "ES6" 17 | }, 18 | "include": ["**/*.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /scripts/translate_commit.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from deep_translator import GoogleTranslator 4 | 5 | def main(): 6 | # Get message from environment variable or command line argument 7 | msg = os.environ.get('COMMIT_MSG', '') 8 | if len(sys.argv) > 1: 9 | msg = sys.argv[1] 10 | 11 | if not msg: 12 | print("No commit message found.") 13 | return 14 | 15 | try: 16 | # Translate to Chinese 17 | zh_trans = GoogleTranslator(source='auto', target='zh-CN').translate(msg) 18 | # Translate to English 19 | en_trans = GoogleTranslator(source='auto', target='en').translate(msg) 20 | 21 | # Output in the requested format: Chinese \n English 22 | # We ensure they are not identical to avoid duplication if source was already one of them 23 | # But user explicitly asked for "Chinese and English", so we output both. 24 | 25 | print(f"{zh_trans}") 26 | print(f"{en_trans}") 27 | 28 | except Exception as e: 29 | print(f"Translation failed: {e}") 30 | # Fallback to original message 31 | print(msg) 32 | 33 | if __name__ == "__main__": 34 | main() 35 | -------------------------------------------------------------------------------- /esbuild.config.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | import process from "process"; 3 | import builtins from "builtin-modules"; 4 | 5 | const banner = 6 | `/* 7 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 8 | if you want to view the source, please visit the github repository of this plugin 9 | */ 10 | `; 11 | 12 | const prod = (process.argv[2] === "production"); 13 | 14 | const context = await esbuild.context({ 15 | banner: { 16 | js: banner, 17 | }, 18 | entryPoints: ["src/main.ts"], 19 | bundle: true, 20 | external: [ 21 | "obsidian", 22 | "electron", 23 | "@codemirror/autocomplete", 24 | "@codemirror/collab", 25 | "@codemirror/commands", 26 | "@codemirror/language", 27 | "@codemirror/lint", 28 | "@codemirror/search", 29 | "@codemirror/state", 30 | "@codemirror/view", 31 | "@lezer/common", 32 | "@lezer/highlight", 33 | "@lezer/lr", 34 | ...builtins], 35 | format: "cjs", 36 | target: "es2018", 37 | logLevel: "info", 38 | sourcemap: prod ? false : "inline", 39 | treeShaking: true, 40 | outfile: "main.js", 41 | minify: prod, 42 | }); 43 | 44 | if (prod) { 45 | await context.rebuild(); 46 | process.exit(0); 47 | } else { 48 | await context.watch(); 49 | } 50 | -------------------------------------------------------------------------------- /src/lang/locale/zh-tw.ts: -------------------------------------------------------------------------------- 1 | // 繁体中文 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const zh_tw: Partial = { 6 | "Fast sync": "專注為使用者提供無打擾、絲般順滑、多端即時同步的筆記同步插件,支持 Mac、Windows、Android、iOS 等平台,並提供多語言支持。", 7 | "同步全部笔记(覆盖远端)": "同步全部筆記(覆蓋遠端)", 8 | "同步全部笔记": "同步全部筆記", 9 | "远端": "遠端", 10 | "接口配置信息已经粘贴到设置中!": "介面配置信息已經粘貼到設置中!", 11 | "未检测到配置信息!": "未檢測到配置信息!", 12 | "远端服务搭建与选择": "遠端服務搭建與選擇", 13 | "选择一个适合自己的远端": "選擇一個適合自己的遠端", 14 | "方式": "方式", 15 | "说明": "說明", 16 | "详情参考": "詳情參考", 17 | "私有服务搭建": "私人服務建置", 18 | "速度好, 自由配置, 无隐私风险": "速度好,自由配置,無隱私風險", 19 | "粘贴的远端配置": "黏貼的遠端配置", 20 | "启用同步": "啟用同步", 21 | "关闭后您的笔记将不做任何同步": "關閉後您的筆記將不做任何同步", 22 | "远端服务地址": "遠端服務地址", 23 | "选择一个 Fast note sync service 服务地址": "選擇一個 Fast note sync service 服務地址", 24 | "输入您的 Fast note sync service 服务地址": "輸入您的 Image API Gateway 地址", 25 | "远端服务令牌": "遠端服務令牌", 26 | "用于远端服务的访问授权令牌": "用於遠端服務的訪問授權令牌", 27 | "输入您的 API 访问令牌": "請輸入您的 API 訪問令牌", 28 | "远端仓库名": "遠端倉庫名", 29 | "支持": "支持", 30 | "捐赠": "捐贈", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "如果您喜歡這個插件,請考慮捐贈以支持繼續開發。", 32 | "Buy me a coffee at ko-fi.com": "在 ko-fi.com 請我喝杯咖啡。", 33 | "复制 Debug 信息": "複製 Debug 信息", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "將調試信息複製到剪貼板,可能包含敏感信!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "透過快捷鍵開啟控制台,你可以看到這個插件和其他插件的日誌", 36 | } 37 | 38 | 39 | export default zh_tw; 40 | -------------------------------------------------------------------------------- /src/lang/locale/zh-cn.ts: -------------------------------------------------------------------------------- 1 | // 简体中文 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const zh_cn: Partial = { 6 | "Fast sync": "可私有化部署,专注为用户提供无打扰、丝般顺滑、多端实时同步的笔记同步插件,支持 Mac、Windows、Android、iOS 等平台,并提供多语言支持。", 7 | "同步全部笔记(覆盖远端)": "同步全部笔记(覆盖远端)", 8 | "同步全部笔记": "同步全部笔记", 9 | "远端": "远端", 10 | "接口配置信息已经粘贴到设置中!": "接口配置信息已经粘贴到设置中!", 11 | "未检测到配置信息!": "未检测到配置信息!", 12 | "远端服务搭建与选择": "远端服务搭建与选择", 13 | "选择一个适合自己的远端": "选择一个适合自己的远端", 14 | "方式": "方式", 15 | "说明": "说明", 16 | "详情参考": "详情参考", 17 | "私有服务搭建": "私有服务搭建", 18 | "速度好, 自由配置, 无隐私风险": "速度好, 自由配置, 无隐私风险", 19 | "粘贴的远端配置": "粘贴的远端配置", 20 | "启用同步": "启用同步", 21 | "关闭后您的笔记将不做任何同步": "关闭后您的笔记将不做任何同步", 22 | "远端服务地址": "远端服务地址", 23 | "选择一个 Fast note sync service 服务地址": "选择一个 Fast note sync service 服务地址", 24 | "输入您的 Fast note sync service 服务地址": "输入您的 Fast note sync service 服务地址", 25 | "远端服务令牌": "远端服务令牌", 26 | "用于远端服务的访问授权令牌": "用于远端服务的访问授权令牌", 27 | "输入您的 API 访问令牌": "输入您的 API 访问令牌", 28 | "远端仓库名": "远端仓库名", 29 | "支持": "支持", 30 | "捐赠": "捐赠", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "如果您喜欢这个插件,请考虑捐赠以支持继续开发。", 32 | "Buy me a coffee at ko-fi.com": "在 ko-fi.com 请我喝一杯咖啡", 33 | "复制 Debug 信息": "复制 Debug 信息", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "将调试信息复制到剪贴板, 可能包含敏感信!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志", 36 | 37 | 38 | 39 | 40 | } 41 | 42 | 43 | export default zh_cn; 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Fast Note Sync", 3 | "version": "0.6.26", 4 | "description": "Privately deployable real-time multi-device note synchronization, supporting desktop and mobile devices.", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "node esbuild.config.mjs", 8 | "devcss": "sass --watch src/styles.scss styles.css && tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", 9 | "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", 10 | "ver": "node ./scripts/update-version.js" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "@types/node": "^22.19.1", 17 | "@types/react": "^19.2.7", 18 | "@types/react-dom": "^19.2.3", 19 | "@typescript-eslint/eslint-plugin": "^8.48.0", 20 | "@typescript-eslint/parser": "^8.48.0", 21 | "builtin-modules": "^4.0.0", 22 | "esbuild": "^0.24.2", 23 | "eslint": "^9.39.1", 24 | "obsidian": "^1.10.3", 25 | "tslib": "^2.8.1", 26 | "typescript": "^5.9.3" 27 | }, 28 | "dependencies": { 29 | "@codemirror/state": "6.5.0", 30 | "@codemirror/view": "6.38.6", 31 | "file-type": "^19.6.0", 32 | "react": "^19.2.0", 33 | "react-dom": "^19.2.0" 34 | }, 35 | "packageManager": "pnpm@9.12.3+sha512.cce0f9de9c5a7c95bef944169cc5dfe8741abfb145078c0d508b868056848a87c81e626246cb60967cbd7fd29a6c062ef73ff840d96b3c86c40ac92cf4a813ee" 36 | } 37 | -------------------------------------------------------------------------------- /src/lang/locale/ja.ts: -------------------------------------------------------------------------------- 1 | // 日语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const ja: Partial = { 6 | "Fast sync": "ユーザーに邪魔されず、シルクのようにスムーズで多端末リアルタイム同期のノート同期プラグインを提供することに専念しており、Mac、Windows、Android、iOS などのプラットフォームをサポートし、多言語サポートを提供しています。", 7 | "同步全部笔记(覆盖远端)": "すべてのノートを同期(リモートを上書きする)", 8 | "同步全部笔记": "すべてのノートを同期", 9 | "远端": "リモート", 10 | "接口配置信息已经粘贴到设置中!": "インターフェース設定情報は既に設定に貼り付けられています!", 11 | "未检测到配置信息!": "構成情報が検出されませんでした!", 12 | "远端服务搭建与选择": "リモートサービスの構築と選択", 13 | "选择一个适合自己的远端": "自分に合ったリモートを選ぶ", 14 | "方式": "方式", 15 | "说明": "説明", 16 | "详情参考": "詳細を参照", 17 | "私有服务搭建": "プライベートサービスの構築", 18 | "速度好, 自由配置, 无隐私风险": "速度が良く、自由に設定でき、プライバシーのリスクがない", 19 | "粘贴的远端配置": "リモート設定のペースト", 20 | "启用同步": "同期を有効にする", 21 | "关闭后您的笔记将不做任何同步": "閉じた後、ノートは同期されません。", 22 | "远端服务地址": "リモートサービスアドレス", 23 | "选择一个 Fast note sync service 服务地址": "Fast note sync service サービスアドレスを選択する", 24 | "输入您的 Fast note sync service 服务地址": "イメージ API ゲートウェイのアドレスを入力してください", 25 | "远端服务令牌": "リモートサービストークン", 26 | "用于远端服务的访问授权令牌": "リモートサービス用のアクセス許可トークン", 27 | "输入您的 API 访问令牌": "APIアクセス トークンを入力してください", 28 | "远端仓库名": "リモートリポジトリ名", 29 | "支持": "サポート", 30 | "捐赠": "寄付", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "このプラグインが気に入ったら、開発を継続するために寄付をご検討ください。", 32 | "Buy me a coffee at ko-fi.com": "コーヒーを買ってください ko-fi.com で", 33 | "复制 Debug 信息": "デバッグ情報をコピー", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "デバッグ情報をクリップボードにコピーする、機密情報が含まれる可能性があります!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "ショートカットキーでコンソールを開くと、このプラグインや他のプラグインのログを見ることができます。", 36 | } 37 | 38 | export default ja; 39 | -------------------------------------------------------------------------------- /src/lang/locale/ko.ts: -------------------------------------------------------------------------------- 1 | // 韩语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const ko: Partial = { 6 | "Fast sync": "사용자에게 방해받지 않고 부드럽고 여러 기기에서 실시간 동기화되는 메모 동기화 플러그인을 제공하며, Mac, Windows, Android, iOS 등의 플랫폼을 지원하고 다국어 지원을 제공합니다.", 7 | "同步全部笔记(覆盖远端)": "모든 노트 동기화(원격 덮어쓰기)", 8 | "同步全部笔记": "모든 노트 동기화하기", 9 | "远端": "원격", 10 | "接口配置信息已经粘贴到设置中!": "인터페이스 구성 정보가 설정에 이미 붙여넣어졌습니다!", 11 | "未检测到配置信息!": "구성 정보를 감지하지 못했습니다!", 12 | "远端服务搭建与选择": "원격 서비스 구축 및 선택", 13 | "选择一个适合自己的远端": "자신에게 맞는 원격을 선택하세요.", 14 | "方式": "방식", 15 | "说明": "설명", 16 | "详情参考": "자세한 사항은 참고하세요", 17 | "私有服务搭建": "개인 서비스 구축", 18 | "速度好, 自由配置, 无隐私风险": "속도가 좋고, 자유롭게 구성 가능하며, 개인정보 위험이 없습니다.", 19 | "粘贴的远端配置": "붙여넣은 원격 구성", 20 | "启用同步": "동기화 활성화", 21 | "关闭后您的笔记将不做任何同步": "닫은 후에는 귀하의 노트가 동기화되지 않습니다.", 22 | "远端服务地址": "원격 서비스 주소", 23 | "选择一个 Fast note sync service 服务地址": "Fast note sync service 서비스 주소 선택", 24 | "输入您的 Fast note sync service 服务地址": "Image API Gateway 주소를 입력하세요", 25 | "远端服务令牌": "원격 서비스 토큰", 26 | "用于远端服务的访问授权令牌": "원격 서비스에 대한 액세스 권한 부여 토큰", 27 | "输入您的 API 访问令牌": "API 액세스 토큰을 입력하세요.", 28 | "远端仓库名": "원격 저장소 이름", 29 | "支持": "지원", 30 | "捐赠": "기부", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "이 플러그인이 마음에 드신다면 계속 개발을 지원하기 위해 기부를 고려해 주세요.", 32 | "Buy me a coffee at ko-fi.com": "ko-fi.com에서 나에게 커피를 사주세요", 33 | "复制 Debug 信息": "디버그 정보 복사", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "디버그 정보를 클립보드에 복사하기, 민감한 정보가 포함될 수 있습니다!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "단축키를 통해 콘솔을 열면 이 플러그인과 다른 플러그인의 로그를 볼 수 있습니다.", 36 | } 37 | 38 | export default ko; 39 | -------------------------------------------------------------------------------- /src/lib/helps.ts: -------------------------------------------------------------------------------- 1 | import { Notice, moment } from "obsidian"; 2 | 3 | 4 | /** 5 | * timestampToDate 6 | * 将时间戳转换为格式化的日期字符串(YYYY-MM-DD HH:mm:ss) 7 | * @param timestamp - 时间戳(以毫秒为单位) 8 | * @returns 格式化的日期字符串 9 | */ 10 | export const timestampToDate = function (timestamp: number): string { 11 | return moment(timestamp).format("YYYY-MM-DD HH:mm:ss") 12 | } 13 | 14 | /** 15 | * stringToDate 16 | * 将日期字符串转换为格式化的日期字符串(YYYY-MM-DD HH:mm:ss) 17 | * 如果输入的日期字符串为空,则使用默认日期 "1970-01-01 00:00:00" 18 | * @param date - 日期字符串 19 | * @returns 格式化的日期字符串 20 | */ 21 | export const stringToDate = function (date: string): string { 22 | if (!date || date == "") { 23 | date = "1970-01-01 00:00:00" 24 | } 25 | return moment(date).format("YYYY-MM-DD HH:mm:ss") 26 | } 27 | 28 | /** 29 | * hashContent 30 | * 使用简单的哈希函数生成输入字符串的哈希值 31 | * @param content - 要哈希的字符串内容 32 | * @returns 字符串内容的哈希值 33 | */ 34 | export const hashContent = function (content: string): string { 35 | // 使用简单的哈希函数生成哈希值 36 | let hash = 0 37 | for (let i = 0; i < content.length; i++) { 38 | const char = content.charCodeAt(i) 39 | hash = (hash << 5) - hash + char 40 | hash &= hash 41 | } 42 | return String(hash) 43 | } 44 | 45 | /** 46 | * showErrorDialog 47 | * 显示一个错误对话框,内容为传入的消息 48 | * @param message - 要显示的错误消息 49 | */ 50 | export const showErrorDialog = function (message: string): void { 51 | new Notice(message) 52 | } 53 | 54 | /** 55 | * dump 56 | * 将传入的消息打印到控制台 57 | * @param message - 要打印的消息,可以是多个参数 58 | */ 59 | export const dump = function (...message: unknown[]): void { 60 | //console.log(...message) 61 | } 62 | 63 | 64 | 65 | 66 | export function isHttpUrl(url: string): boolean { 67 | return /^https?:\/\/.+/i.test(url); 68 | } 69 | 70 | export function isWsUrl(url: string): boolean { 71 | return /^wss?:\/\/.+/i.test(url); 72 | } -------------------------------------------------------------------------------- /src/lang/locale/he.ts: -------------------------------------------------------------------------------- 1 | // 希伯来语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const he: Partial = { 6 | "Fast sync": "ממוקד במתן חוויית משתמש ללא הפרעות, סנכרון חלק בזמן אמת בין מכשירים, תוסף סנכרון הערות התומך בפלטפורמות כמו Mac, Windows, Android, iOS ועוד, עם תמיכה רב-לשונית.", 7 | "同步全部笔记(覆盖远端)": "סנכרן את כל ההערות (החלף את התוכן המרוחק)", 8 | "同步全部笔记": "סנכרן את כל ההערות", 9 | "远端": "מרחוק", 10 | "接口配置信息已经粘贴到设置中!": "מידע תצורת הממשק כבר הודבק להגדרות!", 11 | "未检测到配置信息!": "לא זוהתה תצורת מידע!", 12 | "远端服务搭建与选择": "הקמה ובחירה של שירותים מרוחקים", 13 | "选择一个适合自己的远端": "בחר רימוט מתאים עבורך", 14 | "方式": "דרך", 15 | "说明": "הַנחָיָה", 16 | "详情参考": "פרטים ניתן למצוא", 17 | "私有服务搭建": "הקמת שירות פרטי", 18 | "速度好, 自由配置, 无隐私风险": "מהירות טובה, תצורה חופשית, ללא סיכון לפרטיות", 19 | "粘贴的远端配置": "הגדרות הקצה שהודבקו", 20 | "启用同步": "הפעל סנכרון", 21 | "关闭后您的笔记将不做任何同步": "לאחר הסגירה, הפתקים שלכם לא יסונכרנו", 22 | "远端服务地址": "כתובת שירות מרוחק", 23 | "选择一个 Fast note sync service 服务地址": "בחר כתובת שירות Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "אנא הזן את כתובת שער ה-API של התמונה שלך", 25 | "远端服务令牌": "אסימון שירות מרוחק", 26 | "用于远端服务的访问授权令牌": "אסימון אישור גישה לשירותים מרוחקים", 27 | "输入您的 API 访问令牌": "הזן את אסימוני גישה של ה-API שלך", 28 | "远端仓库名": "שם מאגר מרוחק", 29 | "支持": "תמיכה", 30 | "捐赠": "תרומה", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "אם אתה נהנה מהתוסף הזה, שקול לתרום לתמיכה בפיתוח המתמשך.", 32 | "Buy me a coffee at ko-fi.com": "קנה לי קפה ב-ko-fi.com", 33 | "复制 Debug 信息": "העתק מידע לניפוי שגיאות", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "העתקת מידע הנדסה לאחור ללוח, עשויה להכיל מידע רגיש!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "כדי לפתוח את הקונסולה באמצעות קיצורי מקלדת, אתה יכול לראות את הלוגים של התוסף הזה ותוספים אחרים.", 36 | } 37 | 38 | export default he; 39 | -------------------------------------------------------------------------------- /src/lang/locale/th.ts: -------------------------------------------------------------------------------- 1 | // 泰语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const th: Partial = { 6 | "Fast sync": "มุ่งเน้นให้บริการปลั๊กอินบันทึกที่ไม่มีการรบกวน ลื่นไหลราวกับผ้าไหม และซิงค์แบบเรียลไทม์ข้ามหลายแพลตฟอร์ม รองรับแพลตฟอร์มต่าง ๆ เช่น Mac, Windows, Android, iOS และมีการสนับสนุนหลายภาษา", 7 | "同步全部笔记(覆盖远端)": "ซิงค์บันทึกทั้งหมด (แทนที่ระยะไกล)", 8 | "同步全部笔记": "ซิงค์บันทึกทั้งหมด", 9 | "远端": "ปลายทาง", 10 | "接口配置信息已经粘贴到设置中!": "ข้อมูลการกำหนดค่าอินเทอร์เฟซได้ถูกวางในตั้งค่าแล้ว!", 11 | "未检测到配置信息!": "ไม่พบข้อมูลการกำหนดค่า!", 12 | "远端服务搭建与选择": "การติดตั้งและการเลือกใช้บริการระยะไกล", 13 | "选择一个适合自己的远端": "เลือกรีโมทที่เหมาะสมสำหรับตัวเอง", 14 | "方式": "วิธี", 15 | "说明": "คำแนะนำ", 16 | "详情参考": "รายละเอียดเพิ่มเติม", 17 | "私有服务搭建": "การสร้างบริการส่วนตัว", 18 | "速度好, 自由配置, 无隐私风险": "ความเร็วดี, ปรับแต่งได้อย่างอิสระ, ไม่มีความเสี่ยงด้านความเป็นส่วนตัว", 19 | "粘贴的远端配置": "การกำหนดค่าระยะไกลที่วางลง", 20 | "启用同步": "เปิดใช้งานการซิงค์", 21 | "关闭后您的笔记将不做任何同步": "ปิดแล้วโน้ตของคุณจะไม่ซิงค์ใดๆ", 22 | "远端服务地址": "ที่อยู่บริการระยะไกล", 23 | "选择一个 Fast note sync service 服务地址": "เลือกที่อยู่บริการ Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "กรุณาใส่ที่อยู่ Image API Gateway ของคุณ", 25 | "远端服务令牌": "โทเค็นบริการระยะไกล", 26 | "用于远端服务的访问授权令牌": "โทเค็นการอนุญาตเข้าถึงสำหรับบริการระยะไกล", 27 | "输入您的 API 访问令牌": "กรุณาใส่โทเค็นเข้าถึง API ของคุณ", 28 | "远端仓库名": "ชื่อคลังเก็บระยะไกล", 29 | "支持": "สนับสนุน", 30 | "捐赠": "บริจาค", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "หากคุณชอบปลั๊กอินนี้ โปรดพิจารณาบริจาคเพื่อสนับสนุนการพัฒนาต่อไป", 32 | "Buy me a coffee at ko-fi.com": "ซื้อกาแฟให้ฉันที่ ko-fi.com", 33 | "复制 Debug 信息": "คัดลอกข้อมูล Debug", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "คัดลอกข้อมูลการดีบักไปยังคลิปบอร์ด อาจมีข้อมูลที่ละเอียดอ่อน!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "คุณสามารถเปิดคอนโซลด้วยปุ่มลัดเพื่อดูบันทึกของปลั๊กอินนี้และปลั๊กอินอื่น ๆ", 36 | } 37 | 38 | export default th; 39 | -------------------------------------------------------------------------------- /src/lang/locale/ar.ts: -------------------------------------------------------------------------------- 1 | // 阿拉伯语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const ar: Partial = { 6 | "Fast sync": "التركيز على تقديم ملحق مزامنة الملاحظات بدون إزعاج، وسلس بشكل حريري، ومزامنة في الوقت الفعلي عبر منصات متعددة للمستخدمين، يدعم منصات Mac وWindows وAndroid وiOS وغيرها، ويقدم دعمًا متعدد اللغات.", 7 | "同步全部笔记(覆盖远端)": "مزامنة كافة الملاحظات (استبدال البيانات البعيدة)", 8 | "同步全部笔记": "مزامنة جميع الملاحظات", 9 | "远端": "بعيد", 10 | "接口配置信息已经粘贴到设置中!": "تم لصق معلومات تكوين الواجهة في الإعدادات!", 11 | "未检测到配置信息!": "لم يتم اكتشاف معلومات التكوين!", 12 | "远端服务搭建与选择": "إعداد واختيار الخدمات البعيدة", 13 | "选择一个适合自己的远端": "اختر مستودعاً بعيداً يناسبك", 14 | "方式": "طريقة", 15 | "说明": "توضيح", 16 | "详情参考": "يرجى مراجعة التفاصيل", 17 | "私有服务搭建": "إنشاء خدمات خاصة", 18 | "速度好, 自由配置, 无隐私风险": "سرعة جيدة، قابلية التخصيص، لا يوجد خطر على الخصوصية", 19 | "粘贴的远端配置": "تكوين الوجهة الملصقة", 20 | "启用同步": "تمكين المزامنة", 21 | "关闭后您的笔记将不做任何同步": "بعد الإغلاق، لن تتم مزامنة ملاحظاتك بأي شكل من الأشكال", 22 | "远端服务地址": "عنوان الخدمة البعيدة", 23 | "选择一个 Fast note sync service 服务地址": "اختر عنوان خدمة Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "أدخل عنوان باب بوابة Image API الخاص بك", 25 | "远端服务令牌": "رمز الخدمة البعيدة", 26 | "用于远端服务的访问授权令牌": "رمز تصريح الوصول للخدمات عن بُعد", 27 | "输入您的 API 访问令牌": "أدخل رمز الوصول الخاص بواجهة برمجة التطبيقات", 28 | "远端仓库名": "اسم المستودع البعيد", 29 | "支持": "دعم", 30 | "捐赠": "تبرع", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "إذا كنت تحب هذه الإضافة، يرجى التفكير في التبرع لدعم استمرار التطوير.", 32 | "Buy me a coffee at ko-fi.com": "اشتر لي قهوة في ko-fi.com", 33 | "复制 Debug 信息": "انسخ معلومات Debug", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "انسخ معلومات التصحيح إلى الحافظة، قد تحتوي على معلومات حساسة!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "لفتح وحدة التحكم باستخدام اختصار لوحة المفاتيح، يمكنك رؤية سجل هذا الملحق والملحقات الأخرى.", 36 | } 37 | 38 | export default ar; 39 | -------------------------------------------------------------------------------- /src/lang/locale/en.ts: -------------------------------------------------------------------------------- 1 | // 英语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const en: Partial = { 6 | "Fast sync": "Fast note sync", 7 | "同步全部笔记(覆盖远端)": "Sync all notes (overwrite remote)", 8 | "同步全部笔记": "Sync all notes", 9 | "远端": "Remote", 10 | "接口配置信息已经粘贴到设置中!": "The interface configuration information has been pasted into the settings!", 11 | "未检测到配置信息!": "Configuration information not detected!", 12 | "远端服务搭建与选择": "Remote service setup and selection", 13 | "选择一个适合自己的远端": "Choose a remote that suits you", 14 | "方式": "Method", 15 | "说明": "Instructions", 16 | "详情参考": "Details reference", 17 | "私有服务搭建": "Private service setup", 18 | "速度好, 自由配置, 无隐私风险": "Fast speed, customizable, no privacy risks", 19 | "粘贴的远端配置": "Pasted remote configuration", 20 | "启用同步": "Enable synchronization", 21 | "关闭后您的笔记将不做任何同步": "After closing, your notes will not be synced.", 22 | "远端服务地址": "Remote service address", 23 | "选择一个 Fast note sync service 服务地址": "Choose an fast note sync service address", 24 | "输入您的 Fast note sync service 服务地址": "Enter your fast note sync service address", 25 | "远端服务令牌": "Remote service token", 26 | "用于远端服务的访问授权令牌": "Access authorization token for remote services", 27 | "输入您的 API 访问令牌": "Enter your API access token", 28 | "远端仓库名": "Remote repository name", 29 | "支持": "Support", 30 | "捐赠": "Donation", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "If you like this plugin, please consider donating to support continued development.", 32 | "Buy me a coffee at ko-fi.com": "Buy me a coffee at ko-fi.com", 33 | "复制 Debug 信息": "Copy debug information", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Copy debug information to the clipboard, may contain sensitive information!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "By using the shortcut key to open the console, you can see the logs of this plugin and other plugins.", 36 | "console_mac": "Cmd (⌘) + option (⌥) + i", 37 | "console_windows": "Ctrl (⌃) + shift (⇧) + i", 38 | } 39 | 40 | export default en; 41 | 42 | -------------------------------------------------------------------------------- /src/lang/locale/tr.ts: -------------------------------------------------------------------------------- 1 | // 土耳其语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const tr: Partial = { 6 | "Fast sync": "Kullanıcıya rahatsızlık vermeden, ipek gibi pürüzsüz ve çoklu platformda gerçek zamanlı senkronizasyon sağlayan bir not senkronizasyon eklentisi sunmaya odaklanmıştır, Mac, Windows, Android, iOS gibi platformları destekler ve çok dilli destek sunar.", 7 | "同步全部笔记(覆盖远端)": "Tüm notları senkronize et (uzaktakini üzerine yaz)", 8 | "同步全部笔记": "Tüm notları senkronize et", 9 | "远端": "Uzak uç", 10 | "接口配置信息已经粘贴到设置中!": "Arayüz yapılandırma bilgileri ayarlara yapıştırıldı!", 11 | "未检测到配置信息!": "Yapılandırma bilgisi tespit edilmedi!", 12 | "远端服务搭建与选择": "Uzak hizmet kurulumu ve seçimi", 13 | "选择一个适合自己的远端": "Kendinize uygun bir uzak sunucu seçin", 14 | "方式": "Tarz", 15 | "说明": "Açıklama", 16 | "详情参考": "Detaylara bakınız", 17 | "私有服务搭建": "Özel hizmet kurulumu", 18 | "速度好, 自由配置, 无隐私风险": "Hız iyi, özgür yapılandırma, gizlilik riski yok", 19 | "粘贴的远端配置": "Yapıştırılan uzak uç yapılandırması", 20 | "启用同步": "Senkronizasyonu Etkinleştir", 21 | "关闭后您的笔记将不做任何同步": "Kapandıktan sonra notlarınız senkronize edilmeyecek.", 22 | "远端服务地址": "Uzak sunucu adresi", 23 | "选择一个 Fast note sync service 服务地址": "Bir Fast note sync service hizmet adresi seçin", 24 | "输入您的 Fast note sync service 服务地址": "Lütfen Image API Gateway adresinizi giriniz.", 25 | "远端服务令牌": "Uzak hizmet belirteci", 26 | "用于远端服务的访问授权令牌": "Uzak hizmetler için erişim yetkilendirme belirteci", 27 | "输入您的 API 访问令牌": "API erişim belirtecinizi girin", 28 | "远端仓库名": "Uzak depo adı", 29 | "支持": "destek", 30 | "捐赠": "Bağış", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Eklentiyi beğendiyseniz, geliştirmeye devam etmeyi desteklemek için bağış yapmayı düşünün.", 32 | "Buy me a coffee at ko-fi.com": "Bana Bir Kahve Al ko-fi.com'da", 33 | "复制 Debug 信息": "Hata ayıklama bilgilerini kopyala", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Hata ayıklama bilgilerini panoya kopyala, hassas bilgiler içerebilir!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Kısayol tuşlarıyla konsolu açarak bu eklentinin ve diğer eklentilerin günlüklerini görebilirsiniz.", 36 | } 37 | 38 | export default tr; 39 | -------------------------------------------------------------------------------- /src/lang/locale/vi.ts: -------------------------------------------------------------------------------- 1 | // 越南语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const vi: Partial = { 6 | "Fast sync": "Tập trung cung cấp cho người dùng một plugin đồng bộ ghi chú không phiền nhiễu, mượt mà như lụa và đồng bộ thời gian thực trên nhiều nền tảng, hỗ trợ các nền tảng như Mac, Windows, Android, iOS và cung cấp hỗ trợ đa ngôn ngữ.", 7 | "同步全部笔记(覆盖远端)": "Đồng bộ tất cả ghi chú (ghi đè từ xa)", 8 | "同步全部笔记": "Đồng bộ tất cả ghi chú", 9 | "远端": "Từ xa", 10 | "接口配置信息已经粘贴到设置中!": "Thông tin cấu hình giao diện đã được dán vào cài đặt!", 11 | "未检测到配置信息!": "Không phát hiện thấy thông tin cấu hình!", 12 | "远端服务搭建与选择": "Triển khai và lựa chọn dịch vụ từ xa", 13 | "选择一个适合自己的远端": "Chọn một máy chủ từ xa phù hợp với bản thân", 14 | "方式": "cách thức", 15 | "说明": "Hướng dẫn", 16 | "详情参考": "Chi tiết tham khảo", 17 | "私有服务搭建": "Dịch vụ riêng tư", 18 | "速度好, 自由配置, 无隐私风险": "Tốc độ tốt, cấu hình tự do, không có rủi ro về quyền riêng tư", 19 | "粘贴的远端配置": "Cấu hình kết nối từ xa đã dán", 20 | "启用同步": "Bật đồng bộ hóa", 21 | "关闭后您的笔记将不做任何同步": "Sau khi đóng, ghi chú của bạn sẽ không được đồng bộ hóa", 22 | "远端服务地址": "Địa chỉ dịch vụ từ xa", 23 | "选择一个 Fast note sync service 服务地址": "Chọn một địa chỉ dịch vụ Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Nhập địa chỉ API Gateway Hình ảnh của bạn", 25 | "远端服务令牌": "Mã thông báo dịch vụ từ xa", 26 | "用于远端服务的访问授权令牌": "Mã thông báo ủy quyền truy cập cho dịch vụ từ xa", 27 | "输入您的 API 访问令牌": "Vui lòng nhập mã thông báo API của bạn", 28 | "远端仓库名": "Tên kho từ xa", 29 | "支持": "Hỗ trợ", 30 | "捐赠": "Quyên tặng", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Nếu bạn thích tiện ích này, hãy cân nhắc quyên góp để hỗ trợ phát triển tiếp tục.", 32 | "Buy me a coffee at ko-fi.com": "Mua cho tôi một tách cà phê tại ko-fi.com", 33 | "复制 Debug 信息": "Sao chép thông tin Debug", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Sao chép thông tin gỡ lỗi vào khay nhớ tạm, có thể chứa thông tin nhạy cảm!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Bằng cách sử dụng phím tắt để mở bảng điều khiển, bạn có thể xem nhật ký của plugin này và các plugin khác.", 36 | } 37 | 38 | export default vi; 39 | -------------------------------------------------------------------------------- /src/lang/locale/da.ts: -------------------------------------------------------------------------------- 1 | // 丹麦语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const da: Partial = { 6 | "Fast sync": "Fokus på at give brugerne en uforstyrret, silkeblød oplevelse med en plugin til synkronisering af noter i realtid på tværs af flere enheder, der understøtter platforme som Mac, Windows, Android, iOS og tilbyder flersproget support.", 7 | "同步全部笔记(覆盖远端)": "Synkroniser alle noter (overskriv fjern)", 8 | "同步全部笔记": "Synkroniser alle noter", 9 | "远端": "Fjern", 10 | "接口配置信息已经粘贴到设置中!": "Grænseflade konfigurationsinformation er allerede blevet indsat i indstillingerne!", 11 | "未检测到配置信息!": "Konfigurationsoplysninger ikke fundet!", 12 | "远端服务搭建与选择": "Opsætning og valg af fjernbetjeningstjenester", 13 | "选择一个适合自己的远端": "Vælg en fjernbetjening, der passer til dig", 14 | "方式": "Måde", 15 | "说明": "Instruktion", 16 | "详情参考": "Detaljer henvises til", 17 | "私有服务搭建": "Privat tjenesteopsætning", 18 | "速度好, 自由配置, 无隐私风险": "Hurtig hastighed, fri konfiguration, ingen privatlivsrisiko", 19 | "粘贴的远端配置": "Indsæt fjernkonfiguration", 20 | "启用同步": "Aktiver synkronisering", 21 | "关闭后您的笔记将不做任何同步": "Når det er lukket, vil dine noter ikke blive synkroniseret", 22 | "远端服务地址": "Fjernbetjeningsadresse", 23 | "选择一个 Fast note sync service 服务地址": "Vælg en Fast note sync service adresse", 24 | "输入您的 Fast note sync service 服务地址": "Indtast din Image API Gateway-adresse", 25 | "远端服务令牌": "Fjerntjenestetoken", 26 | "用于远端服务的访问授权令牌": "Adgangsgodkendelsestoken til fjernservice", 27 | "输入您的 API 访问令牌": "Indtast din API-adgangstoken", 28 | "远端仓库名": "Fjernlager navn", 29 | "支持": "Støtte", 30 | "捐赠": "donation", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Hvis du kan lide dette plugin, så overvej at donere for at støtte den fortsatte udvikling.", 32 | "Buy me a coffee at ko-fi.com": "Køb mig en kaffe på ko-fi.com", 33 | "复制 Debug 信息": "Kopiér fejlinformationer", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Kopiér fejlretningsoplysningerne til udklipsholderen, det kan indeholde følsomme oplysninger!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Åbn konsollen med en genvejstast, så kan du se loggene for denne udvidelse og andre udvidelser.", 36 | } 37 | 38 | export default da; 39 | -------------------------------------------------------------------------------- /src/lang/locale/nb.ts: -------------------------------------------------------------------------------- 1 | // 挪威语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const nb: Partial = { 6 | "Fast sync": "Vi fokuserer på å tilby brukerne en forstyrrelsesfri, silkemyk og flerplattform reeltidssynkronisering for notatsynkronisering, støttende plattformer som Mac, Windows, Android, iOS, og tilbyr flerspråklig støtte.", 7 | "同步全部笔记(覆盖远端)": "Synkroniser alle notater (overstyrer eksterne)", 8 | "同步全部笔记": "Synkroniser alle notater", 9 | "远端": "Fjernkontroll", 10 | "接口配置信息已经粘贴到设置中!": "Grensesnittkonfigurasjonsinformasjonen er allerede limt inn i innstillingene!", 11 | "未检测到配置信息!": "Konfigurasjonsinformasjon ble ikke oppdaget!", 12 | "远端服务搭建与选择": "Tjenestetilrettelegging og valg av eksterne tjenester", 13 | "选择一个适合自己的远端": "Velg en passende ekstern(remote) for deg selv", 14 | "方式": "Måte", 15 | "说明": "Forklaring", 16 | "详情参考": "Se detaljer", 17 | "私有服务搭建": "Privat tjenesteoppsett", 18 | "速度好, 自由配置, 无隐私风险": "Hastighet er bra, fri konfigurasjon, ingen personvernrisiko", 19 | "粘贴的远端配置": "Lim inn den eksterne konfigurasjonen", 20 | "启用同步": "Aktiver synkronisering", 21 | "关闭后您的笔记将不做任何同步": "Når den er lukket, vil notatene dine ikke bli synkronisert", 22 | "远端服务地址": "Fjernserveradresse", 23 | "选择一个 Fast note sync service 服务地址": "Velg en Fast note sync service tjenesteadresse", 24 | "输入您的 Fast note sync service 服务地址": "Skriv inn adressen til Image API Gateway", 25 | "远端服务令牌": "Fjerntjenestetoken", 26 | "用于远端服务的访问授权令牌": "Tilgangsautoriseringstoken for ekstern tjeneste", 27 | "输入您的 API 访问令牌": "Skriv inn API-tilgangstokenet ditt", 28 | "远端仓库名": "Fjernlager navn", 29 | "支持": "Støtte", 30 | "捐赠": "Donasjon", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Hvis du liker denne pluginen, vennligst vurder å donere for å støtte videre utvikling.", 32 | "Buy me a coffee at ko-fi.com": "Kjøp meg en kaffe på ko-fi.com", 33 | "复制 Debug 信息": "Kopier Debug-informasjon", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Kopier feilsøkingsinformasjon til utklippstavlen, kan inneholde sensitiv informasjon!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Ved å bruke hurtigtasten for å åpne konsollen, kan du se loggen for dette pluginet og andre pluginer", 36 | } 37 | 38 | export default nb; 39 | -------------------------------------------------------------------------------- /src/lang/locale/ms.ts: -------------------------------------------------------------------------------- 1 | // 马来语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const ms: Partial = { 6 | "Fast sync": "Fokus untuk menyediakan pemalam penyelarasan nota yang bebas gangguan, licin seperti sutera, dan penyelarasan masa nyata menyeluruh, menyokong platform seperti Mac, Windows, Android, iOS, dan menawarkan sokongan berbilang bahasa.", 7 | "同步全部笔记(覆盖远端)": "Menyelaraskan semua nota (menimpa jarak jauh)", 8 | "同步全部笔记": "Segerakkan semua nota", 9 | "远端": "Jauh", 10 | "接口配置信息已经粘贴到设置中!": "Maklumat konfigurasi antara muka telah ditampal ke dalam tetapan!", 11 | "未检测到配置信息!": "Maklumat konfigurasi tidak dikesan!", 12 | "远端服务搭建与选择": "Perkhidmatan jarak jauh pembinaan dan pemilihan", 13 | "选择一个适合自己的远端": "Pilih jarak jauh yang sesuai untuk diri sendiri", 14 | "方式": "cara", 15 | "说明": "Arahan", 16 | "详情参考": "Sila rujuk butiran", 17 | "私有服务搭建": "Perkhidmatan persendirian ditubuhkan", 18 | "速度好, 自由配置, 无隐私风险": "Kelajuan baik, kebolehsuaian bebas, tiada risiko privasi", 19 | "粘贴的远端配置": "Konfigurasi jauh yang ditampal", 20 | "启用同步": "Aktifkan penyegerakan", 21 | "关闭后您的笔记将不做任何同步": "Selepas ditutup, nota anda tidak akan disegerakkan.", 22 | "远端服务地址": "Alamat Perkhidmatan Jauh", 23 | "选择一个 Fast note sync service 服务地址": "Pilih alamat perkhidmatan Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Sila masukkan alamat API Gateway Imej anda", 25 | "远端服务令牌": "Token Perkhidmatan Jauh", 26 | "用于远端服务的访问授权令牌": "Token kebenaran akses untuk perkhidmatan jauh", 27 | "输入您的 API 访问令牌": "Sila masukkan token akses API anda", 28 | "远端仓库名": "Nama repositori jauh", 29 | "支持": "menyokong", 30 | "捐赠": "Derma", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Jika anda suka pemalam ini, sila pertimbangkan untuk menderma bagi menyokong pembangunan yang berterusan.", 32 | "Buy me a coffee at ko-fi.com": "Beli Saya Kopi di ko-fi.com", 33 | "复制 Debug 信息": "Salin Maklumat Debug", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Salin maklumat nyahpepijat ke papan klip, mungkin mengandungi info sensitif!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Dengan menekan kekunci pintas untuk membuka konsol, anda boleh melihat log pemalam ini dan pemalam lain.", 36 | } 37 | 38 | export default ms; 39 | -------------------------------------------------------------------------------- /src/lang/locale/pl.ts: -------------------------------------------------------------------------------- 1 | // 波兰语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const pl: Partial = { 6 | "Fast sync": "Skupia się na dostarczaniu użytkownikom niezakłóconej, jedwabistej płynności, wieloplatformowej, w czasie rzeczywistym synchronizacji notatek, wspiera platformy takie jak Mac, Windows, Android, iOS i oferuje wsparcie dla wielu języków.", 7 | "同步全部笔记(覆盖远端)": "Synchronizuj wszystkie notatki (zastępując zdalne)", 8 | "同步全部笔记": "Synchronizuj wszystkie notatki", 9 | "远端": "zdalny", 10 | "接口配置信息已经粘贴到设置中!": "Informacje o konfiguracji interfejsu zostały wklejone do ustawień!", 11 | "未检测到配置信息!": "Nie wykryto informacji konfiguracyjnych!", 12 | "远端服务搭建与选择": "Budowanie i wybór zdalnych usług", 13 | "选择一个适合自己的远端": "Wybierz odpowiedni dla siebie zdalny.", 14 | "方式": "sposób", 15 | "说明": "instrukcja", 16 | "详情参考": "Szczegóły w odniesieniu", 17 | "私有服务搭建": "Prywatne tworzenie usług", 18 | "速度好, 自由配置, 无隐私风险": "Szybkość dobra, elastyczna konfiguracja, brak ryzyka naruszenia prywatności", 19 | "粘贴的远端配置": "Skopiowana zdalna konfiguracja", 20 | "启用同步": "Włącz synchronizację", 21 | "关闭后您的笔记将不做任何同步": "Po zamknięciu Twoje notatki nie będą synchronizowane", 22 | "远端服务地址": "Zdalny adres serwisu", 23 | "选择一个 Fast note sync service 服务地址": "Wybierz adres usługi Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Wprowadź adres swojego Image API Gateway", 25 | "远端服务令牌": "Zdalny token usługi", 26 | "用于远端服务的访问授权令牌": "Token autoryzacji dostępu do usługi zdalnej", 27 | "输入您的 API 访问令牌": "„Wprowadź swój token dostępu API”", 28 | "远端仓库名": "Nazwa zdalnego repozytorium", 29 | "支持": "Wsparcie", 30 | "捐赠": "Darowizna", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Jeśli podoba Ci się ta wtyczka, rozważ przekazanie darowizny na wsparcie dalszego rozwoju.", 32 | "Buy me a coffee at ko-fi.com": "Kup mi kawę na ko-fi.com", 33 | "复制 Debug 信息": "Skopiuj informacje debugowania", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Skopiuj informacje debugowania do schowka, mogą zawierać wrażliwe dane!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Aby otworzyć konsolę za pomocą skrótu klawiszowego, możesz zobaczyć dzienniki tego dodatku i innych dodatków.", 36 | } 37 | 38 | export default pl; 39 | -------------------------------------------------------------------------------- /src/lang/locale/nl.ts: -------------------------------------------------------------------------------- 1 | // 荷兰语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const nl: Partial = { 6 | "Fast sync": "Gefocust op het bieden van een ongestoorde, zijdezachte, multi-platform real-time synchronisatie voor notities, ondersteund op Mac, Windows, Android, iOS en andere platforms, en biedt meertalige ondersteuning.", 7 | "同步全部笔记(覆盖远端)": "Synchroniseer alle notities (overschrijf op afstand)", 8 | "同步全部笔记": "Synchroniseer alle notities", 9 | "远端": "Afstand", 10 | "接口配置信息已经粘贴到设置中!": "De configuratie-informatie van de interface is al in de instellingen geplakt!", 11 | "未检测到配置信息!": "Configuratie-informatie niet gedetecteerd!", 12 | "远端服务搭建与选择": "Opzetten en kiezen van externe diensten", 13 | "选择一个适合自己的远端": "Kies een geschikte remote voor jezelf", 14 | "方式": "manier", 15 | "说明": "Instructies", 16 | "详情参考": "Details raadplegen", 17 | "私有服务搭建": "Opzetten van privéservices", 18 | "速度好, 自由配置, 无隐私风险": "Snelheid goed, vrije configuratie, geen privacyrisico's", 19 | "粘贴的远端配置": "Gekopieerde externe configuratie", 20 | "启用同步": "Synchronisatie inschakelen", 21 | "关闭后您的笔记将不做任何同步": "Nadat u deze hebt gesloten, worden uw notities niet meer gesynchroniseerd.", 22 | "远端服务地址": "Externe serviceadres", 23 | "选择一个 Fast note sync service 服务地址": "Kies een Fast note sync service serveradres", 24 | "输入您的 Fast note sync service 服务地址": "Voer uw Image API Gateway-adres in", 25 | "远端服务令牌": "Externe servicetoken", 26 | "用于远端服务的访问授权令牌": "Toegangstoken voor autorisatie voor externe diensten", 27 | "输入您的 API 访问令牌": "Voer uw API-toegangstoken in", 28 | "远端仓库名": "Afstandsrepositorynaam", 29 | "支持": "Ondersteuning", 30 | "捐赠": "Doneren", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Als u deze plugin leuk vindt, overweeg dan een donatie om de verdere ontwikkeling te ondersteunen.", 32 | "Buy me a coffee at ko-fi.com": "Koop een koffie voor me op ko-fi.com", 33 | "复制 Debug 信息": "Kopieer debug-informatie", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Kopieer debug-informatie naar het klembord, dit kan gevoelige informatie bevatten!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Met sneltoetsen de console openen, je kunt de logboeken van deze plugin en andere plugins zien.", 36 | } 37 | 38 | export default nl; 39 | -------------------------------------------------------------------------------- /src/lang/locale/id.ts: -------------------------------------------------------------------------------- 1 | // 印度尼西亚语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const id: Partial = { 6 | "Fast sync": "Fokus untuk menyediakan plugin sinkronisasi catatan yang mulus, tanpa gangguan, dan sinkronisasi real-time lintas platform untuk pengguna, mendukung platform seperti Mac, Windows, Android, iOS, dan juga menyediakan dukungan multi-bahasa.", 7 | "同步全部笔记(覆盖远端)": "Sinkronkan semua catatan (menimpa jarak jauh)", 8 | "同步全部笔记": "Menyinkronkan semua catatan", 9 | "远端": "Jarak jauh", 10 | "接口配置信息已经粘贴到设置中!": "Informasi konfigurasi antarmuka telah ditempelkan ke pengaturan!", 11 | "未检测到配置信息!": "Konfigurasi tidak terdeteksi!", 12 | "远端服务搭建与选择": "Pembangunan dan Pemilihan Layanan Jarak Jauh", 13 | "选择一个适合自己的远端": "Pilih remote yang sesuai untuk diri Anda sendiri", 14 | "方式": "cara", 15 | "说明": "Petunjuk", 16 | "详情参考": "Rincian referensi", 17 | "私有服务搭建": "Pembangunan layanan pribadi", 18 | "速度好, 自由配置, 无隐私风险": "Kecepatan bagus, konfigurasi bebas, tanpa risiko privasi", 19 | "粘贴的远端配置": "Konfigurasi jarak jauh yang ditempelkan", 20 | "启用同步": "Aktifkan Sinkronisasi", 21 | "关闭后您的笔记将不做任何同步": "Setelah ditutup, catatan Anda tidak akan disinkronkan sama sekali.", 22 | "远端服务地址": "Alamat Layanan Jarak Jauh", 23 | "选择一个 Fast note sync service 服务地址": "Pilih alamat layanan Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Masukkan alamat Gateway API Gambar Anda", 25 | "远端服务令牌": "Token Layanan Jarak Jauh", 26 | "用于远端服务的访问授权令牌": "Token otorisasi akses untuk layanan jarak jauh", 27 | "输入您的 API 访问令牌": "Masukkan token akses API Anda", 28 | "远端仓库名": "Nama repositori jarak jauh", 29 | "支持": "dukungan", 30 | "捐赠": "Donasi", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Jika Anda menyukai plugin ini, pertimbangkan untuk berdonasi untuk mendukung pengembangan lanjut.", 32 | "Buy me a coffee at ko-fi.com": "Beli Saya Kopi di ko-fi.com", 33 | "复制 Debug 信息": "Salin Debug Informasi", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Salin informasi debug ke clipboard, mungkin berisi informasi sensitif!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Dengan menggunakan pintasan keyboard untuk membuka konsol, Anda dapat melihat log dari plugin ini dan plugin lainnya.", 36 | } 37 | 38 | export default id; 39 | -------------------------------------------------------------------------------- /src/lang/locale/ne.ts: -------------------------------------------------------------------------------- 1 | // 尼泊尔语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const ne: Partial = { 6 | "Fast sync": "प्रयोगकर्तालाई बिना बाधा, रेशमी रूपले चिक्नो, विभिन्न उपकरणमा वास्तविक समय समिकरण नोट समिकरण प्लगइन उपलब्ध गराउनेमा ध्यान केन्द्रित गरिन्छ, जुन म्याक, विन्डोज, एन्ड्रोइड, आइओएस लगायत अन्य प्लेटफर्महरूलाई समर्थन गर्दछ र बहुभाषी समर्थन प्रदान गर्दछ।", 7 | "同步全部笔记(覆盖远端)": "सम्पूर्ण नोटहरू समसुकृत गर्नुहोस् (रिमोटलाई अधिलेखन गर्नुहोस्)", 8 | "同步全部笔记": "सम्पूर्ण नोटहरूको समिकरण गर्नुहोस्", 9 | "远端": "टाढाको", 10 | "接口配置信息已经粘贴到设置中!": "सेटिङमा इन्टरफेस कन्फिगरेसन जानकारी पेस्ट भइसकेको छ!", 11 | "未检测到配置信息!": "कन्फिगरेसन जानकारी फेला परेन!", 12 | "远端服务搭建与选择": "दूरस्थ सेवा सेटअप र चयन", 13 | "选择一个适合自己的远端": "आफ्नो लागि उपयुक्त टाढाको चयन गर्नुहोस्", 14 | "方式": "तरीका", 15 | "说明": "निर्देशन", 16 | "详情参考": "विवरणलाई सन्दर्भ गर्नुहोस्।", 17 | "私有服务搭建": "निजी सेवा सेटअप", 18 | "速度好, 自由配置, 无隐私风险": "गति राम्रो छ, स्वतन्त्र कन्फिगरेसन, गोपनीयताको कुनै जोखिम छैन।", 19 | "粘贴的远端配置": "पेस्ट गरिएको टाढाको कन्फिगरेसन", 20 | "启用同步": "सिंक गर्नुहोस् सक्षम गर्नुहोस्", 21 | "关闭后您的笔记将不做任何同步": "बन्द भएपछि तपाईंको नोटहरू कुनै पनि समकक्षी हुनेछैनन्।", 22 | "远端服务地址": "दूरस्थ सेवा ठेगाना", 23 | "选择一个 Fast note sync service 服务地址": "Fast note sync service सेवा ठेगाना चयन गर्नुहोस्।", 24 | "输入您的 Fast note sync service 服务地址": "तपाईंको छवि एपीआई गेटवे ठेगाना लेख्नुहोस्।", 25 | "远端服务令牌": "दूरस्थ सेवा टोकन", 26 | "用于远端服务的访问授权令牌": "दूरस्थ सेवाहरूको लागि पहुँच प्राधिकरण टोकन", 27 | "输入您的 API 访问令牌": "तपाईंको API पहुँच टोकन प्रविष्ट गर्नुहोस्", 28 | "远端仓库名": "दूरस्थ भण्डारण नाम", 29 | "支持": "समर्थन", 30 | "捐赠": "दान गर्नुहोस्", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "यदि तपाईंलाई यो प्लगइन मन पर्छ भने, कृपया निरन्तर विकासलाई समर्थन गर्न दान गर्न विचार गर्नुहोस्।", 32 | "Buy me a coffee at ko-fi.com": "को-फी डट कममा मलाई कफी किनिदिनुहोस्", 33 | "复制 Debug 信息": "डिबग जानकारी प्रतिलिपि गर्नुहोस्।", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "डिबग जानकारी क्लिपबोर्डमा प्रतिलिपि गर्नुहोस्, संवेदनशील जानकारी समावेश गर्न सक्दछ!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "कन्सोल खोल्नको लागि सर्टकट कुञ्जीहरू प्रयोग गरेर, तपाईं यस प्लगइन र अन्य प्लगइनहरूको लग हेर्न सक्नुहुन्छ।", 36 | } 37 | 38 | export default ne; 39 | -------------------------------------------------------------------------------- /src/lang/locale/pt-br.ts: -------------------------------------------------------------------------------- 1 | // 巴西葡萄牙语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const pt_br: Partial = { 6 | "Fast sync": "Concentramo-nos em fornecer um plugin de sincronização de notas sem interrupções, suave como seda, com sincronização em tempo real entre várias plataformas, como Mac, Windows, Android, iOS e muitas outras, além de suporte multilíngue.", 7 | "同步全部笔记(覆盖远端)": "Sincronizar todas as notas (substituir o remoto)", 8 | "同步全部笔记": "Sincronizar todas as notas", 9 | "远端": "remoto", 10 | "接口配置信息已经粘贴到设置中!": "As informações de configuração da interface já foram coladas nas configurações!", 11 | "未检测到配置信息!": "Configuração não detectada!", 12 | "远端服务搭建与选择": "Configuração e seleção de serviços remotos", 13 | "选择一个适合自己的远端": "Escolha um remoto que se adapte a você", 14 | "方式": "Maneira", 15 | "说明": "Instruções", 16 | "详情参考": "Detalhes de referência", 17 | "私有服务搭建": "Configuração de serviços privados", 18 | "速度好, 自由配置, 无隐私风险": "Boa velocidade, configuração livre, sem risco à privacidade", 19 | "粘贴的远端配置": "Configuração remota colada", 20 | "启用同步": "Ativar sincronização", 21 | "关闭后您的笔记将不做任何同步": "Após o fechamento, suas anotações não serão sincronizadas.", 22 | "远端服务地址": "Endereço do serviço remoto", 23 | "选择一个 Fast note sync service 服务地址": "Selecione um endereço de serviço Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Insira o endereço do seu Image API Gateway", 25 | "远端服务令牌": "Token de Serviço Remoto", 26 | "用于远端服务的访问授权令牌": "Token de autorização de acesso para serviços remotos", 27 | "输入您的 API 访问令牌": "Insira seu token de acesso à API", 28 | "远端仓库名": "Nome do repositório remoto", 29 | "支持": "Apoio", 30 | "捐赠": "Doação", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Se você gosta deste plugin, considere fazer uma doação para apoiar o desenvolvimento contínuo.", 32 | "Buy me a coffee at ko-fi.com": "Compre-me um café em ko-fi.com", 33 | "复制 Debug 信息": "Copiar informações de depuração", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Copiar informações de depuração para a área de transferência, pode conter informações sensíveis!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Para abrir o console com um atalho de teclado, você pode ver os registros deste plugin e de outros plugins.", 36 | } 37 | 38 | export default pt_br; 39 | -------------------------------------------------------------------------------- /src/lang/locale/be.ts: -------------------------------------------------------------------------------- 1 | // 白俄罗斯语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const be: Partial = { 6 | "Fast sync": "Сярэдчыць на прадастаўленне карыстальнікам бясстрымнага, шаўкавістых, рэальнага часу сінхранізаванага на некалькіх платформах плагіна для сінхранізацыі нататнікаў, які падтрымлівае платформы Mac, Windows, Android, iOS і іншыя, а таксама прапануе шматмоўную падтрымку.", 7 | "同步全部笔记(覆盖远端)": "Сінхранізаваць усе нататкі (перазапісаць на аддаленай прыладзе)", 8 | "同步全部笔记": "Сінхранізаваць усе нататкі", 9 | "远端": "Адалёны端", 10 | "接口配置信息已经粘贴到设置中!": "Інтэрфэйс канфігурацыйнай інфармацыі быў устаўлены ў налады!", 11 | "未检测到配置信息!": "Канфігурацыйная інфармацыя не знойдзена!", 12 | "远端服务搭建与选择": "Аддаленае наладжванне і выбар сэрвісу", 13 | "选择一个适合自己的远端": "Абярыце прыдатны дыстанцыйны.", 14 | "方式": "спосаб", 15 | "说明": "тлумачэнне", 16 | "详情参考": "Падрабязнасці глядзіце", 17 | "私有服务搭建": "Прыватная служба настройка", 18 | "速度好, 自由配置, 无隐私风险": "Хуткасць добрая, свабодная канфігурацыя, няма рызыкі канфідэнцыяльнасці", 19 | "粘贴的远端配置": "ўстаўлены аддалены канфігурацыі", 20 | "启用同步": "Уключыць сінхранізацыю", 21 | "关闭后您的笔记将不做任何同步": "Пасля закрыцця вашыя нататкі не будуць сінхранізавацца", 22 | "远端服务地址": "Адрас аддаленай службы", 23 | "选择一个 Fast note sync service 服务地址": "Выберыце адрас службы Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Увядзіце ваш адрас Image API Gateway", 25 | "远端服务令牌": "Аддалены токен сэрвісу", 26 | "用于远端服务的访问授权令牌": "Дазволены токен для доступу да дыстанцыйнага абслугоўвання", 27 | "输入您的 API 访问令牌": "Увядзіце ваш токен доступу API", 28 | "远端仓库名": "Аддаленая назва сховішча", 29 | "支持": "падтрымка", 30 | "捐赠": "ахвяраванне", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Калі вам падабаецца гэты плагін, калі ласка, падумайце аб ахвяраванні для падтрымкі далейшай распрацоўкі.", 32 | "Buy me a coffee at ko-fi.com": "Купіце мне каву на ko-fi.com", 33 | "复制 Debug 信息": "Капіруйце Debug інфармацыю", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Скапіруйце адладкавую інфармацыю ў буфер абмену, гэта можа ўтрымліваць канфідэнцыйную інфармацыю!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Каб адкрыць кансоль з дапамогай спалучэння клавіш, вы можаце ўбачыць журналы гэтага плагіна і іншых плагінаў.", 36 | } 37 | 38 | export default be; 39 | -------------------------------------------------------------------------------- /src/lang/locale/pt.ts: -------------------------------------------------------------------------------- 1 | // 葡萄牙语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const pt: Partial = { 6 | "Fast sync": "Focado em fornecer aos usuários um plugin de sincronização de anotações sem interrupções, com suavidade sedosa e sincronização em tempo real em várias plataformas, com suporte para Mac, Windows, Android, iOS, entre outros, e oferece suporte a vários idiomas.", 7 | "同步全部笔记(覆盖远端)": "Sincronizar todas as notas (sobrescrever remoto)", 8 | "同步全部笔记": "Sincronizar todas as notas", 9 | "远端": "remoto", 10 | "接口配置信息已经粘贴到设置中!": "As informações de configuração da interface já foram coladas nas configurações!", 11 | "未检测到配置信息!": "Informações de configuração não detectadas!", 12 | "远端服务搭建与选择": "Configuração e seleção de serviços remotos", 13 | "选择一个适合自己的远端": "Escolha um remoto adequado para si.", 14 | "方式": "método", 15 | "说明": "Instruções", 16 | "详情参考": "Detalhes de referência", 17 | "私有服务搭建": "Configuração de serviço privado", 18 | "速度好, 自由配置, 无隐私风险": "Boa velocidade, configuração livre, sem risco de privacidade", 19 | "粘贴的远端配置": "Configuração remota colada", 20 | "启用同步": "Ativar sincronização", 21 | "关闭后您的笔记将不做任何同步": "Após o encerramento, suas anotações não serão sincronizadas.", 22 | "远端服务地址": "Endereço do serviço remoto", 23 | "选择一个 Fast note sync service 服务地址": "Escolha um endereço de serviço Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Insira o endereço da sua API Gateway de Imagem", 25 | "远端服务令牌": "Token de serviço remoto", 26 | "用于远端服务的访问授权令牌": "Token de autorização de acesso para serviços remotos", 27 | "输入您的 API 访问令牌": "Insira seu token de acesso à API", 28 | "远端仓库名": "Nome do repositório remoto", 29 | "支持": "apoio", 30 | "捐赠": "Doação", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Se gostar deste plugin, considere fazer uma doação para apoiar o desenvolvimento contínuo.", 32 | "Buy me a coffee at ko-fi.com": "Compre-me um café em ko-fi.com", 33 | "复制 Debug 信息": "Copiar informações de depuração", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Copiar informações de depuração para a área de transferência, pode conter informações sensíveis!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Para abrir o console usando atalhos de teclado, você pode ver os registros deste plug-in e de outros plug-ins.", 36 | } 37 | 38 | export default pt; 39 | -------------------------------------------------------------------------------- /src/lang/locale/sq.ts: -------------------------------------------------------------------------------- 1 | // 阿尔巴尼亚语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const sq: Partial = { 6 | "Fast sync": "Përqendrohuni në ofrimin e një shtojce për sinkronizim shënimesh pa ndërprerje, me një veprim të lëmuar dhe sinkronizim në kohë reale në shumë pajisje, mbështet platformat Mac, Windows, Android, iOS etj., dhe ofron mbështetje për shumë gjuhë.", 7 | "同步全部笔记(覆盖远端)": "Sinkronizo të gjitha shënimet (mbulo të largëtën)", 8 | "同步全部笔记": "Sinkronizo të gjitha shënimet", 9 | "远端": "I largët", 10 | "接口配置信息已经粘贴到设置中!": "Informacioni i konfigurimit të ndërfaqes është ngjitur tashmë në cilësime!", 11 | "未检测到配置信息!": "Nuk u zbulua informacioni i konfigurimit!", 12 | "远端服务搭建与选择": "Ngritja dhe zgjedhja e shërbimeve të largëta", 13 | "选择一个适合自己的远端": "Zgjidh një të largët që të përshtatet ty", 14 | "方式": "mënyrë", 15 | "说明": "Shpjegim", 16 | "详情参考": "Shiko detajet", 17 | "私有服务搭建": "Shërbime private të ndërtimit", 18 | "速度好, 自由配置, 无隐私风险": "Shpejtësia është e mirë, konfigurimi i lirë, pa rrezik për privatësinë", 19 | "粘贴的远端配置": "Konfigurimi i largët i ngjitur", 20 | "启用同步": "Aktivizo sinkronizimin", 21 | "关闭后您的笔记将不做任何同步": "Pas mbylljes, shënimet tuaja nuk do të sinkronizohen.", 22 | "远端服务地址": "Adresa e shërbimit të largët", 23 | "选择一个 Fast note sync service 服务地址": "Zgjidhni një adresë shërbimi Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Vendosni adresën tuaj të API Gateway Image", 25 | "远端服务令牌": "Shenjë shërbimi i largët", 26 | "用于远端服务的访问授权令牌": "Token i autorizimit të qasjes për shërbimin e largët", 27 | "输入您的 API 访问令牌": "Futni tokenin e aksesit të API tuaj", 28 | "远端仓库名": "Emri i depozitës së largët", 29 | "支持": "Mbështetje", 30 | "捐赠": "dhurim", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Nëse ju pëlqen ky shtojcë, ju lutem konsideroni donacionin për të mbështetur zhvillimin e mëtejshëm.", 32 | "Buy me a coffee at ko-fi.com": "Blerëm një kafe tek ko-fi.com", 33 | "复制 Debug 信息": "Kopjoni informacionin e Debug", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Kopjoni informacionin e shkyçjes në kujtesën e përkohshme, mund të përmbajë informacion të ndjeshëm!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Përmes shkurtoreve hapni konsolën, mund të shihni regjistrat e këtij shtojce dhe të shtojcave të tjera.", 36 | } 37 | 38 | export default sq; 39 | -------------------------------------------------------------------------------- /src/lang/locale/uk.ts: -------------------------------------------------------------------------------- 1 | // 乌克兰语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const uk: Partial = { 6 | "Fast sync": "Зосередьтеся на наданні користувачам безперебійного, плавного досвіду та миттєвої синхронізації нотаток на різних пристроях, підтримуйте такі платформи, як Mac, Windows, Android, iOS тощо, а також забезпечуйте багатомовну підтримку.", 7 | "同步全部笔记(覆盖远端)": "Синхронізувати всі нотатки (перезаписати на віддаленому сервері)", 8 | "同步全部笔记": "Синхронізувати всі нотатки", 9 | "远端": "віддалений", 10 | "接口配置信息已经粘贴到设置中!": "Інформація про конфігурацію інтерфейсу вже вставлена в налаштування!", 11 | "未检测到配置信息!": "Конфігураційні дані не виявлено!", 12 | "远端服务搭建与选择": "Налаштування та вибір віддалених сервісів", 13 | "选择一个适合自己的远端": "Виберіть відповідний для себе віддалений сервер.", 14 | "方式": "спосіб", 15 | "说明": "Інструкція", 16 | "详情参考": "Детальна інформація розглядається", 17 | "私有服务搭建": "Запуск приватних послуг", 18 | "速度好, 自由配置, 无隐私风险": "Швидкість хороша, вільна конфігурація, без ризику порушення конфіденційності", 19 | "粘贴的远端配置": "Вставлений віддалений конфігураційний файл", 20 | "启用同步": "Увімкнути синхронізацію", 21 | "关闭后您的笔记将不做任何同步": "Після закриття ваші нотатки не будуть синхронізовані.", 22 | "远端服务地址": "Віддалена адреса сервісу", 23 | "选择一个 Fast note sync service 服务地址": "Виберіть адресу служби Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Введіть адресу вашого Image API Gateway", 25 | "远端服务令牌": "Токен служби на віддаленому сервері", 26 | "用于远端服务的访问授权令牌": "Токен авторизації доступу для віддалених послуг", 27 | "输入您的 API 访问令牌": "Введіть свій токен доступу API", 28 | "远端仓库名": "Віддалене ім'я сховища", 29 | "支持": "підтримка", 30 | "捐赠": "пожертва", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Якщо вам подобається цей плагін, будь ласка, розгляньте можливість пожертвування для підтримки подальшої розробки.", 32 | "Buy me a coffee at ko-fi.com": "Придбай мені каву на ko-fi.com", 33 | "复制 Debug 信息": "Скопіювати відладку інформації", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Копіювання налагоджувальної інформації в буфер обміну може містити конфіденційні дані!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Щоб відкрити консоль за допомогою гарячих клавіш, ви можете побачити журнали цього плагіна та інших плагінів.", 36 | } 37 | 38 | export default uk; 39 | -------------------------------------------------------------------------------- /src/lang/locale/de.ts: -------------------------------------------------------------------------------- 1 | // 德语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const de: Partial = { 6 | "Fast sync": "Fokus auf die Bereitstellung eines störungsfreien, seidenglattes und geräteübergreifend in Echtzeit synchronisiertes Notiz-Synchronisations-Plugins für Benutzer, das Plattformen wie Mac, Windows, Android, iOS unterstützt und mehrsprachigen Support bietet.", 7 | "同步全部笔记(覆盖远端)": "Alle Notizen synchronisieren (Remote überschreiben)", 8 | "同步全部笔记": "Alle Notizen synchronisieren", 9 | "远端": "Fernbedienung", 10 | "接口配置信息已经粘贴到设置中!": "Die Schnittstelleneinstellungen wurden in die Einstellungen eingefügt!", 11 | "未检测到配置信息!": "Konfigurationsinformationen nicht erkannt!", 12 | "远端服务搭建与选择": "Aufbau und Auswahl von Remote-Diensten", 13 | "选择一个适合自己的远端": "Wählen Sie ein passendes Remote aus", 14 | "方式": "Methode", 15 | "说明": "Anleitung", 16 | "详情参考": "Einzelheiten siehe", 17 | "私有服务搭建": "Einrichtung privater Dienste", 18 | "速度好, 自由配置, 无隐私风险": "Gute Geschwindigkeit, frei konfigurierbar, kein Privatsphärenrisiko", 19 | "粘贴的远端配置": "Eingefügte Remote-Konfiguration", 20 | "启用同步": "Synchronisation aktivieren", 21 | "关闭后您的笔记将不做任何同步": "Nach dem Schließen wird Ihre Notiz nicht mehr synchronisiert.", 22 | "远端服务地址": "Remote-Dienstadresse", 23 | "选择一个 Fast note sync service 服务地址": "Wählen Sie eine Fast note sync service-Adresse aus", 24 | "输入您的 Fast note sync service 服务地址": "Geben Sie Ihre Image API Gateway-Adresse ein", 25 | "远端服务令牌": "Remote-Service-Token", 26 | "用于远端服务的访问授权令牌": "Zugangsberechtigungstoken für Remote-Dienste", 27 | "输入您的 API 访问令牌": "Geben Sie Ihr API-Zugriffstoken ein", 28 | "远端仓库名": "Name des entfernten Repositorys", 29 | "支持": "Unterstützung", 30 | "捐赠": "Spende", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Wenn Ihnen dieses Plugin gefällt, ziehen Sie bitte eine Spende in Betracht, um die weitere Entwicklung zu unterstützen.", 32 | "Buy me a coffee at ko-fi.com": "Kauf mir einen Kaffee auf ko-fi.com", 33 | "复制 Debug 信息": "Debug-Informationen kopieren", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Kopieren von Debug-Informationen in die Zwischenablage kann sensible Informationen enthalten!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Öffnen Sie die Konsole mit einer Tastenkombination, um die Protokolle dieses Plugins und anderer Plugins anzuzeigen.", 36 | } 37 | 38 | export default de; 39 | -------------------------------------------------------------------------------- /src/lang/locale/es.ts: -------------------------------------------------------------------------------- 1 | // 西班牙语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const es: Partial = { 6 | "Fast sync": "Enfocado en proporcionar al usuario un complemento de sincronización de notas sin interrupciones, con suavidad sedosa y sincronización en tiempo real en múltiples dispositivos, compatible con plataformas como Mac, Windows, Android, iOS, y con soporte multilingüe.", 7 | "同步全部笔记(覆盖远端)": "Sincronizar todas las notas (sobrescribir remoto)", 8 | "同步全部笔记": "Sincronizar todas las notas", 9 | "远端": "remoto", 10 | "接口配置信息已经粘贴到设置中!": "¡La información de configuración de la interfaz ya se ha pegado en la configuración!", 11 | "未检测到配置信息!": "¡No se detectó información de configuración!", 12 | "远端服务搭建与选择": "Configuración y selección de servicios remotos", 13 | "选择一个适合自己的远端": "Elige un remoto adecuado para ti", 14 | "方式": "Método", 15 | "说明": "instrucciones", 16 | "详情参考": "Detalles de referencia", 17 | "私有服务搭建": "Configuración de servicios privados", 18 | "速度好, 自由配置, 无隐私风险": "Velocidad buena, configuración libre, sin riesgos de privacidad", 19 | "粘贴的远端配置": "Configuración remota pegada", 20 | "启用同步": "Habilitar sincronización", 21 | "关闭后您的笔记将不做任何同步": "Después de cerrar, sus notas no se sincronizarán.", 22 | "远端服务地址": "Dirección del servicio remoto", 23 | "选择一个 Fast note sync service 服务地址": "Elige una dirección de servicio de Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Ingrese la dirección de su Image API Gateway", 25 | "远端服务令牌": "Token de servicio remoto", 26 | "用于远端服务的访问授权令牌": "Token de autorización de acceso para servicios remotos", 27 | "输入您的 API 访问令牌": "Ingrese su token de acceso API", 28 | "远端仓库名": "Nombre del repositorio remoto", 29 | "支持": "Soporte", 30 | "捐赠": "Donación", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Si te gusta este complemento, considera realizar una donación para apoyar su desarrollo continuo.", 32 | "Buy me a coffee at ko-fi.com": "Cómpreme un café en ko-fi.com", 33 | "复制 Debug 信息": "Copiar información de depuración", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Copiar la información de depuración al portapapeles, ¡podría contener información sensible!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Usando atajos de teclado para abrir la consola, puedes ver los registros de este complemento y otros complementos.", 36 | } 37 | 38 | export default es; 39 | -------------------------------------------------------------------------------- /src/lang/locale/ru.ts: -------------------------------------------------------------------------------- 1 | // 俄语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const ru: Partial = { 6 | "Fast sync": "Плагин для синхронизации заметок, предоставляющий пользователям возможность беспрепятственной, плавной и многоплатформенной синхронизации в режиме реального времени, поддерживает платформы Mac, Windows, Android, iOS и предлагает поддержку нескольких языков.", 7 | "同步全部笔记(覆盖远端)": "Синхронизировать все заметки (перезаписать на удаленном хранилище)", 8 | "同步全部笔记": "Синхронизировать все заметки", 9 | "远端": "Удаленный", 10 | "接口配置信息已经粘贴到设置中!": "Информация о конфигурации интерфейса уже вставлена в настройки!", 11 | "未检测到配置信息!": "Конфигурационная информация не обнаружена!", 12 | "远端服务搭建与选择": "Настройка и выбор удаленных сервисов", 13 | "选择一个适合自己的远端": "Выберите подходящий для себя удалённый сервер.", 14 | "方式": "Способ", 15 | "说明": "Пояснение", 16 | "详情参考": "Подробности см.", 17 | "私有服务搭建": "Настройка частных сервисов", 18 | "速度好, 自由配置, 无隐私风险": "Скорость хорошая, свободная конфигурация, отсутствие риска для конфиденциальности", 19 | "粘贴的远端配置": "Вставленные конфигурации удалённого доступа", 20 | "启用同步": "Включить синхронизацию", 21 | "关闭后您的笔记将不做任何同步": "После закрытия ваши заметки не будут синхронизироваться.", 22 | "远端服务地址": "Удаленный адрес службы", 23 | "选择一个 Fast note sync service 服务地址": "Выберите адрес службы Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Введите адрес вашего Image API Gateway", 25 | "远端服务令牌": "Токен удалённого сервиса", 26 | "用于远端服务的访问授权令牌": "Токен авторизации для доступа к удалённым сервисам", 27 | "输入您的 API 访问令牌": "Введите ваш API токен доступа", 28 | "远端仓库名": "Удалённое имя хранилища", 29 | "支持": "поддержка", 30 | "捐赠": "пожертвование", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Если вам нравится этот плагин, пожалуйста, рассмотрите возможность внести пожертвование для поддержки дальнейшей разработки.", 32 | "Buy me a coffee at ko-fi.com": "Купи мне кофе на ko-fi.com", 33 | "复制 Debug 信息": "Скопировать информацию отладки", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Скопировать отладочную информацию в буфер обмена, может содержать конфиденциальную информацию!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Чтобы открыть консоль с помощью горячих клавиш, вы можете увидеть журналы этого плагина и других плагинов.", 36 | } 37 | 38 | export default ru; 39 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | .fast-note-sync-ribbon-icon { 3 | animation: rotate 2s linear infinite; 4 | /* 旋转动画:2秒一圈,线性,无限循环 */ 5 | } 6 | 7 | @keyframes rotate { 8 | from { 9 | transform: rotate(0deg); 10 | } 11 | to { 12 | transform: rotate(-360deg); 13 | } 14 | } 15 | .fast-note-sync-settings-tag .setting-item-name { 16 | color: var(--interactive-accent); 17 | } 18 | 19 | .fast-note-sync-settings-support .ko-fi-logo { 20 | height: 36px !important; 21 | border: 0px !important; 22 | } 23 | 24 | .fast-note-sync-settings-debug { 25 | margin: var(--size-4-2); 26 | text-align: center; 27 | } 28 | .fast-note-sync-settings-debug button { 29 | margin: var(--size-4-2); 30 | } 31 | 32 | .fast-note-sync-settings { 33 | padding: 0.75em 0; 34 | border-top: 1px solid var(--background-modifier-border); 35 | } 36 | .fast-note-sync-settings table { 37 | width: 100%; 38 | border: 1px solid var(--background-modifier-border); 39 | margin-bottom: 20px; 40 | } 41 | .fast-note-sync-settings table a { 42 | color: var(--color-blue); 43 | text-decoration: none; 44 | } 45 | .fast-note-sync-settings table a:hover { 46 | text-decoration: underline; 47 | } 48 | .fast-note-sync-settings table th, .fast-note-sync-settings table td { 49 | border: 0 !important; 50 | } 51 | .fast-note-sync-settings table th { 52 | text-align: center; 53 | padding: 8px 10px; 54 | font-size: var(--font-ui-smaller); 55 | background-color: var(--background-secondary); 56 | } 57 | .fast-note-sync-settings table .no-columns-added { 58 | padding: 15px; 59 | text-align: center; 60 | background-color: var(--background-secondary); 61 | } 62 | .fast-note-sync-settings table .no-columns-added p { 63 | margin: 4px; 64 | font-size: 10px; 65 | } 66 | .fast-note-sync-settings table thead { 67 | border-bottom: 1px solid var(--color-base-40) !important; 68 | } 69 | .fast-note-sync-settings table tbody tr { 70 | background-color: var(--background-secondary); 71 | } 72 | .fast-note-sync-settings table tbody tr:nth-child(odd) { 73 | background-color: var(--background-secondary-alt); 74 | } 75 | .fast-note-sync-settings table tbody td { 76 | padding: 4px 8px; 77 | font-size: var(--font-ui-small); 78 | } 79 | .fast-note-sync-settings table tfoot td { 80 | padding: 8px 10px; 81 | } 82 | 83 | /*# sourceMappingURL=styles.css.map */ 84 | -------------------------------------------------------------------------------- /src/lang/locale/ro.ts: -------------------------------------------------------------------------------- 1 | // 罗马尼亚语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const ro: Partial = { 6 | "Fast sync": "Concentrați-vă pe oferirea utilizatorilor unui plugin de sincronizare a notițelor fără întreruperi, neted ca mătasea, cu sincronizare în timp real pe mai multe platforme, acceptând platformele Mac, Windows, Android, iOS și oferind suport multilingv.", 7 | "同步全部笔记(覆盖远端)": "Sincronizați toate notele (suprascrieți distanța)", 8 | "同步全部笔记": "Sincronizați toate notele", 9 | "远端": "Distantă", 10 | "接口配置信息已经粘贴到设置中!": "Informațiile de configurare a interfeței au fost lipite în setări!", 11 | "未检测到配置信息!": "Nu s-au detectat informații de configurare!", 12 | "远端服务搭建与选择": "Configurarea și selecția serviciilor la distanță", 13 | "选择一个适合自己的远端": "Alege un control de la distanță care ți se potrivește.", 14 | "方式": "metodă", 15 | "说明": "instrucțiuni", 16 | "详情参考": "Detalii de referință", 17 | "私有服务搭建": "Construirea serviciului privat", 18 | "速度好, 自由配置, 无隐私风险": "Viteză bună, configurare liberă, fără riscuri de confidențialitate", 19 | "粘贴的远端配置": "Configurație lipită de la distanță", 20 | "启用同步": "Activare sincronizare", 21 | "关闭后您的笔记将不做任何同步": "După închidere, notițele dumneavoastră nu vor fi sincronizate.", 22 | "远端服务地址": "Adresa serviciului la distanță", 23 | "选择一个 Fast note sync service 服务地址": "Alegeți o adresă de serviciu Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Introduceți adresa dvs. de Image API Gateway", 25 | "远端服务令牌": "Token de serviciu la distanță", 26 | "用于远端服务的访问授权令牌": "Token de autorizare pentru accesul la serviciile remote", 27 | "输入您的 API 访问令牌": "Introduceți tokenul de acces API.", 28 | "远端仓库名": "Numele depozitului la distanță", 29 | "支持": "sprijin", 30 | "捐赠": "Donație", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Dacă vă place acest plugin, luați în considerare donația pentru a sprijini continuarea dezvoltării.", 32 | "Buy me a coffee at ko-fi.com": "Cumpără-mi o cafea la ko-fi.com", 33 | "复制 Debug 信息": "Copiați informațiile de depanare", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Copiați informațiile de depanare în clipboard, pot conține informații sensibile!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Puteți deschide consola printr-o comandă rapidă de la tastatură pentru a vedea jurnalul acestui plugin și al altor pluginuri.", 36 | } 37 | 38 | export default ro; 39 | -------------------------------------------------------------------------------- /src/lang/locale/it.ts: -------------------------------------------------------------------------------- 1 | // 意大利语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const it: Partial = { 6 | "Fast sync": "Concentrati a fornire agli utenti un plugin di sincronizzazione delle note senza interruzioni, fluido come la seta e sincronizzato in tempo reale su più dispositivi, supportando piattaforme come Mac, Windows, Android, iOS e offrendo supporto multilingue.", 7 | "同步全部笔记(覆盖远端)": "Sincronizza tutte le note (sovrascrivi remoto)", 8 | "同步全部笔记": "Sincronizza tutte le note", 9 | "远端": "remoto", 10 | "接口配置信息已经粘贴到设置中!": "Le informazioni di configurazione dell'interfaccia sono già state incollate nelle impostazioni!", 11 | "未检测到配置信息!": "Informazioni di configurazione non rilevate!", 12 | "远端服务搭建与选择": "Configurazione e scelta del servizio remoto", 13 | "选择一个适合自己的远端": "Scegli un remoto adatto a te stesso.", 14 | "方式": "modalità", 15 | "说明": "istruzioni", 16 | "详情参考": "Fare riferimento ai dettagli", 17 | "私有服务搭建": "Realizzazione di servizi privati", 18 | "速度好, 自由配置, 无隐私风险": "Velocità buona, configurazione libera, nessun rischio per la privacy.", 19 | "粘贴的远端配置": "Configurazione remota incollata", 20 | "启用同步": "Abilita sincronizzazione", 21 | "关闭后您的笔记将不做任何同步": "Dopo la chiusura, le tue note non verranno sincronizzate", 22 | "远端服务地址": "Indirizzo del servizio remoto", 23 | "选择一个 Fast note sync service 服务地址": "Scegli un indirizzo del servizio Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Inserisci l'indirizzo del tuo gateway API per le immagini", 25 | "远端服务令牌": "Token di servizio remoto", 26 | "用于远端服务的访问授权令牌": "Token di autorizzazione all'accesso per servizi remoti", 27 | "输入您的 API 访问令牌": "Inserisci il tuo token di accesso API", 28 | "远端仓库名": "Nome del repository remoto", 29 | "支持": "sostegno", 30 | "捐赠": "Donazione", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Se ti piace questo plugin, considera di fare una donazione per supportare lo sviluppo continuo.", 32 | "Buy me a coffee at ko-fi.com": "Comprami un caffè su ko-fi.com", 33 | "复制 Debug 信息": "Copia informazioni di debug", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Copia le informazioni di debug negli appunti, potrebbe contenere informazioni sensibili!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Premendo i tasti di scelta rapida per aprire la console, puoi vedere i log di questo plugin e di altri plugin.", 36 | } 37 | 38 | export default it; 39 | -------------------------------------------------------------------------------- /src/lang/locale/hu.ts: -------------------------------------------------------------------------------- 1 | // 匈牙利语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const hu: Partial = { 6 | "Fast sync": "A hangsúly az, hogy zavartalan, selymesen sima, többplatformos, valós idejű szinkronizálást biztosító jegyzettömb bővítményt kínáljon a felhasználóknak, amely támogatja a Mac, Windows, Android, iOS és más platformokat, és több nyelvi támogatást nyújt.", 7 | "同步全部笔记(覆盖远端)": "Szinkronizálja az összes jegyzetet (távoli felülírása)", 8 | "同步全部笔记": "Minden jegyzet szinkronizálása", 9 | "远端": "Távoli", 10 | "接口配置信息已经粘贴到设置中!": "A felület konfigurációs információi már beillesztésre kerültek a beállításokba!", 11 | "未检测到配置信息!": "A konfigurációs információk nem észlelhetők!", 12 | "远端服务搭建与选择": "Távoli szolgáltatások felépítése és kiválasztása", 13 | "选择一个适合自己的远端": "Válassz egy számodra megfelelő távoli helyet", 14 | "方式": "mód", 15 | "说明": "Magyarázat", 16 | "详情参考": "Részletekért tekintse meg", 17 | "私有服务搭建": "Magánszolgáltatás kiépítése", 18 | "速度好, 自由配置, 无隐私风险": "Gyors, szabadon konfigurálható, nincs adatvédelmi kockázat", 19 | "粘贴的远端配置": "Távoli konfiguráció beillesztése", 20 | "启用同步": "Szinkronizálás engedélyezése", 21 | "关闭后您的笔记将不做任何同步": "A bezárás után a jegyzetei nem lesznek szinkronizálva.", 22 | "远端服务地址": "A távoli szolgáltatás címe", 23 | "选择一个 Fast note sync service 服务地址": "Válasszon egy Fast note sync service szolgáltatáscímet", 24 | "输入您的 Fast note sync service 服务地址": "Kérjük, adja meg az Image API Gateway címét", 25 | "远端服务令牌": "Távoli szolgáltatási token", 26 | "用于远端服务的访问授权令牌": "Távoli szolgáltatásokhoz használt hozzáférési jogosultsági token", 27 | "输入您的 API 访问令牌": "Kérjük, adja meg az API hozzáférési tokenjét", 28 | "远端仓库名": "Távoli tároló neve", 29 | "支持": "támogatás", 30 | "捐赠": "Adományozás", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Ha tetszik ez a bővítmény, kérjük, fontolja meg az adományozást a további fejlesztés támogatására.", 32 | "Buy me a coffee at ko-fi.com": "Vegyél nekem egy kávét a ko-fi.com-on", 33 | "复制 Debug 信息": "Hibakeresési információk másolása", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Másolja a hibakeresési információkat a vágólapra, érzékeny információkat tartalmazhat!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "A vezérlőpult gyorsbillentyű segítségével történő megnyitásával megtekintheti ezt a bővítményt és más bővítmények naplóit.", 36 | } 37 | 38 | export default hu; 39 | -------------------------------------------------------------------------------- /src/lang/locale/ca.ts: -------------------------------------------------------------------------------- 1 | // 加泰罗尼亚语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const ca: Partial = { 6 | "Fast sync": "Ens concéntrens a proporcionar als usuaris un complement de sincronització de notes sense interrupcions, suau com la seda i de sincronització en temps real a diversos dispositius, compatible amb plataformes com Mac, Windows, Android, iOS, i oferim suport multilingüe.", 7 | "同步全部笔记(覆盖远端)": "Sincronitza totes les notes (sobreescriu les remotes)", 8 | "同步全部笔记": "Sincronitza totes les notes", 9 | "远端": "remot", 10 | "接口配置信息已经粘贴到设置中!": "La informació de configuració de la interfície ja s'ha enganxat a la configuració!", 11 | "未检测到配置信息!": "No s'han detectat informacions de configuració!", 12 | "远端服务搭建与选择": "Implementació i selecció de serveis remots", 13 | "选择一个适合自己的远端": "Trieu un remot que et convingui", 14 | "方式": "manera", 15 | "说明": "Instruccions", 16 | "详情参考": "Detalls de referència", 17 | "私有服务搭建": "Serveis privats d'instal·lació", 18 | "速度好, 自由配置, 无隐私风险": "Bona velocitat, configuració lliure, sense risc de privadesa", 19 | "粘贴的远端配置": "Configuració remota enganxada", 20 | "启用同步": "Activa la sincronització", 21 | "关闭后您的笔记将不做任何同步": "Un cop tancat, les vostres notes no es sincronitzaran.", 22 | "远端服务地址": "adreça del servei remot", 23 | "选择一个 Fast note sync service 服务地址": "Seleccioneu una adreça de servei d'Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Si us plau, introduïu la vostra adreça del Gateway de l'API d'Imatge", 25 | "远端服务令牌": "Testimoni del servei remot", 26 | "用于远端服务的访问授权令牌": "Token d'autorització d'accés per a serveis remots", 27 | "输入您的 API 访问令牌": "Introduïu el vostre testimoni d'accés a l'API", 28 | "远端仓库名": "Nom del repositori remot", 29 | "支持": "Suport", 30 | "捐赠": "Donació", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Si us agrada aquest complement, si us plau, considereu fer una donació per donar suport a la continuació del desenvolupament.", 32 | "Buy me a coffee at ko-fi.com": "Compra'm un cafè a ko-fi.com", 33 | "复制 Debug 信息": "Copia la informació de Debug", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Copia la informació de depuració al porta-retalls, pot contenir informació sensible!", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "Per obrir la consola amb una drecera de teclat, pots veure els registres d'aquest complement i d'altres complements.", 36 | } 37 | 38 | export default ca; 39 | -------------------------------------------------------------------------------- /src/lang/locale/fr.ts: -------------------------------------------------------------------------------- 1 | // 法语 2 | import type { LangMap } from "../lang"; 3 | 4 | 5 | const fr: Partial = { 6 | "Fast sync": "Concentré sur la fourniture d'un plugin de synchronisation de notes sans distraction, extrêmement fluide et avec synchronisation en temps réel sur plusieurs appareils, compatible avec les plateformes Mac, Windows, Android, iOS et offrant une prise en charge multilingue.", 7 | "同步全部笔记(覆盖远端)": "Synchroniser toutes les notes (écraser la distante)", 8 | "同步全部笔记": "Synchroniser toutes les notes", 9 | "远端": "À distance", 10 | "接口配置信息已经粘贴到设置中!": "Les informations de configuration de l'interface ont été collées dans les paramètres !", 11 | "未检测到配置信息!": "Aucune information de configuration détectée !", 12 | "远端服务搭建与选择": "Configuration et choix des services distants", 13 | "选择一个适合自己的远端": "Choisir un emplacement distant qui vous convient", 14 | "方式": "façon", 15 | "说明": "instructions", 16 | "详情参考": "Détails de référence", 17 | "私有服务搭建": "Mise en place de services privés", 18 | "速度好, 自由配置, 无隐私风险": "Vitesse bonne, configuration libre, aucun risque pour la vie privée", 19 | "粘贴的远端配置": "Configuration distante collée", 20 | "启用同步": "Activer la synchronisation", 21 | "关闭后您的笔记将不做任何同步": "Une fois fermé, vos notes ne seront plus synchronisées.", 22 | "远端服务地址": "Adresse du service distant", 23 | "选择一个 Fast note sync service 服务地址": "Choisissez une adresse de service Fast note sync service", 24 | "输入您的 Fast note sync service 服务地址": "Entrez l'adresse de votre passerelle API d'image", 25 | "远端服务令牌": "Jeton de service distant", 26 | "用于远端服务的访问授权令牌": "Jeton d'autorisation d'accès pour services distants", 27 | "输入您的 API 访问令牌": "Veuillez entrer votre jeton d'accès API", 28 | "远端仓库名": "Nom du dépôt distant", 29 | "支持": "Soutien", 30 | "捐赠": "Donner", 31 | "如果您喜欢这个插件,请考虑捐赠以支持继续开发。": "Si vous aimez ce plugin, envisagez de faire un don pour soutenir le développement continu.", 32 | "Buy me a coffee at ko-fi.com": "Achetez-moi un café sur ko-fi.com", 33 | "复制 Debug 信息": "Copier les informations de débogage", 34 | "将调试信息复制到剪贴板, 可能包含敏感信!": "Copier les informations de débogage dans le presse-papiers, elles peuvent contenir des informations sensibles !", 35 | "通过快捷键打开控制台,你可以看到这个插件和其他插件的日志": "En ouvrant la console avec un raccourci clavier, vous pouvez voir les journaux de ce plugin et d'autres plugins.", 36 | } 37 | 38 | export default fr; 39 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | .fast-note-sync-ribbon-icon{ 2 | animation: rotate 2s linear infinite; 3 | /* 旋转动画:2秒一圈,线性,无限循环 */ 4 | } 5 | 6 | @keyframes rotate { 7 | from { 8 | transform: rotate(0deg); 9 | } 10 | 11 | to { 12 | transform: rotate(-360deg); 13 | } 14 | } 15 | 16 | 17 | .fast-note-sync-settings-tag{ 18 | .setting-item-name { 19 | color: var(--interactive-accent); 20 | } 21 | } 22 | 23 | .fast-note-sync-settings-support { 24 | .ko-fi-logo { 25 | height: 36px !important; 26 | border: 0px !important; 27 | } 28 | } 29 | 30 | .fast-note-sync-settings-debug { 31 | margin: var(--size-4-2); 32 | text-align: center; 33 | button { 34 | margin: var(--size-4-2); 35 | } 36 | } 37 | 38 | .fast-note-sync-settings { 39 | padding: 0.75em 0; 40 | border-top: 1px solid var(--background-modifier-border); 41 | 42 | table { 43 | width: 100%; 44 | border: 1px solid var(--background-modifier-border); 45 | margin-bottom: 20px; 46 | 47 | a { 48 | color: var(--color-blue); 49 | text-decoration: none; 50 | } 51 | 52 | a:hover { 53 | text-decoration: underline; 54 | } 55 | 56 | th, td { 57 | border: 0 !important; 58 | } 59 | 60 | th { 61 | text-align: center; 62 | padding: 8px 10px; 63 | font-size: var(--font-ui-smaller); 64 | background-color: var(--background-secondary); 65 | } 66 | 67 | .no-columns-added { 68 | padding: 15px; 69 | text-align: center; 70 | background-color: var(--background-secondary); 71 | 72 | p { 73 | margin: 4px; 74 | font-size: 10px; 75 | } 76 | } 77 | 78 | thead { 79 | border-bottom: 1px solid var(--color-base-40) !important; 80 | } 81 | 82 | tbody { 83 | tr { 84 | background-color: var(--background-secondary); 85 | 86 | &:nth-child(odd) { 87 | background-color: var(--background-secondary-alt); 88 | } 89 | } 90 | 91 | td { 92 | padding: 4px 8px; 93 | font-size: var(--font-ui-small); 94 | } 95 | } 96 | 97 | 98 | tfoot td { 99 | padding: 8px 10px; 100 | } 101 | } 102 | } 103 | 104 | 105 | -------------------------------------------------------------------------------- /docs/README.zh-CN.md: -------------------------------------------------------------------------------- 1 | [中文文档](https://github.com/haierkeys/obsidian-fast-note-sync/blob/master/docs/README.zh-CN.md) / [English Document](https://github.com/haierkeys/obsidian-fast-note-sync/blob/master/README.md) 2 | 3 | 4 |

Fast Note Sync For Obsidian

5 | 6 |

7 | release 8 | alpha-release 9 | license 10 | TypeScript 11 |

12 | 13 | 14 | 15 |

16 | 快速、稳定、高效、任意部署的 Obsidian 笔记 同步&备份 插件 17 |
18 | 可私有化部署,专注为 Obsidian 用户提供无打扰、丝般顺滑、多端实时同步的笔记同步&备份插件, 支持 Mac、Windows、Android、iOS 等平台,并提供多语言支持。 19 |

20 | 21 |

22 | 需配合独立服务端使用:Fast Note Sync Service 23 |

24 | 25 |
26 | fast-note-sync-service-preview 27 |
28 | 29 | 30 | ## ✨ 插件功能 31 | 32 | - **极简配置**:无需繁琐设置,只需粘贴远端服务配置即可开箱即用。 33 | - **笔记实时同步**:自动监听并同步 Vault (仓库) 内所有笔记的创建、更新与删除操作。 34 | - **附件全面支持**:实时同步图片、视频、音频等各类非设置文件。 35 | > ⚠️ **注意**:需要 v1.0+,服务端 v0.9+。请控制附件文件大小,大文件可能会导致同步延迟。 36 | - **多端同步**:支持 Mac、Windows、Android、iOS 等平台。 37 | 38 | ## 🗺️ 路线图 (Roadmap) 39 | 40 | 我们正在持续改进,以下是未来的开发计划: 41 | 42 | - [ ] **服务端版本查看**: 显示服务器的版本信息,方便了解服务器的版本状态。 43 | - [ ] **配置同步**:提供配置同步功能,支持多台设备的配置同步, 告别手动给多端设备拷贝配置文件的痛苦。 44 | - [ ] **笔记历史**:提供笔记历史快照功能,您可以插件端、服务端WebGui,查看笔记的版本历史,回退到之前的版本。 45 | - [ ] **云存储备份状态**:随时查看云存储备份状态功能,方便你了解最新的云存储备份状态。 46 | - [ ] **笔记分享功能**:为您的云端笔记生成分享链接,方便您将自己成果分享给他人。 47 | - [ ] **AI笔记**:探索 AI+ 笔记相关的创新玩法, 等待您提供宝贵的建议。 48 | 49 | > **如果您有改进建议或新想法,欢迎通过提交 issue 与我们分享——我们会认真评估并采纳合适的建议。** 50 | 51 | ## 💰 价格 52 | 53 | - 如果觉得这个插件很有用,并且想要支持它的继续开发,你可以在这里支持我: 54 | [BuyMeACoffee](https://ko-fi.com/haierkeys) 55 | 56 | 57 | ## 🚀 快速开始 58 | 59 | 1. 安装插件 (二选一) 60 | - **官方商店**: 打开 OBSidian 社区插件市场, 搜索 **Fast Note Sync** 安装 61 | > ⚠️ 插件尚未上架官方商店,无法搜索, 请手动安装 62 | - **手动安装**: 访问 https://github.com/haierkeys/obsidian-fast-note-sync/releases 下载安装包, 解压到 Obsidian 插件目录下 **.obsidian/plugin** 63 | 2. 打开插件配置项,点击 **粘贴远端配置** 按钮,将远端服务配置粘贴到输入框中。 64 | 65 | 66 | ## 📦 服务端部署 67 | 68 | 后端服务设置,请参考:[Fast Note Sync Service](https://github.com/haierkeys/fast-note-sync-service)。 69 | -------------------------------------------------------------------------------- /src/views/settings-view.tsx: -------------------------------------------------------------------------------- 1 | import { dump } from "src/lib/helps"; 2 | import FastSync from "src/main"; 3 | 4 | import { $ } from "../lang/lang"; 5 | 6 | 7 | async function getClipboardContent(plugin: FastSync): Promise { 8 | const clipboardReadTipSave = async (api: string, apiToken: string, Vault: string, tip: string) => { 9 | if (plugin.settings.api != api || plugin.settings.apiToken != apiToken) { 10 | plugin.wsSettingChange = true 11 | } 12 | plugin.settings.api = api 13 | plugin.settings.apiToken = apiToken 14 | plugin.settings.vault = Vault 15 | plugin.clipboardReadTip = tip 16 | 17 | await plugin.saveSettings() 18 | plugin.settingTab.display() 19 | 20 | setTimeout(() => { 21 | plugin.clipboardReadTip = "" 22 | }, 2000) 23 | } 24 | 25 | // 26 | const clipboardReadTipTipSave = async (tip: string) => { 27 | plugin.clipboardReadTip = tip 28 | 29 | await plugin.saveData(plugin.settings) 30 | plugin.settingTab.display() 31 | 32 | setTimeout(() => { 33 | plugin.clipboardReadTip = "" 34 | }, 2000) 35 | } 36 | 37 | try { 38 | // 检查浏览器是否支持 Clipboard API 39 | if (!navigator.clipboard) { 40 | return 41 | } 42 | 43 | // 获取剪贴板文本内容 44 | const text = await navigator.clipboard.readText() 45 | 46 | // 检查是否为 JSON 格式 47 | let parsedData = JSON.parse(text) 48 | 49 | // 检查是否为对象且包含 api 和 apiToken 50 | if (typeof parsedData === "object" && parsedData !== null) { 51 | const hasApi = "api" in parsedData 52 | const hasApiToken = "apiToken" in parsedData 53 | const vault = "vault" in parsedData 54 | 55 | if (hasApi && hasApiToken && vault) { 56 | void clipboardReadTipSave(parsedData.api, parsedData.apiToken, parsedData.vault, $("接口配置信息已经粘贴到设置中!")) 57 | return 58 | } 59 | } 60 | void clipboardReadTipTipSave($("未检测到配置信息!")) 61 | return 62 | } catch (err) { 63 | dump(err) 64 | void clipboardReadTipTipSave($("未检测到配置信息!")) 65 | return 66 | } 67 | } 68 | 69 | const handleClipboardClick = (plugin: FastSync) => { getClipboardContent(plugin).catch(err => { dump(err); }); }; 70 | 71 | export const SettingsView = ({ plugin }: { plugin: FastSync }) => { 72 | return ( 73 | <> 74 |
75 |
76 |
{$("远端服务搭建与选择")}
77 |
{$("选择一个适合自己的远端")}
78 |
79 |
80 |
81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 96 | 97 | 98 | 99 |
{$("方式")}{$("说明")}{$("详情参考")}
{$("私有服务搭建")}{$("速度好, 自由配置, 无隐私风险")} 94 | https://github.com/haierkeys/obsidian-fast-note-sync-service 95 |
100 |
101 |
102 | 105 |
{plugin.clipboardReadTip}
106 |
107 | 108 | ) 109 | } 110 | -------------------------------------------------------------------------------- /update-version.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 用法(在项目根目录): 4 | * pnpm run ver -- 0.7.0 # 将 version 设置为 0.7.0 5 | * pnpm run ver -- patch # 将 patch 自增(如 0.6.24 -> 0.6.25) 6 | * 或者使用环境变量: NEW_VERSION=0.7.0 pnpm run ver 7 | * 8 | * 优先级(目标版本来源): 9 | * 1. 命令行参数(pnpm run ver -- ) 10 | * 2. 环境变量 NEW_VERSION 11 | * 3. 环境变量 npm_package_version(不常用,通常为 package.json 中原始值) 12 | */ 13 | 14 | const fs = require('fs'); 15 | const path = require('path'); 16 | 17 | function readJson(filePath) { 18 | const txt = fs.readFileSync(filePath, 'utf8'); 19 | return JSON.parse(txt); 20 | } 21 | function writeJsonWithBackup(filePath, obj) { 22 | const txt = JSON.stringify(obj, null, 2) + '\n'; 23 | const bak = filePath + '.bak'; 24 | //if (fs.existsSync(filePath)) fs.copyFileSync(filePath, bak); 25 | fs.writeFileSync(filePath, txt, 'utf8'); 26 | } 27 | function isValidSemver(v) { 28 | return /^\d+\.\d+\.\d+$/.test(v); 29 | } 30 | function bumpVersion(current, part) { 31 | if (!isValidSemver(current)) throw new Error('当前版本不是 x.y.z 格式: ' + current); 32 | const [maj, min, pat] = current.split('.').map(n => parseInt(n, 10)); 33 | if (part === 'major') return `${maj + 1}.0.0`; 34 | if (part === 'minor') return `${maj}.${min + 1}.0`; 35 | if (part === 'patch') return `${maj}.${min}.${pat + 1}`; 36 | throw new Error('未知的增量类型: ' + part); 37 | } 38 | function updateFileVersion(filePath, targetVersion, bumpOption) { 39 | if (!fs.existsSync(filePath)) { 40 | console.warn('文件不存在,跳过:', filePath); 41 | return null; 42 | } 43 | const data = readJson(filePath); 44 | if (!data.version) { 45 | console.warn('文件中没有 version 字段,跳过:', filePath); 46 | return null; 47 | } 48 | const from = data.version; 49 | let to = targetVersion; 50 | if (!to && bumpOption) to = bumpVersion(from, bumpOption); 51 | if (!to) throw new Error('没有提供目标版本或增量选项'); 52 | if (!isValidSemver(to)) throw new Error('目标版本格式不合法,应为 x.y.z: ' + to); 53 | data.version = to; 54 | writeJsonWithBackup(filePath, data); 55 | return { filePath, from, to }; 56 | } 57 | 58 | // 主逻辑 59 | (function main() { 60 | const rawArgs = process.argv.slice(2); // 通过 npm run bump -- 传入 61 | const arg = rawArgs[0]; 62 | const envVersion = process.env.NEW_VERSION || process.env.npm_package_version || null; 63 | const bumpOptions = new Set(['major', 'minor', 'patch']); 64 | 65 | let newVersion = null; 66 | let bumpOption = null; 67 | 68 | if (arg) { 69 | if (bumpOptions.has(arg)) bumpOption = arg; 70 | else if (isValidSemver(arg)) newVersion = arg; 71 | else { 72 | console.error('参数无效,应为 x.y.z 或 major/minor/patch'); 73 | process.exit(1); 74 | } 75 | } else if (envVersion) { 76 | if (bumpOptions.has(envVersion)) bumpOption = envVersion; 77 | else if (isValidSemver(envVersion)) newVersion = envVersion; 78 | else { 79 | console.error('环境变量 NEW_VERSION 格式无效,应为 x.y.z 或 major/minor/patch'); 80 | process.exit(1); 81 | } 82 | } else { 83 | console.error('未提供版本参数:使用 npm run bump -- 或 NEW_VERSION 环境变量'); 84 | process.exit(1); 85 | } 86 | 87 | const cwd = process.cwd(); 88 | const targets = [ 89 | path.join(cwd, 'package.json'), 90 | path.join(cwd, 'manifest.json'), 91 | ]; 92 | 93 | try { 94 | const results = []; 95 | for (const t of targets) { 96 | const res = updateFileVersion(t, newVersion, bumpOption); 97 | if (res) results.push(res); 98 | } 99 | if (results.length === 0) { 100 | console.warn('没有更新任何文件。'); 101 | process.exit(0); 102 | } 103 | for (const r of results) { 104 | console.log(`${path.basename(r.filePath)}: ${r.from} -> ${r.to}`); 105 | } 106 | } catch (err) { 107 | console.error('错误:', err.message); 108 | process.exit(1); 109 | } 110 | })(); 111 | -------------------------------------------------------------------------------- /scripts/update-version.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 用法(在项目根目录): 4 | * pnpm run ver -- 0.7.0 # 将 version 设置为 0.7.0 5 | * pnpm run ver -- patch (或 c) # 将 patch 自增(如 0.6.24 -> 0.6.25) 6 | * pnpm run ver -- minor (或 b) # 将 minor 自增(如 0.6.24 -> 0.6.25) 7 | * pnpm run ver -- major (或 a) # 将 major 自增(如 0.6.24 -> 0.6.25) 8 | * 或者使用环境变量: NEW_VERSION=0.7.0 pnpm run ver 9 | * 10 | * 优先级(目标版本来源): 11 | * 1. 命令行参数(pnpm run ver -- ) 12 | * 2. 环境变量 NEW_VERSION 13 | * 3. 环境变量 npm_package_version(不常用,通常为 package.json 中原始值) 14 | */ 15 | 16 | const fs = require('fs'); 17 | const path = require('path'); 18 | 19 | function readJson(filePath) { 20 | const txt = fs.readFileSync(filePath, 'utf8'); 21 | return JSON.parse(txt); 22 | } 23 | function writeJsonWithBackup(filePath, obj) { 24 | const txt = JSON.stringify(obj, null, 2) + '\n'; 25 | const bak = filePath + '.bak'; 26 | //if (fs.existsSync(filePath)) fs.copyFileSync(filePath, bak); 27 | fs.writeFileSync(filePath, txt, 'utf8'); 28 | } 29 | function isValidSemver(v) { 30 | return /^\d+\.\d+\.\d+$/.test(v); 31 | } 32 | function bumpVersion(current, part) { 33 | if (!isValidSemver(current)) throw new Error('当前版本不是 x.y.z 格式: ' + current); 34 | const [maj, min, pat] = current.split('.').map(n => parseInt(n, 10)); 35 | if (part === 'major') return `${maj + 1}.0.0`; 36 | if (part === 'minor') return `${maj}.${min + 1}.0`; 37 | if (part === 'patch') return `${maj}.${min}.${pat + 1}`; 38 | throw new Error('未知的增量类型: ' + part); 39 | } 40 | function updateFileVersion(filePath, targetVersion, bumpOption) { 41 | if (!fs.existsSync(filePath)) { 42 | console.warn('文件不存在,跳过:', filePath); 43 | return null; 44 | } 45 | const data = readJson(filePath); 46 | if (!data.version) { 47 | console.warn('文件中没有 version 字段,跳过:', filePath); 48 | return null; 49 | } 50 | const from = data.version; 51 | let to = targetVersion; 52 | if (!to && bumpOption) to = bumpVersion(from, bumpOption); 53 | if (!to) throw new Error('没有提供目标版本或增量选项'); 54 | if (!isValidSemver(to)) throw new Error('目标版本格式不合法,应为 x.y.z: ' + to); 55 | data.version = to; 56 | writeJsonWithBackup(filePath, data); 57 | return { filePath, from, to }; 58 | } 59 | 60 | // 主逻辑 61 | (function main() { 62 | const rawArgs = process.argv.slice(2); // 通过 npm run bump -- 传入 63 | 64 | const aliasMap = { 'a': 'major', 'b': 'minor', 'c': 'patch' }; 65 | const resolve = (v) => aliasMap[v] || v; 66 | 67 | const arg = resolve(rawArgs[0]); 68 | const envVersion = resolve(process.env.NEW_VERSION || process.env.npm_package_version || null); 69 | const bumpOptions = new Set(['major', 'minor', 'patch']); 70 | 71 | let newVersion = null; 72 | let bumpOption = null; 73 | 74 | if (arg) { 75 | if (bumpOptions.has(arg)) bumpOption = arg; 76 | else if (isValidSemver(arg)) newVersion = arg; 77 | else { 78 | console.error('参数无效,应为 x.y.z 或 major/minor/patch'); 79 | process.exit(1); 80 | } 81 | } else if (envVersion) { 82 | if (bumpOptions.has(envVersion)) bumpOption = envVersion; 83 | else if (isValidSemver(envVersion)) newVersion = envVersion; 84 | else { 85 | console.error('环境变量 NEW_VERSION 格式无效,应为 x.y.z 或 major/minor/patch'); 86 | process.exit(1); 87 | } 88 | } else { 89 | console.error('未提供版本参数:使用 npm run bump -- 或 NEW_VERSION 环境变量'); 90 | process.exit(1); 91 | } 92 | 93 | const cwd = process.cwd(); 94 | const targets = [ 95 | path.join(cwd, 'package.json'), 96 | path.join(cwd, 'manifest.json'), 97 | ]; 98 | 99 | try { 100 | const results = []; 101 | for (const t of targets) { 102 | const res = updateFileVersion(t, newVersion, bumpOption); 103 | if (res) results.push(res); 104 | } 105 | if (results.length === 0) { 106 | console.warn('没有更新任何文件。'); 107 | process.exit(0); 108 | } 109 | for (const r of results) { 110 | console.log(`${path.basename(r.filePath)}: ${r.from} -> ${r.to}`); 111 | } 112 | } catch (err) { 113 | console.error('错误:', err.message); 114 | process.exit(1); 115 | } 116 | })(); 117 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { Plugin, setIcon } from "obsidian"; 2 | 3 | import { NoteModify, NoteDelete, NoteRename, StartupFullNotesForceOverSync, StartupFullNotesSync } from "./lib/fs"; 4 | import { SettingTab, PluginSettings, DEFAULT_SETTINGS } from "./setting"; 5 | import { WebSocketClient } from "./lib/websocket"; 6 | import { $ } from "./lang/lang"; 7 | 8 | 9 | interface SyncSkipFiles { 10 | [key: string]: string 11 | } 12 | interface EditorChangeTimeout { 13 | [key: string]: unknown 14 | } 15 | 16 | export default class FastSync extends Plugin { 17 | settingTab: SettingTab 18 | wsSettingChange: boolean 19 | settings: PluginSettings 20 | websocket: WebSocketClient 21 | syncSkipFiles: SyncSkipFiles = {} 22 | syncSkipDelFiles: SyncSkipFiles = {} 23 | syncSkipModifyFiles: SyncSkipFiles = {} 24 | clipboardReadTip: string = "" 25 | 26 | editorChangeTimeout: EditorChangeTimeout = {} 27 | 28 | ribbonIcon: HTMLElement 29 | ribbonIconStatus: boolean = false 30 | 31 | isWatchEnabled: boolean = false 32 | ignoredFiles: Set = new Set() 33 | 34 | enableWatch() { 35 | this.isWatchEnabled = true 36 | } 37 | 38 | disableWatch() { 39 | this.isWatchEnabled = false 40 | } 41 | 42 | addIgnoredFile(path: string) { 43 | this.ignoredFiles.add(path) 44 | } 45 | 46 | removeIgnoredFile(path: string) { 47 | this.ignoredFiles.delete(path) 48 | } 49 | 50 | 51 | async onload() { 52 | this.syncSkipFiles = {} 53 | 54 | await this.loadSettings() 55 | this.settingTab = new SettingTab(this.app, this) 56 | // 注册设置选项 57 | this.addSettingTab(this.settingTab) 58 | this.websocket = new WebSocketClient(this) 59 | 60 | // Create Ribbon Icon once 61 | this.ribbonIcon = this.addRibbonIcon("loader-circle", "Fast Sync: " + $("同步全部笔记"), () => { 62 | StartupFullNotesSync(this) 63 | }) 64 | 65 | this.websocket.isSyncAllFilesInProgress = false 66 | if (this.settings.syncEnabled && this.settings.api && this.settings.apiToken) { 67 | this.websocket.register((status) => this.updateRibbonIcon(status)) 68 | this.ignoredFiles = new Set() 69 | //2333 70 | } else { 71 | this.websocket.unRegister() 72 | this.ignoredFiles = new Set() 73 | } 74 | 75 | // 注册文件事件 76 | this.registerEvent(this.app.vault.on("create", (file) => NoteModify(file, this, true))) 77 | this.registerEvent(this.app.vault.on("modify", (file) => NoteModify(file, this, true))) 78 | this.registerEvent(this.app.vault.on("delete", (file) => NoteDelete(file, this, true))) 79 | this.registerEvent(this.app.vault.on("rename", (file, oldfile) => NoteRename(file, oldfile, this, true))) 80 | 81 | // 注册命令 82 | this.addCommand({ 83 | id: "init-all-files", 84 | name: $("同步全部笔记(覆盖远端)"), 85 | callback: () => StartupFullNotesForceOverSync(this), 86 | }) 87 | 88 | this.addCommand({ 89 | id: "sync-all-files", 90 | name: $("同步全部笔记"), 91 | callback: () => StartupFullNotesSync(this), 92 | }) 93 | } 94 | 95 | onunload() { 96 | // 取消注册文件事件 97 | this.websocket.isSyncAllFilesInProgress = false 98 | this.websocket.unRegister() 99 | } 100 | 101 | updateRibbonIcon(status: boolean) { 102 | if (status) { 103 | setIcon(this.ribbonIcon, "rotate-cw") 104 | this.ribbonIcon.setAttribute("aria-label", "Fast Sync: " + $("同步全部笔记") + " (Connected)") 105 | } else { 106 | setIcon(this.ribbonIcon, "loader-circle") 107 | this.ribbonIcon.setAttribute("aria-label", "Fast Sync: " + $("同步全部笔记") + " (Disconnected)") 108 | } 109 | } 110 | 111 | async loadSettings() { 112 | this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()) 113 | } 114 | 115 | async saveSettings() { 116 | this.websocket.isSyncAllFilesInProgress = false 117 | if (this.settings.api && this.settings.apiToken) { 118 | this.settings.wsApi = this.settings.api 119 | .replace(/^http/, "ws") 120 | .replace(/\/+$/, '') // 去除尾部斜杠 121 | } 122 | this.websocket.unRegister() 123 | if (this.settings.syncEnabled) { 124 | if (this.wsSettingChange) { 125 | this.websocket.unRegister() 126 | this.websocket.register((status) => this.updateRibbonIcon(status)) 127 | this.wsSettingChange = false 128 | } 129 | 130 | } else { 131 | this.websocket.unRegister() 132 | } 133 | await this.saveData(this.settings) 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [中文文档](https://github.com/haierkeys/obsidian-fast-note-sync/blob/master/docs/README.zh-CN.md) / [English Document](https://github.com/haierkeys/obsidian-fast-note-sync/blob/master/README.md) 2 | 3 | 4 |

Fast Note Sync For Obsidian

5 | 6 |

7 | release 8 | alpha-release 9 | license 10 | TypeScript 11 |

12 | 13 | 14 | 15 |

16 | Fast, stable, efficient, and deployable Obsidian note sync & backup plugin 17 |
18 | Supports private deployment, dedicated to providing Obsidian users with an uninterrupted, silky-smooth, multi-device real-time note sync & backup plugin. Supports Mac, Windows, Android, iOS, and other platforms, with multi-language support. 19 |

20 | 21 |

22 | Requires use with a separate server: Fast Note Sync Service 23 |

24 | 25 |
26 | fast-note-sync-service-preview 27 |
28 | 29 | 30 | ## ✨ Plugin Features 31 | 32 | - **Minimal Configuration**: No complicated settings needed. Just paste the remote service configuration to get started out of the box. 33 | - **Real-time Note Sync**: Automatically monitors and synchronizes all note creation, update, and deletion operations within the Vault. 34 | - **Comprehensive Attachment Support**: Real-time sync of images, videos, audio, and other non-setting files. 35 | > ⚠️ **Note**: Requires v1.0+ and server v0.9+. Please control attachment file sizes; large files may cause sync delays. 36 | - **Multi-device Sync**: Supports Mac, Windows, Android, iOS, and other platforms. 37 | 38 | ## 🗺️ Roadmap 39 | 40 | We are continuously improving. Here are the future development plans: 41 | 42 | - [ ] **Server Version Check**: Display server version information for easy understanding of the server's version status. 43 | - [ ] **Configuration Sync**: Provide configuration sync functionality, supporting configuration sync across multiple devices, eliminating the hassle of manually copying configuration files to multiple devices. 44 | - [ ] **Note History**: Provide note history snapshot functionality. You can view note version history and revert to previous versions via the plugin or server WebGui. 45 | - [ ] **Cloud Storage Backup Status**: View cloud storage backup status at any time, keeping you informed of the latest backup status. 46 | - [ ] **Note Sharing Feature**: Generate share links for your cloud notes, making it easy to share your work with others. 47 | - [ ] **AI Notes**: Explore innovative AI + note features. We welcome your valuable suggestions. 48 | 49 | > **If you have improvement suggestions or new ideas, feel free to share them by submitting an issue—we will carefully evaluate and adopt suitable suggestions.** 50 | 51 | ## 💰 Pricing 52 | 53 | - If you find this plugin useful and want to support its continued development, you can support me here: 54 | [BuyMeACoffee](https://ko-fi.com/haierkeys) 55 | 56 | 57 | ## 🚀 Quick Start 58 | 59 | 1. Install the plugin (choose one) 60 | - **Official Store**: Open the Obsidian Community Plugin Market, search for **Fast Note Sync** and install 61 | > ⚠️ The plugin is not yet listed in the official store and cannot be searched. Please install manually. 62 | - **Manual Installation**: Visit https://github.com/haierkeys/obsidian-fast-note-sync/releases to download the installation package, unzip it to the Obsidian plugin directory **.obsidian/plugin** 63 | 2. Open the plugin configuration, click the **Paste Remote Configuration** button, and paste the remote service configuration into the input box. 64 | 65 | 66 | ## 📦 Server Deployment 67 | 68 | For backend service setup, please refer to: [Fast Note Sync Service](https://github.com/haierkeys/fast-note-sync-service). -------------------------------------------------------------------------------- /.github/workflows/pre-release.yml: -------------------------------------------------------------------------------- 1 | name: Pre-release Build 2 | 3 | # 触发条件配置 4 | on: 5 | push: 6 | branches-ignore: 7 | - "master" # 明确忽略 master,由 release.yml 处理 8 | 9 | # 权限配置 10 | permissions: 11 | contents: write # 需要写入权限以创建 Release 12 | 13 | env: 14 | NAME: ${{ github.event.repository.name }} 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v6 22 | with: 23 | ref: ${{ github.ref }} # 明确 checkout 触发 workflow 的分支 24 | # 获取所有历史记录,为了确保能获取到 git tags 25 | fetch-depth: 0 # Required to fetch tags 26 | 27 | - uses: pnpm/action-setup@v4 28 | 29 | - name: Setup Node.js 30 | uses: actions/setup-node@v6 31 | with: 32 | node-version-file: ".node-version" 33 | 34 | # 核心逻辑:检查版本号变更 (Branch vs Master) 35 | - name: Check Version 36 | id: check_version 37 | run: | 38 | echo "=== Debug: Git Status ===" 39 | echo "Current Ref: ${{ github.ref }}" 40 | echo "Current SHA: ${{ github.sha }}" 41 | git branch -v 42 | git log -1 --oneline 43 | echo "=========================" 44 | BRANCH_NAME=${{ github.ref_name }} 45 | echo "Triggered by branch: $BRANCH_NAME. Checking against master..." 46 | 47 | # 确保 fetch 了 origin/master 48 | git fetch origin master 49 | 50 | # 读取当前分支的 version 51 | CURRENT_VERSION=$(jq -r .version manifest.json) 52 | 53 | # 读取 master 分支的 version 54 | # 使用 git show origin/master:manifest.json 提取内容并用 jq 解析 55 | MASTER_VERSION=$(git show origin/master:manifest.json | jq -r .version) 56 | 57 | echo "Branch Logic -> Master Version: $MASTER_VERSION, Current Version: $CURRENT_VERSION" 58 | 59 | if [ "$MASTER_VERSION" = "$CURRENT_VERSION" ]; then 60 | echo "Version equals to master. Skipping." 61 | echo "should_release=false" >> $GITHUB_OUTPUT 62 | else 63 | # 比较是否大于 master 64 | HIGHER_VERSION=$(echo -e "$MASTER_VERSION\n$CURRENT_VERSION" | sort -V | tail -n 1) 65 | if [ "$HIGHER_VERSION" = "$CURRENT_VERSION" ]; then 66 | echo "New version detected on branch!" 67 | echo "should_release=true" >> $GITHUB_OUTPUT 68 | echo "release_tag=$CURRENT_VERSION" >> $GITHUB_OUTPUT 69 | else 70 | echo "Current version $CURRENT_VERSION is not greater than master ($MASTER_VERSION). Skipping." 71 | echo "should_release=false" >> $GITHUB_OUTPUT 72 | fi 73 | fi 74 | - name: Prepare Commit Message 75 | id: prepare 76 | if: steps.check_version.outputs.should_release == 'true' 77 | run: | 78 | # 获取提交文本信息 79 | msg=$(git log -1 --pretty=%B) 80 | 81 | # 设置 Python 进行翻译 82 | pip install deep-translator > /dev/null 2>&1 || true 83 | 84 | # 运行翻译脚本 85 | export COMMIT_MSG="$msg" 86 | 87 | echo "commit_msg<> $GITHUB_OUTPUT 88 | if [ -f "scripts/translate_commit.py" ]; then 89 | python3 scripts/translate_commit.py >> $GITHUB_OUTPUT 90 | else 91 | echo "未找到翻译脚本,使用原始消息" 92 | echo "$msg" >> $GITHUB_OUTPUT 93 | fi 94 | echo "EOF" >> $GITHUB_OUTPUT 95 | - name: Build 96 | id: build 97 | # 只有在 check_version 步骤输出 should_release 为 true 时才执行 98 | if: steps.check_version.outputs.should_release == 'true' 99 | run: | 100 | 101 | pnpm install 102 | pnpm run build 103 | mkdir ${{ env.NAME }} 104 | cp main.js manifest.json styles.css ${{ env.NAME }} 105 | zip -r ${{ env.NAME }}-v${{ steps.check_version.outputs.release_tag }}.zip ${{ env.NAME }} 106 | ls 107 | # 设置输出变量 tag_name 108 | echo "tag_name=${{ steps.check_version.outputs.release_tag }}" >> $GITHUB_OUTPUT 109 | 110 | - name: Create Release 111 | if: steps.check_version.outputs.should_release == 'true' 112 | uses: softprops/action-gh-release@v2 113 | with: 114 | tag_name: ${{ steps.check_version.outputs.release_tag }}-alpha 115 | name: ${{ steps.check_version.outputs.release_tag }}-alpha 116 | draft: false 117 | prerelease: true 118 | generate_release_notes: true 119 | body: ${{ steps.prepare.outputs.commit_msg }} 120 | target_commitish: ${{ github.sha }} # 明确指定在当前分支的 commit 上创建 tag 121 | overwrite_files: true 122 | files: | 123 | ${{ env.NAME }}-v${{ steps.check_version.outputs.release_tag }}.zip 124 | main.js 125 | manifest.json 126 | styles.css 127 | env: 128 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 129 | 130 | 131 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Build plugin 2 | 3 | # 触发条件配置 4 | on: 5 | push: 6 | # 针对 master 分支的普通提交也触发 7 | branches: 8 | - "master" 9 | # 权限配置:允许脚本写入仓库内容(用于发布 Release) 10 | permissions: 11 | contents: write 12 | 13 | env: 14 | NAME: ${{ github.event.repository.name }} 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v6 22 | with: 23 | ref: ${{ github.ref }} # 明确 checkout 触发 workflow 的分支 24 | # 获取所有历史记录,为了确保能获取到 git tags 25 | fetch-depth: 0 # Required to fetch tags 26 | 27 | - uses: pnpm/action-setup@v4 28 | 29 | - name: Setup Node.js 30 | uses: actions/setup-node@v6 31 | with: 32 | node-version-file: ".node-version" 33 | 34 | # 核心逻辑:检查版本号变更 35 | - name: Check Version 36 | id: check_version 37 | run: | 38 | # 如果是 master 分支触发 39 | if [[ "${{ github.ref }}" == "refs/heads/master" ]]; then 40 | echo "Triggered by master branch. Checking for version bump..." 41 | 42 | # 获取上一个 commit 的版本号 43 | LAST_VERSION=$(git show HEAD~1:manifest.json | jq -r .version) 44 | 45 | # 从 manifest.json 中读取当前版本号 46 | CURRENT_VERSION=$(jq -r .version manifest.json) 47 | 48 | echo "Last Version: $LAST_VERSION" 49 | echo "Current Version: $CURRENT_VERSION" 50 | 51 | # 对比版本号 52 | if [ "$LAST_VERSION" = "$CURRENT_VERSION" ]; then 53 | echo "Version has not changed. Skipping." 54 | # 输出变量 should_release = false,后续步骤会通过 if 判断跳过 55 | echo "should_release=false" >> $GITHUB_OUTPUT 56 | else 57 | # check if current > last 58 | # 使用 sort -V 进行版本号大小比较 59 | # 取较大值 60 | HIGHER_VERSION=$(echo -e "$LAST_VERSION\n$CURRENT_VERSION" | sort -V | tail -n 1) 61 | if [ "$HIGHER_VERSION" = "$CURRENT_VERSION" ]; then 62 | echo "New version detected!" 63 | # 确认需要发布 64 | echo "should_release=true" >> $GITHUB_OUTPUT 65 | # 设置发布的 tag 名称为当前版本号 66 | echo "release_tag=$CURRENT_VERSION" >> $GITHUB_OUTPUT 67 | 68 | # Create tag locally so we can push it later (or just use Release API to tag) 69 | # Since we use create-release action using 'tag_name', it often expects the tag to exist or it creates it. 70 | # Let's rely on create-release to create the tag if we pass a tag name that doesn't exist? 71 | # Actually actions/create-release usually creates a release object which creates the git tag. 72 | else 73 | echo "Current version $CURRENT_VERSION is lower than $LAST_VERSION. Skipping." 74 | echo "should_release=false" >> $GITHUB_OUTPUT 75 | fi 76 | fi 77 | else 78 | # 如果是 tag 触发 (手动打 tag 推送) 79 | echo "Triggered by tag ${{ github.ref }}. Proceeding with release." 80 | echo "should_release=true" >> $GITHUB_OUTPUT 81 | echo "release_tag=${{ github.ref_name }}" >> $GITHUB_OUTPUT 82 | fi 83 | - name: Prepare Commit Message 84 | id: prepare 85 | if: steps.check_version.outputs.should_release == 'true' 86 | run: | 87 | # 获取提交文本信息 88 | msg=$(git log -1 --pretty=%B) 89 | 90 | # 设置 Python 进行翻译 91 | pip install deep-translator > /dev/null 2>&1 || true 92 | 93 | # 运行翻译脚本 94 | export COMMIT_MSG="$msg" 95 | 96 | echo "commit_msg<> $GITHUB_OUTPUT 97 | if [ -f "scripts/translate_commit.py" ]; then 98 | python3 scripts/translate_commit.py >> $GITHUB_OUTPUT 99 | else 100 | echo "未找到翻译脚本,使用原始消息" 101 | echo "$msg" >> $GITHUB_OUTPUT 102 | fi 103 | echo "EOF" >> $GITHUB_OUTPUT 104 | - name: Build 105 | id: build 106 | # 只有在 check_version 步骤输出 should_release 为 true 时才执行 107 | if: steps.check_version.outputs.should_release == 'true' 108 | run: | 109 | 110 | pnpm install 111 | pnpm run build 112 | mkdir ${{ env.NAME }} 113 | cp main.js manifest.json styles.css ${{ env.NAME }} 114 | zip -r ${{ env.NAME }}-v${{ steps.check_version.outputs.release_tag }}.zip ${{ env.NAME }} 115 | ls 116 | # 设置输出变量 tag_name 117 | echo "tag_name=${{ steps.check_version.outputs.release_tag }}" >> $GITHUB_OUTPUT 118 | 119 | - name: Create Release 120 | if: steps.check_version.outputs.should_release == 'true' 121 | uses: softprops/action-gh-release@v2 122 | with: 123 | tag_name: ${{ steps.check_version.outputs.release_tag }} 124 | name: ${{ steps.check_version.outputs.release_tag }} 125 | draft: false 126 | prerelease: false 127 | generate_release_notes: true 128 | body: ${{ steps.prepare.outputs.commit_msg }} 129 | target_commitish: ${{ github.sha }} # 明确指定在当前分支的 commit 上创建 tag 130 | overwrite_files: true 131 | files: | 132 | ${{ env.NAME }}-v${{ steps.check_version.outputs.release_tag }}.zip 133 | main.js 134 | manifest.json 135 | styles.css 136 | env: 137 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 138 | 139 | 140 | -------------------------------------------------------------------------------- /src/setting.tsx: -------------------------------------------------------------------------------- 1 | import { App, PluginSettingTab, Notice, Setting, Platform } from "obsidian"; 2 | import { createRoot, Root } from "react-dom/client"; 3 | 4 | import { SettingsView } from "./views/settings-view"; 5 | import { KofiImage } from "./lib/icons"; 6 | import { $ } from "./lang/lang"; 7 | import FastSync from "./main"; 8 | 9 | 10 | export interface PluginSettings { 11 | //是否自动上传 12 | syncEnabled: boolean 13 | //API地址 14 | api: string 15 | wsApi: string 16 | //API Token 17 | apiToken: string 18 | vault: string 19 | lastSyncTime: number 20 | // [propName: string]: any; 21 | clipboardReadTip: string 22 | } 23 | 24 | /** 25 | * 26 | 27 | ![这是图片](https://markdown.com.cn/assets/img/philly-magic-garden.9c0b4415.jpg) 28 | 29 | */ 30 | 31 | // 默认插件设置 32 | export const DEFAULT_SETTINGS: PluginSettings = { 33 | // 是否自动上传 34 | syncEnabled: true, 35 | // API 网关地址 36 | api: "", 37 | wsApi: "", 38 | // API 令牌 39 | apiToken: "", 40 | lastSyncTime: 0, 41 | vault: "defaultVault", 42 | // 剪贴板读取提示 43 | clipboardReadTip: "", 44 | } 45 | 46 | export class SettingTab extends PluginSettingTab { 47 | plugin: FastSync 48 | root: Root | null = null 49 | 50 | constructor(app: App, plugin: FastSync) { 51 | super(app, plugin) 52 | this.plugin = plugin 53 | this.plugin.clipboardReadTip = "" 54 | } 55 | 56 | hide(): void { 57 | if (this.root) { 58 | this.root.unmount() 59 | this.root = null 60 | } 61 | } 62 | 63 | display(): void { 64 | const { containerEl: set } = this 65 | 66 | set.empty() 67 | 68 | // new Setting(set).setName("Fast Note Sync").setDesc($("Fast sync")).setHeading() 69 | 70 | new Setting(set) 71 | .setName($("启用同步")) 72 | .setDesc($("关闭后您的笔记将不做任何同步")) 73 | .addToggle((toggle) => 74 | toggle.setValue(this.plugin.settings.syncEnabled).onChange(async (value) => { 75 | if (value != this.plugin.settings.syncEnabled) { 76 | this.plugin.wsSettingChange = true 77 | this.plugin.settings.syncEnabled = value 78 | this.display() 79 | await this.plugin.saveSettings() 80 | } 81 | }) 82 | ) 83 | 84 | new Setting(set) 85 | .setName("| " + $("远端")) 86 | .setHeading() 87 | .setClass("fast-note-sync-settings-tag") 88 | 89 | const apiSet = set.createDiv() 90 | apiSet.addClass("fast-note-sync-settings") 91 | 92 | this.root = createRoot(apiSet) 93 | this.root.render() 94 | 95 | new Setting(set) 96 | .setName($("远端服务地址")) 97 | .setDesc($("选择一个 Fast note sync service 服务地址")) 98 | .addText((text) => 99 | text 100 | .setPlaceholder($("输入您的 Fast note sync service 服务地址")) 101 | .setValue(this.plugin.settings.api) 102 | .onChange(async (value) => { 103 | if (value != this.plugin.settings.api) { 104 | this.plugin.wsSettingChange = true 105 | this.plugin.settings.api = value 106 | await this.plugin.saveSettings() 107 | } 108 | }) 109 | ) 110 | 111 | new Setting(set) 112 | .setName($("远端服务令牌")) 113 | .setDesc($("用于远端服务的访问授权令牌")) 114 | .addText((text) => 115 | text 116 | .setPlaceholder($("输入您的 API 访问令牌")) 117 | .setValue(this.plugin.settings.apiToken) 118 | .onChange(async (value) => { 119 | if (value != this.plugin.settings.apiToken) { 120 | this.plugin.wsSettingChange = true 121 | this.plugin.settings.apiToken = value 122 | await this.plugin.saveSettings() 123 | } 124 | }) 125 | ) 126 | 127 | new Setting(set) 128 | .setName($("远端仓库名")) 129 | .setDesc($("远端仓库名")) 130 | .addText((text) => 131 | text 132 | .setPlaceholder($("远端仓库名")) 133 | .setValue(this.plugin.settings.vault) 134 | .onChange(async (value) => { 135 | this.plugin.settings.vault = value 136 | await this.plugin.saveSettings() 137 | }) 138 | ) 139 | 140 | new Setting(set) 141 | .setName("| " + $("支持")) 142 | .setHeading() 143 | .setClass("fast-note-sync-settings-tag") 144 | new Setting(set) 145 | .setName($("捐赠")) 146 | .setDesc($("如果您喜欢这个插件,请考虑捐赠以支持继续开发。")) 147 | .setClass("fast-note-sync-settings-support") 148 | .settingEl.createEl("a", { href: "https://ko-fi.com/haierkeys" }) 149 | .createEl("img", { 150 | attr: { src: KofiImage, height: "36", border: "0", alt: "Buy me a coffee at ko-fi.com", class: "ko-fi-logo" }, 151 | }) 152 | 153 | const debugDiv = set.createDiv() 154 | debugDiv.addClass("fast-note-sync-settings-debug") 155 | 156 | const debugButton = debugDiv.createEl("button") 157 | debugButton.setText($("复制 Debug 信息")) 158 | debugButton.onclick = async () => { 159 | await window.navigator.clipboard.writeText( 160 | JSON.stringify( 161 | { 162 | settings: { 163 | ...this.plugin.settings, 164 | apiToken: this.plugin.settings.apiToken ? "***HIDDEN***" : "", 165 | }, 166 | pluginVersion: this.plugin.manifest.version, 167 | }, 168 | null, 169 | 4 170 | ) 171 | ) 172 | new Notice($("将调试信息复制到剪贴板, 可能包含敏感信!")) 173 | } 174 | 175 | if (Platform.isDesktopApp) { 176 | const info = debugDiv.createDiv() 177 | info.setText($("通过快捷键打开控制台,你可以看到这个插件和其他插件的日志")) 178 | 179 | const keys = debugDiv.createDiv() 180 | keys.addClass("custom-shortcuts") 181 | if (Platform.isMacOS === true) { 182 | keys.createEl("kbd", { text: $("console_mac") }) 183 | } else { 184 | keys.createEl("kbd", { text: $("console_windows") }) 185 | } 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/lang/lang.ts: -------------------------------------------------------------------------------- 1 | import zhTW from "src/lang/locale/zh-tw"; 2 | import zhCN from "src/lang/locale/zh-cn"; 3 | import ptBR from "src/lang/locale/pt-br"; 4 | import vi from "src/lang/locale/vi"; 5 | import uk from "src/lang/locale/uk"; 6 | import tr from "src/lang/locale/tr"; 7 | import th from "src/lang/locale/th"; 8 | import sq from "src/lang/locale/sq"; 9 | import ru from "src/lang/locale/ru"; 10 | import ro from "src/lang/locale/ro"; 11 | import pt from "src/lang/locale/pt"; 12 | import pl from "src/lang/locale/pl"; 13 | import nl from "src/lang/locale/nl"; 14 | import ne from "src/lang/locale/ne"; 15 | import nb from "src/lang/locale/nb"; 16 | import ms from "src/lang/locale/ms"; 17 | import ko from "src/lang/locale/ko"; 18 | import ja from "src/lang/locale/ja"; 19 | import it from "src/lang/locale/it"; 20 | import id from "src/lang/locale/id"; 21 | import hu from "src/lang/locale/hu"; 22 | import he from "src/lang/locale/he"; 23 | //import fa from "src/lang/locale/fa"; 24 | import fr from "src/lang/locale/fr"; 25 | import es from "src/lang/locale/es"; 26 | import en from "src/lang/locale/en"; 27 | import de from "src/lang/locale/de"; 28 | import da from "src/lang/locale/da"; 29 | import ca from "src/lang/locale/ca"; 30 | import be from "src/lang/locale/be"; 31 | import ar from "src/lang/locale/ar"; 32 | import { moment } from "obsidian"; 33 | 34 | 35 | /** 36 | * Locale object type. 37 | * 假设每个 locale 文件都是键 => 字符串(常见情形),使用更严格的类型。 38 | */ 39 | export type LangMap = Record; 40 | 41 | export const localeMap: { [k: string]: Partial } = { 42 | ar, 43 | be, 44 | ca, 45 | da, 46 | de, 47 | en, 48 | es, 49 | // fa, 50 | fr, 51 | he, 52 | hu, 53 | id, 54 | it, 55 | ja, 56 | ko, 57 | ms, 58 | ne, 59 | nl, 60 | nb, 61 | pl, 62 | pt, 63 | "pt-br": ptBR, 64 | ro, 65 | ru, 66 | sq, 67 | th, 68 | tr, 69 | uk, 70 | vi, 71 | "zh-cn": zhCN, 72 | "zh-tw": zhTW, 73 | }; 74 | 75 | const locale = localeMap[moment.locale()] as Partial | undefined; 76 | 77 | 78 | function getValueFromPath(root: Record, path: string): unknown { 79 | const normalized = path 80 | .replace(/\[(?:'([^']*)'|"([^"]*)"|([^'\]"[\]]+))\]/g, (_m, g1, g2, g3) => { 81 | const key = g1 ?? g2 ?? g3; 82 | return "." + key; 83 | }) 84 | .replace(/^\./, ""); 85 | 86 | if (normalized === "") return undefined; 87 | 88 | const parts = normalized.split("."); 89 | let cur: unknown = root; 90 | for (const part of parts) { 91 | if (cur == null) return undefined; 92 | if (part === "") return undefined; 93 | if (typeof cur === "object") { 94 | cur = (cur as Record)[part]; 95 | } else { 96 | return undefined; 97 | } 98 | } 99 | return cur; 100 | } 101 | 102 | 103 | function interpolate(str: string, params: Record): string { 104 | if (!str || typeof str !== "string") return String(str ?? ""); 105 | return str.replace(/\$\{([^}]+)\}/g, (_match, expression) => { 106 | const path = expression.trim(); 107 | if (!/^[A-Za-z0-9_.[\]'"\s-]+$/.test(path)) { 108 | return ""; 109 | } 110 | const val = getValueFromPath(params, path); 111 | if (val === undefined || val === null) return ""; 112 | if (typeof val === "string") return val; 113 | if (typeof val === "number" || typeof val === "boolean" || typeof val === "bigint") { 114 | return String(val); 115 | } 116 | try { 117 | return JSON.stringify(val); 118 | } catch { 119 | return ""; 120 | } 121 | }); 122 | } 123 | 124 | 125 | export function $( 126 | str: Extract, 127 | params?: Record 128 | ): string { 129 | // str 的类型现在必为 string,安全用于索引 130 | const key = str; 131 | const fallback = en[key]; 132 | const result = (locale && (locale[key] as string)) ?? fallback ?? key; 133 | 134 | if (params) { 135 | return interpolate(result, params); 136 | } 137 | 138 | return result; 139 | } 140 | 141 | 142 | // // CARD_TYPES_SUMMARY: "总卡片数: ${totalCardsCount}", 143 | // t("CARD_TYPES_SUMMARY", { totalCardsCount }), 144 | /** 145 | 146 | 通过AI 进行多语言翻译 147 | 148 | 我提供一段typescript的代码,键的部分保持简体中文,你帮我把值的部分翻译成英文。 149 | 150 | 151 | 键的部分保持简体中文,再帮我把值的部分翻译成繁体中文 152 | 153 | 154 | 键的部分保持简体中文,再帮我把值的部分翻译成阿拉伯语 ar 155 | 键的部分保持简体中文,再帮我把值的部分翻译成白俄罗斯语 be 156 | 键的部分保持简体中文,再帮我把值的部分翻译成加泰罗尼亚语 ca 157 | 键的部分保持简体中文,再帮我把值的部分翻译成丹麦语 da 158 | 键的部分保持简体中文,再帮我把值的部分翻译成德语 de 159 | 键的部分保持简体中文,再帮我把值的部分翻译成西班牙语 es 160 | 键的部分保持简体中文,再帮我把值的部分翻译成法语 fr 161 | 键的部分保持简体中文,再帮我把值的部分翻译成希伯来语 he 162 | 键的部分保持简体中文,再帮我把值的部分翻译成匈牙利语 hu 163 | 键的部分保持简体中文,再帮我把值的部分翻译成印度尼西亚语 id 164 | 键的部分保持简体中文,再帮我把值的部分翻译成意大利语 it 165 | 键的部分保持简体中文,再帮我把值的部分翻译成日语 ja 166 | 键的部分保持简体中文,再帮我把值的部分翻译成韩语 ko 167 | 键的部分保持简体中文,再帮我把值的部分翻译成马来语 ms 168 | 键的部分保持简体中文,再帮我把值的部分翻译成挪威语 nb 169 | 键的部分保持简体中文,再帮我把值的部分翻译成尼泊尔语 ne 170 | 键的部分保持简体中文,再帮我把值的部分翻译成荷兰语 nl 171 | 键的部分保持简体中文,再帮我把值的部分翻译成波兰语 pl 172 | 键的部分保持简体中文,再帮我把值的部分翻译成葡萄牙语 pt-br 173 | 键的部分保持简体中文,再帮我把值的部分翻译成葡萄牙语 pt 174 | 键的部分保持简体中文,再帮我把值的部分翻译成罗马尼亚语 ro 175 | 键的部分保持简体中文,再帮我把值的部分翻译成俄语 ru 176 | 键的部分保持简体中文,再帮我把值的部分翻译成阿尔巴尼亚语 sq 177 | 键的部分保持简体中文,再帮我把值的部分翻译成泰语 th 178 | 键的部分保持简体中文,再帮我把值的部分翻译成土耳其语 tr 179 | 键的部分保持简体中文,再帮我把值的部分翻译成乌克兰语 uk 180 | 键的部分保持简体中文,再帮我把值的部分翻译成越南语 vi 181 | 182 | 语言对照表 183 | am አማርኛ 184 | ar اَلْعَرَبِيَّةُ 185 | be беларуская мова 186 | ca català 187 | cs čeština 188 | da Dansk 189 | de Deutsch 190 | en English 191 | en-GB English (GB) 192 | es Español 193 | fa فارسی 194 | fr Français 195 | he עברית 196 | hu Magyar 197 | id Bahasa Indonesia 198 | it Italiano 199 | ja 日本語 200 | kh ខ្មែរ 201 | ko 한국어 202 | ms Bahasa Melayu 203 | ne नेपाली 204 | nl Nederlands 205 | no Norsk 206 | pl Polski 207 | pt Português 208 | pt-BR Português do Brasil 209 | ro Română 210 | ru Pусский 211 | sq Shqip 212 | th ไทย 213 | tr Türkçe 214 | uk Українська 215 | vi Tiếng Việt 216 | zh 简体中文 217 | zh-TW 繁體中文 218 | 219 | */ -------------------------------------------------------------------------------- /src/lib/websocket.ts: -------------------------------------------------------------------------------- 1 | import { Notice, moment } from "obsidian"; 2 | 3 | import { syncReceiveMethodHandlers, StartupFullNotesSync } from "./fs"; 4 | import { dump, isWsUrl } from "./helps"; 5 | import FastSync from "../main"; 6 | 7 | 8 | // WebSocket 连接常量 9 | const RECONNECT_BASE_DELAY = 3000 // 重连基础延迟 (毫秒) 10 | const CONNECTION_CHECK_INTERVAL = 3000 // 连接检查间隔 (毫秒) 11 | 12 | 13 | export class WebSocketClient { 14 | private ws: WebSocket 15 | private wsApi: string 16 | private plugin: FastSync 17 | public isOpen: boolean = false 18 | public isAuth: boolean = false 19 | public checkConnection: number 20 | public checkReConnectTimeout: number 21 | public timeConnect = 0 22 | public count = 0 23 | //同步全部文件时设置 24 | public isSyncAllFilesInProgress: boolean = false 25 | public isRegister: boolean = false 26 | private messageQueue: { action: string; data: object | string }[] = [] 27 | private onStatusChange?: (status: boolean) => void 28 | 29 | constructor(plugin: FastSync) { 30 | this.plugin = plugin 31 | this.wsApi = plugin.settings.wsApi 32 | .replace(/^http/, "ws") 33 | .replace(/\/+$/, '') // 去除尾部斜杠 34 | } 35 | 36 | public isConnected(): boolean { 37 | return this.isOpen 38 | } 39 | 40 | public register(onStatusChange?: (status: boolean) => void) { 41 | if (onStatusChange) this.onStatusChange = onStatusChange 42 | 43 | if ((!this.ws || this.ws.readyState !== WebSocket.OPEN) && isWsUrl(this.wsApi)) { 44 | this.isRegister = true 45 | this.ws = new WebSocket(this.wsApi + "/api/user/sync?lang=" + moment.locale() + "&count=" + this.count) 46 | this.count++ 47 | this.ws.onerror = (error) => { 48 | dump("WebSocket error:", error) 49 | if (this.onStatusChange) this.onStatusChange(false) 50 | } 51 | this.ws.onopen = (e: Event): void => { 52 | this.timeConnect = 0 53 | this.isOpen = true 54 | dump("Service connected") 55 | if (this.onStatusChange) this.onStatusChange(true) 56 | this.Send("Authorization", this.plugin.settings.apiToken) 57 | dump("Service authorization") 58 | this.OnlineStatusCheck() 59 | } 60 | this.ws.onclose = (e) => { 61 | this.isAuth = false 62 | this.isOpen = false 63 | if (this.onStatusChange) this.onStatusChange(false) 64 | window.clearInterval(this.checkConnection) 65 | if (e.reason == "AuthorizationFaild") { 66 | new Notice("Remote Service Connection Closed: " + e.reason) 67 | } else if (e.reason == "ClientClose") { 68 | new Notice("Remote Service Connection Closed: " + e.reason) 69 | } 70 | if (this.isRegister && (e.reason != "AuthorizationFaild" && e.reason != "ClientClose")) { 71 | this.checkReConnect() 72 | } 73 | dump("Service close") 74 | } 75 | this.ws.onmessage = (event) => { 76 | // 使用字符串的 indexOf 找到第一个分隔符的位置 77 | let msgData: string = event.data 78 | let msgAction: string = "" 79 | const index = event.data.indexOf("|") 80 | if (index !== -1) { 81 | msgData = event.data.slice(index + 1) 82 | msgAction = event.data.slice(0, index) 83 | } 84 | const data = JSON.parse(msgData) 85 | if (msgAction == "Authorization") { 86 | if (data.code == 0 || data.code > 200) { 87 | new Notice("Service Authorization Error: Code=" + data.code + " Msg=" + data.msg + data.details) 88 | return 89 | } else { 90 | this.isAuth = true 91 | dump("Service authorization success") 92 | this.FlushQueue() 93 | this.StartHandle() 94 | } 95 | } 96 | if (data.code == 0 || data.code > 200) { 97 | new Notice("Service Error: Code=" + data.code + " Msg=" + data.msg + data.details) 98 | } else { 99 | const handler = syncReceiveMethodHandlers.get(msgAction) 100 | if (handler) { 101 | handler(data.data, this.plugin) 102 | } 103 | } 104 | } 105 | } 106 | } 107 | public unRegister() { 108 | window.clearInterval(this.checkConnection) 109 | window.clearTimeout(this.checkReConnectTimeout) 110 | this.isOpen = false 111 | this.isAuth = false 112 | this.isRegister = false 113 | if (this.ws) { 114 | this.ws.close(1000, "unRegister") 115 | } 116 | dump("Service unregister") 117 | } 118 | 119 | //ddd 120 | public checkReConnect() { 121 | window.clearTimeout(this.checkReConnectTimeout) 122 | if (this.timeConnect > 15) { // Max attempts hardcoded or use constant 123 | return 124 | } 125 | if (this.ws && this.ws.readyState === WebSocket.CLOSED) { 126 | this.timeConnect++ 127 | // Exponential backoff: 3s, 6s, 12s, 24s... 128 | const delay = RECONNECT_BASE_DELAY * Math.pow(2, this.timeConnect - 1) 129 | dump(`Service waiting reconnect: ${this.timeConnect}, delay: ${delay}ms`) 130 | 131 | this.checkReConnectTimeout = window.setTimeout(() => { 132 | this.register() 133 | }, delay) 134 | } 135 | } 136 | public StartHandle() { 137 | StartupFullNotesSync(this.plugin) 138 | } 139 | 140 | public OnlineStatusCheck() { 141 | // 检查 WebSocket 连接是否打开 142 | this.checkConnection = window.setInterval(() => { 143 | if (this.ws && this.ws.readyState === WebSocket.OPEN) { 144 | this.isOpen = true 145 | } else { 146 | this.isOpen = false 147 | } 148 | }, CONNECTION_CHECK_INTERVAL) 149 | } 150 | 151 | public MsgSend(action: string, data: object | string, isSync: boolean = false) { 152 | if (!this.isAuth || (this.isSyncAllFilesInProgress && !isSync)) { 153 | dump(`Service not ready or sync in progress, queuing message: ${action}`) 154 | this.messageQueue.push({ action, data }) 155 | return 156 | } 157 | this.Send(action, data) 158 | } 159 | 160 | public Send(action: string, data: object | string) { 161 | if (this.ws.readyState !== WebSocket.OPEN) { 162 | dump(`Service not connected, queuing message: ${action}`) 163 | this.messageQueue.push({ action, data }) 164 | return 165 | } 166 | 167 | dump(`Sending message: ${action}`) 168 | if (typeof data === "string") { 169 | this.ws.send(action + "|" + data) 170 | } else { 171 | this.ws.send(action + "|" + JSON.stringify(data)) 172 | } 173 | } 174 | 175 | public FlushQueue() { 176 | if (this.messageQueue.length === 0) return 177 | 178 | dump(`Flushing ${this.messageQueue.length} queued messages`) 179 | while (this.messageQueue.length > 0) { 180 | const msg = this.messageQueue.shift() 181 | dump(`Flushing message: `, msg) 182 | if (msg) { 183 | this.Send(msg.action, msg.data) 184 | } 185 | } 186 | } 187 | } -------------------------------------------------------------------------------- /src/lib/fs.ts: -------------------------------------------------------------------------------- 1 | import { TFile, TAbstractFile, Notice } from "obsidian"; 2 | 3 | import { hashContent, dump } from "./helps"; 4 | import FastSync from "../main"; 5 | 6 | 7 | /** 8 | 消息推送操作方法 Message Push Operation Method 9 | */ 10 | 11 | export const NoteModify = async function (file: TAbstractFile, plugin: FastSync, eventEnter: boolean = false) { 12 | if (!file.path.endsWith(".md")) return 13 | if (!plugin.isWatchEnabled && eventEnter) { 14 | return 15 | } 16 | if (plugin.ignoredFiles.has(file.path) && eventEnter) { 17 | return 18 | } 19 | if (!(file instanceof TFile)) { 20 | return 21 | } 22 | 23 | plugin.addIgnoredFile(file.path) 24 | 25 | const content: string = await plugin.app.vault.cachedRead(file) 26 | const contentHash = hashContent(content) 27 | 28 | const data = { 29 | vault: plugin.settings.vault, 30 | ctime: file.stat.ctime, 31 | mtime: file.stat.mtime, 32 | path: file.path, 33 | pathHash: hashContent(file.path), 34 | content: content, 35 | contentHash: contentHash, 36 | } 37 | plugin.websocket.MsgSend("NoteModify", data) 38 | dump(`Note modify send`, data.path, data.contentHash, data.mtime, data.pathHash) 39 | plugin.removeIgnoredFile(file.path) 40 | 41 | 42 | } 43 | 44 | 45 | export const NoteDelete = function (file: TAbstractFile, plugin: FastSync, eventEnter: boolean = false) { 46 | if (!file.path.endsWith(".md")) return 47 | if (!plugin.isWatchEnabled && eventEnter) { 48 | return 49 | } 50 | if (plugin.ignoredFiles.has(file.path) && eventEnter) { 51 | return 52 | } 53 | if (!(file instanceof TFile)) { 54 | return 55 | } 56 | 57 | plugin.addIgnoredFile(file.path) 58 | 59 | NoteDeleteByPath(file.path, plugin) 60 | dump(`Note delete send`, file.path) 61 | 62 | plugin.removeIgnoredFile(file.path) 63 | } 64 | 65 | export const NoteRename = async function (file: TAbstractFile, oldfile: string, plugin: FastSync, eventEnter: boolean = false) { 66 | if (!file.path.endsWith(".md")) return 67 | if (!plugin.isWatchEnabled && eventEnter) { 68 | return 69 | } 70 | if (plugin.ignoredFiles.has(file.path) && eventEnter) { 71 | return 72 | } 73 | if (!(file instanceof TFile)) { 74 | return 75 | } 76 | 77 | plugin.addIgnoredFile(file.path) 78 | 79 | const content: string = await plugin.app.vault.cachedRead(file) 80 | const contentHash = hashContent(content) 81 | 82 | const data = { 83 | vault: plugin.settings.vault, 84 | ctime: file.stat.ctime, 85 | mtime: file.stat.mtime, 86 | path: file.path, 87 | pathHash: hashContent(file.path), 88 | content: content, 89 | contentHash: contentHash, 90 | } 91 | 92 | plugin.websocket.MsgSend("NoteModify", data) 93 | dump(`Note rename modify send`, data.path, data.contentHash, data.mtime, data.pathHash) 94 | 95 | NoteDeleteByPath(oldfile, plugin) 96 | dump(`Note rename delete send`, oldfile) 97 | 98 | plugin.removeIgnoredFile(file.path) 99 | } 100 | 101 | 102 | export const NoteDeleteByPath = function (path: string, plugin: FastSync) { 103 | if (!path.endsWith(".md")) return 104 | const data = { 105 | vault: plugin.settings.vault, 106 | path: path, 107 | pathHash: hashContent(path), 108 | } 109 | plugin.websocket.MsgSend("NoteDelete", data) 110 | } 111 | 112 | 113 | /** 114 | 调用动作操作方法 Invoke action operation method 115 | */ 116 | 117 | // 异步实现:保留原始逻辑,返回 Promise 118 | export async function overrideRemoteAllFilesImpl(plugin: FastSync): Promise { 119 | if (plugin.websocket.isSyncAllFilesInProgress) { 120 | // Notice 文本使用 sentence case 121 | new Notice("上一次的全部笔记同步尚未完成,请耐心等待或检查服务端状态") 122 | return 123 | } 124 | 125 | const localNotes: NoteSyncCheck[] = [] 126 | const files = plugin.app.vault.getMarkdownFiles() 127 | for (const file of files) { 128 | const content: string = await plugin.app.vault.cachedRead(file) 129 | localNotes.push({ 130 | path: file.path, 131 | pathHash: hashContent(file.path), 132 | contentHash: hashContent(content), 133 | mtime: file.stat.mtime, 134 | }) 135 | } 136 | plugin.settings.lastSyncTime = 0 137 | await plugin.saveData(plugin.settings) 138 | NoteSync(plugin, localNotes) 139 | } 140 | 141 | // 同步包装:供 addCommand 使用,返回 void(命令回调类型安全) 142 | export const StartupFullNotesForceOverSync = (plugin: FastSync): void => { 143 | void overrideRemoteAllFilesImpl(plugin) 144 | } 145 | 146 | // 异步实现:保留原始逻辑,返回 Promise 147 | export async function syncAllFilesImpl(plugin: FastSync): Promise { 148 | if (plugin.websocket.isSyncAllFilesInProgress) { 149 | new Notice("上一次的全部笔记同步尚未完成,请耐心等待或检查服务端状态") 150 | return 151 | } 152 | 153 | // 发送同步请求 154 | NoteSync(plugin) 155 | // 等待接收结束信号 156 | while (plugin.websocket.isSyncAllFilesInProgress) { 157 | // 这些 dump 是调试输出,若会展示给用户请改为 sentence case 的用户提示或移除 158 | dump("Waiting for receive notesync end.") 159 | if (!plugin.websocket.isRegister) { 160 | dump("Plugin websocket is not register, return.") 161 | return 162 | } 163 | dump("Loop, waiting...") 164 | await sleep(2000) // 每隔两秒重试一次 165 | } 166 | const localNotes: NoteSyncCheck[] = [] 167 | const files = plugin.app.vault.getMarkdownFiles() 168 | for (const file of files) { 169 | const content: string = await plugin.app.vault.cachedRead(file) 170 | localNotes.push({ 171 | path: file.path, 172 | pathHash: hashContent(file.path), 173 | contentHash: hashContent(content), 174 | mtime: file.stat.mtime, 175 | }) 176 | } 177 | plugin.settings.lastSyncTime = 0 178 | await plugin.saveData(plugin.settings) 179 | NoteSync(plugin, localNotes) 180 | } 181 | 182 | // 同步包装:供 addCommand 使用,返回 void(命令回调类型安全) 183 | export const StartupFullNotesSync = (plugin: FastSync): void => { 184 | void syncAllFilesImpl(plugin) 185 | } 186 | 187 | interface NoteSyncCheck { 188 | path: string 189 | pathHash: string 190 | contentHash: string 191 | mtime: number 192 | } 193 | 194 | export const NoteSync = function (plugin: FastSync, notes: NoteSyncCheck[] = []) { 195 | while (plugin.websocket.isSyncAllFilesInProgress) { 196 | new Notice("上一次的全部笔记同步尚未完成,请耐心等待或检查服务端状态") 197 | return 198 | } 199 | 200 | plugin.disableWatch() 201 | 202 | const data = { 203 | vault: plugin.settings.vault, 204 | lastTime: Number(plugin.settings.lastSyncTime), 205 | notes: notes 206 | } 207 | plugin.websocket.MsgSend("NoteSync", data) 208 | dump("Notesync", data) 209 | plugin.websocket.isSyncAllFilesInProgress = true 210 | } 211 | 212 | /** 213 | 消息接收操作方法 Message receiving methods 214 | */ 215 | 216 | interface ReceiveData { 217 | vault: string 218 | path: string 219 | pathHash: string 220 | action: string 221 | content: string 222 | contentHash: string 223 | ctime: number 224 | mtime: number 225 | lastTime: number 226 | } 227 | 228 | interface ReceiveCheckData { 229 | path: string 230 | ctime: number 231 | mtime: number 232 | } 233 | 234 | // ReceiveNoteModify 接收文件修改 235 | export const ReceiveNoteSyncModify = async function (data: ReceiveData, plugin: FastSync) { 236 | dump(`Receive note modify:`, data.action, data.path, data.contentHash, data.mtime, data.pathHash) 237 | 238 | const file = plugin.app.vault.getFileByPath(data.path) 239 | plugin.addIgnoredFile(data.path) 240 | if (file) { 241 | await plugin.app.vault.modify(file, data.content, { ctime: data.ctime, mtime: data.mtime }) 242 | } else { 243 | const folder = data.path.split("/").slice(0, -1).join("/") 244 | if (folder != "") { 245 | const dirExists = plugin.app.vault.getFolderByPath(folder) 246 | if (dirExists == null) await plugin.app.vault.createFolder(folder) 247 | } 248 | await plugin.app.vault.create(data.path, data.content, { ctime: data.ctime, mtime: data.mtime }) 249 | } 250 | plugin.removeIgnoredFile(data.path) 251 | } 252 | 253 | // ReceiveNoteSyncNeed 接收处理需要上传需求 254 | export const ReceiveNoteSyncNeedPush = async function (data: ReceiveCheckData, plugin: FastSync) { 255 | dump(`Receive note need push:`, data.path, data.mtime) 256 | const file = plugin.app.vault.getFileByPath(data.path) 257 | if (file) { 258 | await NoteModify(file, plugin, false) 259 | } 260 | } 261 | 262 | // ReceiveNoteSyncNeedMtime 接收需求修改mtime 263 | export const ReceiveNoteSyncMtime = async function (data: ReceiveCheckData, plugin: FastSync) { 264 | dump(`Receive note sync mtime:`, data.path, data.mtime) 265 | 266 | const file = plugin.app.vault.getFileByPath(data.path) 267 | if (file) { 268 | const content: string = await plugin.app.vault.cachedRead(file) 269 | plugin.addIgnoredFile(data.path) 270 | await plugin.app.vault.modify(file, content, { ctime: data.ctime, mtime: data.mtime }) 271 | plugin.removeIgnoredFile(data.path) 272 | } 273 | } 274 | 275 | // 接收文件删除任务 276 | export const ReceiveNoteSyncDelete = async function (data: ReceiveData, plugin: FastSync) { 277 | dump(`Receive note delete:`, data.action, data.path, data.mtime, data.pathHash) 278 | const file = plugin.app.vault.getFileByPath(data.path) 279 | if (file instanceof TFile) { 280 | plugin.addIgnoredFile(data.path) 281 | await plugin.app.vault.delete(file) 282 | plugin.removeIgnoredFile(data.path) 283 | } 284 | } 285 | 286 | //接收同步结束消息 287 | export const ReceiveNoteSyncEnd = async function (data: ReceiveData, plugin: FastSync) { 288 | dump(`Receive note end:`, data.vault, data, data.lastTime) 289 | plugin.settings.lastSyncTime = data.lastTime 290 | await plugin.saveData(plugin.settings) 291 | plugin.websocket.isSyncAllFilesInProgress = false 292 | plugin.websocket.FlushQueue() 293 | plugin.enableWatch() 294 | } 295 | 296 | type ReceiveSyncMethod = (data: unknown, plugin: FastSync) => void 297 | 298 | export const syncReceiveMethodHandlers: Map = new Map([ 299 | ["NoteSyncModify", ReceiveNoteSyncModify], 300 | ["NoteSyncNeedPush", ReceiveNoteSyncNeedPush], 301 | ["NoteSyncMtime", ReceiveNoteSyncMtime], 302 | ["NoteSyncDelete", ReceiveNoteSyncDelete], 303 | ["NoteSyncEnd", ReceiveNoteSyncEnd], 304 | ]) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2024-2025 HaierKeys 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/lib/icons.ts: -------------------------------------------------------------------------------- 1 | 2 | export const KofiImage: string = 3 | "data:image/png;base64,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"; 4 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@codemirror/state': 12 | specifier: 6.5.0 13 | version: 6.5.0 14 | '@codemirror/view': 15 | specifier: 6.38.6 16 | version: 6.38.6 17 | file-type: 18 | specifier: ^19.6.0 19 | version: 19.6.0 20 | react: 21 | specifier: ^19.2.0 22 | version: 19.2.0 23 | react-dom: 24 | specifier: ^19.2.0 25 | version: 19.2.0(react@19.2.0) 26 | devDependencies: 27 | '@types/node': 28 | specifier: ^22.19.1 29 | version: 22.19.1 30 | '@types/react': 31 | specifier: ^19.2.7 32 | version: 19.2.7 33 | '@types/react-dom': 34 | specifier: ^19.2.3 35 | version: 19.2.3(@types/react@19.2.7) 36 | '@typescript-eslint/eslint-plugin': 37 | specifier: ^8.48.0 38 | version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) 39 | '@typescript-eslint/parser': 40 | specifier: ^8.48.0 41 | version: 8.48.0(eslint@9.39.1)(typescript@5.9.3) 42 | builtin-modules: 43 | specifier: ^4.0.0 44 | version: 4.0.0 45 | esbuild: 46 | specifier: ^0.24.2 47 | version: 0.24.2 48 | eslint: 49 | specifier: ^9.39.1 50 | version: 9.39.1 51 | obsidian: 52 | specifier: ^1.10.3 53 | version: 1.10.3(@codemirror/state@6.5.0)(@codemirror/view@6.38.6) 54 | tslib: 55 | specifier: ^2.8.1 56 | version: 2.8.1 57 | typescript: 58 | specifier: ^5.9.3 59 | version: 5.9.3 60 | 61 | packages: 62 | 63 | '@borewit/text-codec@0.1.1': 64 | resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} 65 | 66 | '@codemirror/state@6.5.0': 67 | resolution: {integrity: sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==} 68 | 69 | '@codemirror/view@6.38.6': 70 | resolution: {integrity: sha512-qiS0z1bKs5WOvHIAC0Cybmv4AJSkAXgX5aD6Mqd2epSLlVJsQl8NG23jCVouIgkh4All/mrbdsf2UOLFnJw0tw==} 71 | 72 | '@esbuild/aix-ppc64@0.24.2': 73 | resolution: {integrity: sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==} 74 | engines: {node: '>=18'} 75 | cpu: [ppc64] 76 | os: [aix] 77 | 78 | '@esbuild/android-arm64@0.24.2': 79 | resolution: {integrity: sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==} 80 | engines: {node: '>=18'} 81 | cpu: [arm64] 82 | os: [android] 83 | 84 | '@esbuild/android-arm@0.24.2': 85 | resolution: {integrity: sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==} 86 | engines: {node: '>=18'} 87 | cpu: [arm] 88 | os: [android] 89 | 90 | '@esbuild/android-x64@0.24.2': 91 | resolution: {integrity: sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==} 92 | engines: {node: '>=18'} 93 | cpu: [x64] 94 | os: [android] 95 | 96 | '@esbuild/darwin-arm64@0.24.2': 97 | resolution: {integrity: sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==} 98 | engines: {node: '>=18'} 99 | cpu: [arm64] 100 | os: [darwin] 101 | 102 | '@esbuild/darwin-x64@0.24.2': 103 | resolution: {integrity: sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==} 104 | engines: {node: '>=18'} 105 | cpu: [x64] 106 | os: [darwin] 107 | 108 | '@esbuild/freebsd-arm64@0.24.2': 109 | resolution: {integrity: sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==} 110 | engines: {node: '>=18'} 111 | cpu: [arm64] 112 | os: [freebsd] 113 | 114 | '@esbuild/freebsd-x64@0.24.2': 115 | resolution: {integrity: sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==} 116 | engines: {node: '>=18'} 117 | cpu: [x64] 118 | os: [freebsd] 119 | 120 | '@esbuild/linux-arm64@0.24.2': 121 | resolution: {integrity: sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==} 122 | engines: {node: '>=18'} 123 | cpu: [arm64] 124 | os: [linux] 125 | 126 | '@esbuild/linux-arm@0.24.2': 127 | resolution: {integrity: sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==} 128 | engines: {node: '>=18'} 129 | cpu: [arm] 130 | os: [linux] 131 | 132 | '@esbuild/linux-ia32@0.24.2': 133 | resolution: {integrity: sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==} 134 | engines: {node: '>=18'} 135 | cpu: [ia32] 136 | os: [linux] 137 | 138 | '@esbuild/linux-loong64@0.24.2': 139 | resolution: {integrity: sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==} 140 | engines: {node: '>=18'} 141 | cpu: [loong64] 142 | os: [linux] 143 | 144 | '@esbuild/linux-mips64el@0.24.2': 145 | resolution: {integrity: sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==} 146 | engines: {node: '>=18'} 147 | cpu: [mips64el] 148 | os: [linux] 149 | 150 | '@esbuild/linux-ppc64@0.24.2': 151 | resolution: {integrity: sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==} 152 | engines: {node: '>=18'} 153 | cpu: [ppc64] 154 | os: [linux] 155 | 156 | '@esbuild/linux-riscv64@0.24.2': 157 | resolution: {integrity: sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==} 158 | engines: {node: '>=18'} 159 | cpu: [riscv64] 160 | os: [linux] 161 | 162 | '@esbuild/linux-s390x@0.24.2': 163 | resolution: {integrity: sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==} 164 | engines: {node: '>=18'} 165 | cpu: [s390x] 166 | os: [linux] 167 | 168 | '@esbuild/linux-x64@0.24.2': 169 | resolution: {integrity: sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==} 170 | engines: {node: '>=18'} 171 | cpu: [x64] 172 | os: [linux] 173 | 174 | '@esbuild/netbsd-arm64@0.24.2': 175 | resolution: {integrity: sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==} 176 | engines: {node: '>=18'} 177 | cpu: [arm64] 178 | os: [netbsd] 179 | 180 | '@esbuild/netbsd-x64@0.24.2': 181 | resolution: {integrity: sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==} 182 | engines: {node: '>=18'} 183 | cpu: [x64] 184 | os: [netbsd] 185 | 186 | '@esbuild/openbsd-arm64@0.24.2': 187 | resolution: {integrity: sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==} 188 | engines: {node: '>=18'} 189 | cpu: [arm64] 190 | os: [openbsd] 191 | 192 | '@esbuild/openbsd-x64@0.24.2': 193 | resolution: {integrity: sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==} 194 | engines: {node: '>=18'} 195 | cpu: [x64] 196 | os: [openbsd] 197 | 198 | '@esbuild/sunos-x64@0.24.2': 199 | resolution: {integrity: sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==} 200 | engines: {node: '>=18'} 201 | cpu: [x64] 202 | os: [sunos] 203 | 204 | '@esbuild/win32-arm64@0.24.2': 205 | resolution: {integrity: sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==} 206 | engines: {node: '>=18'} 207 | cpu: [arm64] 208 | os: [win32] 209 | 210 | '@esbuild/win32-ia32@0.24.2': 211 | resolution: {integrity: sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==} 212 | engines: {node: '>=18'} 213 | cpu: [ia32] 214 | os: [win32] 215 | 216 | '@esbuild/win32-x64@0.24.2': 217 | resolution: {integrity: sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==} 218 | engines: {node: '>=18'} 219 | cpu: [x64] 220 | os: [win32] 221 | 222 | '@eslint-community/eslint-utils@4.9.0': 223 | resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} 224 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 225 | peerDependencies: 226 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 227 | 228 | '@eslint-community/regexpp@4.12.2': 229 | resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} 230 | engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 231 | 232 | '@eslint/config-array@0.21.1': 233 | resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} 234 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 235 | 236 | '@eslint/config-helpers@0.4.2': 237 | resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} 238 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 239 | 240 | '@eslint/core@0.17.0': 241 | resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} 242 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 243 | 244 | '@eslint/eslintrc@3.3.1': 245 | resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} 246 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 247 | 248 | '@eslint/js@9.39.1': 249 | resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} 250 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 251 | 252 | '@eslint/object-schema@2.1.7': 253 | resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} 254 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 255 | 256 | '@eslint/plugin-kit@0.4.1': 257 | resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} 258 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 259 | 260 | '@humanfs/core@0.19.1': 261 | resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} 262 | engines: {node: '>=18.18.0'} 263 | 264 | '@humanfs/node@0.16.7': 265 | resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} 266 | engines: {node: '>=18.18.0'} 267 | 268 | '@humanwhocodes/module-importer@1.0.1': 269 | resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 270 | engines: {node: '>=12.22'} 271 | 272 | '@humanwhocodes/retry@0.4.3': 273 | resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} 274 | engines: {node: '>=18.18'} 275 | 276 | '@marijn/find-cluster-break@1.0.2': 277 | resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} 278 | 279 | '@sec-ant/readable-stream@0.4.1': 280 | resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} 281 | 282 | '@tokenizer/token@0.3.0': 283 | resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} 284 | 285 | '@types/codemirror@5.60.8': 286 | resolution: {integrity: sha512-VjFgDF/eB+Aklcy15TtOTLQeMjTo07k7KAjql8OK5Dirr7a6sJY4T1uVBDuTVG9VEmn1uUsohOpYnVfgC6/jyw==} 287 | 288 | '@types/estree@1.0.8': 289 | resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 290 | 291 | '@types/json-schema@7.0.15': 292 | resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 293 | 294 | '@types/node@22.19.1': 295 | resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} 296 | 297 | '@types/react-dom@19.2.3': 298 | resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} 299 | peerDependencies: 300 | '@types/react': ^19.2.0 301 | 302 | '@types/react@19.2.7': 303 | resolution: {integrity: sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==} 304 | 305 | '@types/tern@0.23.9': 306 | resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==} 307 | 308 | '@typescript-eslint/eslint-plugin@8.48.0': 309 | resolution: {integrity: sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==} 310 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 311 | peerDependencies: 312 | '@typescript-eslint/parser': ^8.48.0 313 | eslint: ^8.57.0 || ^9.0.0 314 | typescript: '>=4.8.4 <6.0.0' 315 | 316 | '@typescript-eslint/parser@8.48.0': 317 | resolution: {integrity: sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==} 318 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 319 | peerDependencies: 320 | eslint: ^8.57.0 || ^9.0.0 321 | typescript: '>=4.8.4 <6.0.0' 322 | 323 | '@typescript-eslint/project-service@8.48.0': 324 | resolution: {integrity: sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==} 325 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 326 | peerDependencies: 327 | typescript: '>=4.8.4 <6.0.0' 328 | 329 | '@typescript-eslint/scope-manager@8.48.0': 330 | resolution: {integrity: sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==} 331 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 332 | 333 | '@typescript-eslint/tsconfig-utils@8.48.0': 334 | resolution: {integrity: sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==} 335 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 336 | peerDependencies: 337 | typescript: '>=4.8.4 <6.0.0' 338 | 339 | '@typescript-eslint/type-utils@8.48.0': 340 | resolution: {integrity: sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==} 341 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 342 | peerDependencies: 343 | eslint: ^8.57.0 || ^9.0.0 344 | typescript: '>=4.8.4 <6.0.0' 345 | 346 | '@typescript-eslint/types@8.48.0': 347 | resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==} 348 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 349 | 350 | '@typescript-eslint/typescript-estree@8.48.0': 351 | resolution: {integrity: sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==} 352 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 353 | peerDependencies: 354 | typescript: '>=4.8.4 <6.0.0' 355 | 356 | '@typescript-eslint/utils@8.48.0': 357 | resolution: {integrity: sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==} 358 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 359 | peerDependencies: 360 | eslint: ^8.57.0 || ^9.0.0 361 | typescript: '>=4.8.4 <6.0.0' 362 | 363 | '@typescript-eslint/visitor-keys@8.48.0': 364 | resolution: {integrity: sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==} 365 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 366 | 367 | acorn-jsx@5.3.2: 368 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 369 | peerDependencies: 370 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 371 | 372 | acorn@8.15.0: 373 | resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} 374 | engines: {node: '>=0.4.0'} 375 | hasBin: true 376 | 377 | ajv@6.12.6: 378 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 379 | 380 | ansi-styles@4.3.0: 381 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 382 | engines: {node: '>=8'} 383 | 384 | argparse@2.0.1: 385 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 386 | 387 | balanced-match@1.0.2: 388 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 389 | 390 | brace-expansion@1.1.12: 391 | resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} 392 | 393 | brace-expansion@2.0.2: 394 | resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 395 | 396 | builtin-modules@4.0.0: 397 | resolution: {integrity: sha512-p1n8zyCkt1BVrKNFymOHjcDSAl7oq/gUvfgULv2EblgpPVQlQr9yHnWjg9IJ2MhfwPqiYqMMrr01OY7yQoK2yA==} 398 | engines: {node: '>=18.20'} 399 | 400 | callsites@3.1.0: 401 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 402 | engines: {node: '>=6'} 403 | 404 | chalk@4.1.2: 405 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 406 | engines: {node: '>=10'} 407 | 408 | color-convert@2.0.1: 409 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 410 | engines: {node: '>=7.0.0'} 411 | 412 | color-name@1.1.4: 413 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 414 | 415 | concat-map@0.0.1: 416 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 417 | 418 | crelt@1.0.6: 419 | resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} 420 | 421 | cross-spawn@7.0.6: 422 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 423 | engines: {node: '>= 8'} 424 | 425 | csstype@3.2.3: 426 | resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} 427 | 428 | debug@4.4.3: 429 | resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} 430 | engines: {node: '>=6.0'} 431 | peerDependencies: 432 | supports-color: '*' 433 | peerDependenciesMeta: 434 | supports-color: 435 | optional: true 436 | 437 | deep-is@0.1.4: 438 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 439 | 440 | esbuild@0.24.2: 441 | resolution: {integrity: sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==} 442 | engines: {node: '>=18'} 443 | hasBin: true 444 | 445 | escape-string-regexp@4.0.0: 446 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 447 | engines: {node: '>=10'} 448 | 449 | eslint-scope@8.4.0: 450 | resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} 451 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 452 | 453 | eslint-visitor-keys@3.4.3: 454 | resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 455 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 456 | 457 | eslint-visitor-keys@4.2.1: 458 | resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} 459 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 460 | 461 | eslint@9.39.1: 462 | resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} 463 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 464 | hasBin: true 465 | peerDependencies: 466 | jiti: '*' 467 | peerDependenciesMeta: 468 | jiti: 469 | optional: true 470 | 471 | espree@10.4.0: 472 | resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} 473 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 474 | 475 | esquery@1.6.0: 476 | resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} 477 | engines: {node: '>=0.10'} 478 | 479 | esrecurse@4.3.0: 480 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 481 | engines: {node: '>=4.0'} 482 | 483 | estraverse@5.3.0: 484 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 485 | engines: {node: '>=4.0'} 486 | 487 | esutils@2.0.3: 488 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 489 | engines: {node: '>=0.10.0'} 490 | 491 | fast-deep-equal@3.1.3: 492 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 493 | 494 | fast-json-stable-stringify@2.1.0: 495 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 496 | 497 | fast-levenshtein@2.0.6: 498 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 499 | 500 | fdir@6.5.0: 501 | resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} 502 | engines: {node: '>=12.0.0'} 503 | peerDependencies: 504 | picomatch: ^3 || ^4 505 | peerDependenciesMeta: 506 | picomatch: 507 | optional: true 508 | 509 | file-entry-cache@8.0.0: 510 | resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 511 | engines: {node: '>=16.0.0'} 512 | 513 | file-type@19.6.0: 514 | resolution: {integrity: sha512-VZR5I7k5wkD0HgFnMsq5hOsSc710MJMu5Nc5QYsbe38NN5iPV/XTObYLc/cpttRTf6lX538+5uO1ZQRhYibiZQ==} 515 | engines: {node: '>=18'} 516 | 517 | find-up@5.0.0: 518 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 519 | engines: {node: '>=10'} 520 | 521 | flat-cache@4.0.1: 522 | resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 523 | engines: {node: '>=16'} 524 | 525 | flatted@3.3.3: 526 | resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 527 | 528 | get-stream@9.0.1: 529 | resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} 530 | engines: {node: '>=18'} 531 | 532 | glob-parent@6.0.2: 533 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 534 | engines: {node: '>=10.13.0'} 535 | 536 | globals@14.0.0: 537 | resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 538 | engines: {node: '>=18'} 539 | 540 | graphemer@1.4.0: 541 | resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} 542 | 543 | has-flag@4.0.0: 544 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 545 | engines: {node: '>=8'} 546 | 547 | ieee754@1.2.1: 548 | resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} 549 | 550 | ignore@5.3.2: 551 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 552 | engines: {node: '>= 4'} 553 | 554 | ignore@7.0.5: 555 | resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} 556 | engines: {node: '>= 4'} 557 | 558 | import-fresh@3.3.1: 559 | resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} 560 | engines: {node: '>=6'} 561 | 562 | imurmurhash@0.1.4: 563 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 564 | engines: {node: '>=0.8.19'} 565 | 566 | is-extglob@2.1.1: 567 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 568 | engines: {node: '>=0.10.0'} 569 | 570 | is-glob@4.0.3: 571 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 572 | engines: {node: '>=0.10.0'} 573 | 574 | is-stream@4.0.1: 575 | resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} 576 | engines: {node: '>=18'} 577 | 578 | isexe@2.0.0: 579 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 580 | 581 | js-yaml@4.1.1: 582 | resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} 583 | hasBin: true 584 | 585 | json-buffer@3.0.1: 586 | resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 587 | 588 | json-schema-traverse@0.4.1: 589 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 590 | 591 | json-stable-stringify-without-jsonify@1.0.1: 592 | resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 593 | 594 | keyv@4.5.4: 595 | resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 596 | 597 | levn@0.4.1: 598 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 599 | engines: {node: '>= 0.8.0'} 600 | 601 | locate-path@6.0.0: 602 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 603 | engines: {node: '>=10'} 604 | 605 | lodash.merge@4.6.2: 606 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 607 | 608 | minimatch@3.1.2: 609 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 610 | 611 | minimatch@9.0.5: 612 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 613 | engines: {node: '>=16 || 14 >=14.17'} 614 | 615 | moment@2.29.4: 616 | resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==} 617 | 618 | ms@2.1.3: 619 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 620 | 621 | natural-compare@1.4.0: 622 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 623 | 624 | obsidian@1.10.3: 625 | resolution: {integrity: sha512-VP+ZSxNMG7y6Z+sU9WqLvJAskCfkFrTz2kFHWmmzis+C+4+ELjk/sazwcTHrHXNZlgCeo8YOlM6SOrAFCynNew==} 626 | peerDependencies: 627 | '@codemirror/state': 6.5.0 628 | '@codemirror/view': 6.38.6 629 | 630 | optionator@0.9.4: 631 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 632 | engines: {node: '>= 0.8.0'} 633 | 634 | p-limit@3.1.0: 635 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 636 | engines: {node: '>=10'} 637 | 638 | p-locate@5.0.0: 639 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 640 | engines: {node: '>=10'} 641 | 642 | parent-module@1.0.1: 643 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 644 | engines: {node: '>=6'} 645 | 646 | path-exists@4.0.0: 647 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 648 | engines: {node: '>=8'} 649 | 650 | path-key@3.1.1: 651 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 652 | engines: {node: '>=8'} 653 | 654 | peek-readable@5.4.2: 655 | resolution: {integrity: sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg==} 656 | engines: {node: '>=14.16'} 657 | 658 | picomatch@4.0.3: 659 | resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} 660 | engines: {node: '>=12'} 661 | 662 | prelude-ls@1.2.1: 663 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 664 | engines: {node: '>= 0.8.0'} 665 | 666 | punycode@2.3.1: 667 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 668 | engines: {node: '>=6'} 669 | 670 | react-dom@19.2.0: 671 | resolution: {integrity: sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==} 672 | peerDependencies: 673 | react: ^19.2.0 674 | 675 | react@19.2.0: 676 | resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} 677 | engines: {node: '>=0.10.0'} 678 | 679 | resolve-from@4.0.0: 680 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 681 | engines: {node: '>=4'} 682 | 683 | scheduler@0.27.0: 684 | resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} 685 | 686 | semver@7.7.3: 687 | resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} 688 | engines: {node: '>=10'} 689 | hasBin: true 690 | 691 | shebang-command@2.0.0: 692 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 693 | engines: {node: '>=8'} 694 | 695 | shebang-regex@3.0.0: 696 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 697 | engines: {node: '>=8'} 698 | 699 | strip-json-comments@3.1.1: 700 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 701 | engines: {node: '>=8'} 702 | 703 | strtok3@9.1.1: 704 | resolution: {integrity: sha512-FhwotcEqjr241ZbjFzjlIYg6c5/L/s4yBGWSMvJ9UoExiSqL+FnFA/CaeZx17WGaZMS/4SOZp8wH18jSS4R4lw==} 705 | engines: {node: '>=16'} 706 | 707 | style-mod@4.1.3: 708 | resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} 709 | 710 | supports-color@7.2.0: 711 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 712 | engines: {node: '>=8'} 713 | 714 | tinyglobby@0.2.15: 715 | resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} 716 | engines: {node: '>=12.0.0'} 717 | 718 | token-types@6.1.1: 719 | resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==} 720 | engines: {node: '>=14.16'} 721 | 722 | ts-api-utils@2.1.0: 723 | resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} 724 | engines: {node: '>=18.12'} 725 | peerDependencies: 726 | typescript: '>=4.8.4' 727 | 728 | tslib@2.8.1: 729 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 730 | 731 | type-check@0.4.0: 732 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 733 | engines: {node: '>= 0.8.0'} 734 | 735 | typescript@5.9.3: 736 | resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 737 | engines: {node: '>=14.17'} 738 | hasBin: true 739 | 740 | uint8array-extras@1.5.0: 741 | resolution: {integrity: sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==} 742 | engines: {node: '>=18'} 743 | 744 | undici-types@6.21.0: 745 | resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} 746 | 747 | uri-js@4.4.1: 748 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 749 | 750 | w3c-keyname@2.2.8: 751 | resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} 752 | 753 | which@2.0.2: 754 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 755 | engines: {node: '>= 8'} 756 | hasBin: true 757 | 758 | word-wrap@1.2.5: 759 | resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 760 | engines: {node: '>=0.10.0'} 761 | 762 | yocto-queue@0.1.0: 763 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 764 | engines: {node: '>=10'} 765 | 766 | snapshots: 767 | 768 | '@borewit/text-codec@0.1.1': {} 769 | 770 | '@codemirror/state@6.5.0': 771 | dependencies: 772 | '@marijn/find-cluster-break': 1.0.2 773 | 774 | '@codemirror/view@6.38.6': 775 | dependencies: 776 | '@codemirror/state': 6.5.0 777 | crelt: 1.0.6 778 | style-mod: 4.1.3 779 | w3c-keyname: 2.2.8 780 | 781 | '@esbuild/aix-ppc64@0.24.2': 782 | optional: true 783 | 784 | '@esbuild/android-arm64@0.24.2': 785 | optional: true 786 | 787 | '@esbuild/android-arm@0.24.2': 788 | optional: true 789 | 790 | '@esbuild/android-x64@0.24.2': 791 | optional: true 792 | 793 | '@esbuild/darwin-arm64@0.24.2': 794 | optional: true 795 | 796 | '@esbuild/darwin-x64@0.24.2': 797 | optional: true 798 | 799 | '@esbuild/freebsd-arm64@0.24.2': 800 | optional: true 801 | 802 | '@esbuild/freebsd-x64@0.24.2': 803 | optional: true 804 | 805 | '@esbuild/linux-arm64@0.24.2': 806 | optional: true 807 | 808 | '@esbuild/linux-arm@0.24.2': 809 | optional: true 810 | 811 | '@esbuild/linux-ia32@0.24.2': 812 | optional: true 813 | 814 | '@esbuild/linux-loong64@0.24.2': 815 | optional: true 816 | 817 | '@esbuild/linux-mips64el@0.24.2': 818 | optional: true 819 | 820 | '@esbuild/linux-ppc64@0.24.2': 821 | optional: true 822 | 823 | '@esbuild/linux-riscv64@0.24.2': 824 | optional: true 825 | 826 | '@esbuild/linux-s390x@0.24.2': 827 | optional: true 828 | 829 | '@esbuild/linux-x64@0.24.2': 830 | optional: true 831 | 832 | '@esbuild/netbsd-arm64@0.24.2': 833 | optional: true 834 | 835 | '@esbuild/netbsd-x64@0.24.2': 836 | optional: true 837 | 838 | '@esbuild/openbsd-arm64@0.24.2': 839 | optional: true 840 | 841 | '@esbuild/openbsd-x64@0.24.2': 842 | optional: true 843 | 844 | '@esbuild/sunos-x64@0.24.2': 845 | optional: true 846 | 847 | '@esbuild/win32-arm64@0.24.2': 848 | optional: true 849 | 850 | '@esbuild/win32-ia32@0.24.2': 851 | optional: true 852 | 853 | '@esbuild/win32-x64@0.24.2': 854 | optional: true 855 | 856 | '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)': 857 | dependencies: 858 | eslint: 9.39.1 859 | eslint-visitor-keys: 3.4.3 860 | 861 | '@eslint-community/regexpp@4.12.2': {} 862 | 863 | '@eslint/config-array@0.21.1': 864 | dependencies: 865 | '@eslint/object-schema': 2.1.7 866 | debug: 4.4.3 867 | minimatch: 3.1.2 868 | transitivePeerDependencies: 869 | - supports-color 870 | 871 | '@eslint/config-helpers@0.4.2': 872 | dependencies: 873 | '@eslint/core': 0.17.0 874 | 875 | '@eslint/core@0.17.0': 876 | dependencies: 877 | '@types/json-schema': 7.0.15 878 | 879 | '@eslint/eslintrc@3.3.1': 880 | dependencies: 881 | ajv: 6.12.6 882 | debug: 4.4.3 883 | espree: 10.4.0 884 | globals: 14.0.0 885 | ignore: 5.3.2 886 | import-fresh: 3.3.1 887 | js-yaml: 4.1.1 888 | minimatch: 3.1.2 889 | strip-json-comments: 3.1.1 890 | transitivePeerDependencies: 891 | - supports-color 892 | 893 | '@eslint/js@9.39.1': {} 894 | 895 | '@eslint/object-schema@2.1.7': {} 896 | 897 | '@eslint/plugin-kit@0.4.1': 898 | dependencies: 899 | '@eslint/core': 0.17.0 900 | levn: 0.4.1 901 | 902 | '@humanfs/core@0.19.1': {} 903 | 904 | '@humanfs/node@0.16.7': 905 | dependencies: 906 | '@humanfs/core': 0.19.1 907 | '@humanwhocodes/retry': 0.4.3 908 | 909 | '@humanwhocodes/module-importer@1.0.1': {} 910 | 911 | '@humanwhocodes/retry@0.4.3': {} 912 | 913 | '@marijn/find-cluster-break@1.0.2': {} 914 | 915 | '@sec-ant/readable-stream@0.4.1': {} 916 | 917 | '@tokenizer/token@0.3.0': {} 918 | 919 | '@types/codemirror@5.60.8': 920 | dependencies: 921 | '@types/tern': 0.23.9 922 | 923 | '@types/estree@1.0.8': {} 924 | 925 | '@types/json-schema@7.0.15': {} 926 | 927 | '@types/node@22.19.1': 928 | dependencies: 929 | undici-types: 6.21.0 930 | 931 | '@types/react-dom@19.2.3(@types/react@19.2.7)': 932 | dependencies: 933 | '@types/react': 19.2.7 934 | 935 | '@types/react@19.2.7': 936 | dependencies: 937 | csstype: 3.2.3 938 | 939 | '@types/tern@0.23.9': 940 | dependencies: 941 | '@types/estree': 1.0.8 942 | 943 | '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': 944 | dependencies: 945 | '@eslint-community/regexpp': 4.12.2 946 | '@typescript-eslint/parser': 8.48.0(eslint@9.39.1)(typescript@5.9.3) 947 | '@typescript-eslint/scope-manager': 8.48.0 948 | '@typescript-eslint/type-utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) 949 | '@typescript-eslint/utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) 950 | '@typescript-eslint/visitor-keys': 8.48.0 951 | eslint: 9.39.1 952 | graphemer: 1.4.0 953 | ignore: 7.0.5 954 | natural-compare: 1.4.0 955 | ts-api-utils: 2.1.0(typescript@5.9.3) 956 | typescript: 5.9.3 957 | transitivePeerDependencies: 958 | - supports-color 959 | 960 | '@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3)': 961 | dependencies: 962 | '@typescript-eslint/scope-manager': 8.48.0 963 | '@typescript-eslint/types': 8.48.0 964 | '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) 965 | '@typescript-eslint/visitor-keys': 8.48.0 966 | debug: 4.4.3 967 | eslint: 9.39.1 968 | typescript: 5.9.3 969 | transitivePeerDependencies: 970 | - supports-color 971 | 972 | '@typescript-eslint/project-service@8.48.0(typescript@5.9.3)': 973 | dependencies: 974 | '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) 975 | '@typescript-eslint/types': 8.48.0 976 | debug: 4.4.3 977 | typescript: 5.9.3 978 | transitivePeerDependencies: 979 | - supports-color 980 | 981 | '@typescript-eslint/scope-manager@8.48.0': 982 | dependencies: 983 | '@typescript-eslint/types': 8.48.0 984 | '@typescript-eslint/visitor-keys': 8.48.0 985 | 986 | '@typescript-eslint/tsconfig-utils@8.48.0(typescript@5.9.3)': 987 | dependencies: 988 | typescript: 5.9.3 989 | 990 | '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1)(typescript@5.9.3)': 991 | dependencies: 992 | '@typescript-eslint/types': 8.48.0 993 | '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) 994 | '@typescript-eslint/utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) 995 | debug: 4.4.3 996 | eslint: 9.39.1 997 | ts-api-utils: 2.1.0(typescript@5.9.3) 998 | typescript: 5.9.3 999 | transitivePeerDependencies: 1000 | - supports-color 1001 | 1002 | '@typescript-eslint/types@8.48.0': {} 1003 | 1004 | '@typescript-eslint/typescript-estree@8.48.0(typescript@5.9.3)': 1005 | dependencies: 1006 | '@typescript-eslint/project-service': 8.48.0(typescript@5.9.3) 1007 | '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) 1008 | '@typescript-eslint/types': 8.48.0 1009 | '@typescript-eslint/visitor-keys': 8.48.0 1010 | debug: 4.4.3 1011 | minimatch: 9.0.5 1012 | semver: 7.7.3 1013 | tinyglobby: 0.2.15 1014 | ts-api-utils: 2.1.0(typescript@5.9.3) 1015 | typescript: 5.9.3 1016 | transitivePeerDependencies: 1017 | - supports-color 1018 | 1019 | '@typescript-eslint/utils@8.48.0(eslint@9.39.1)(typescript@5.9.3)': 1020 | dependencies: 1021 | '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) 1022 | '@typescript-eslint/scope-manager': 8.48.0 1023 | '@typescript-eslint/types': 8.48.0 1024 | '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) 1025 | eslint: 9.39.1 1026 | typescript: 5.9.3 1027 | transitivePeerDependencies: 1028 | - supports-color 1029 | 1030 | '@typescript-eslint/visitor-keys@8.48.0': 1031 | dependencies: 1032 | '@typescript-eslint/types': 8.48.0 1033 | eslint-visitor-keys: 4.2.1 1034 | 1035 | acorn-jsx@5.3.2(acorn@8.15.0): 1036 | dependencies: 1037 | acorn: 8.15.0 1038 | 1039 | acorn@8.15.0: {} 1040 | 1041 | ajv@6.12.6: 1042 | dependencies: 1043 | fast-deep-equal: 3.1.3 1044 | fast-json-stable-stringify: 2.1.0 1045 | json-schema-traverse: 0.4.1 1046 | uri-js: 4.4.1 1047 | 1048 | ansi-styles@4.3.0: 1049 | dependencies: 1050 | color-convert: 2.0.1 1051 | 1052 | argparse@2.0.1: {} 1053 | 1054 | balanced-match@1.0.2: {} 1055 | 1056 | brace-expansion@1.1.12: 1057 | dependencies: 1058 | balanced-match: 1.0.2 1059 | concat-map: 0.0.1 1060 | 1061 | brace-expansion@2.0.2: 1062 | dependencies: 1063 | balanced-match: 1.0.2 1064 | 1065 | builtin-modules@4.0.0: {} 1066 | 1067 | callsites@3.1.0: {} 1068 | 1069 | chalk@4.1.2: 1070 | dependencies: 1071 | ansi-styles: 4.3.0 1072 | supports-color: 7.2.0 1073 | 1074 | color-convert@2.0.1: 1075 | dependencies: 1076 | color-name: 1.1.4 1077 | 1078 | color-name@1.1.4: {} 1079 | 1080 | concat-map@0.0.1: {} 1081 | 1082 | crelt@1.0.6: {} 1083 | 1084 | cross-spawn@7.0.6: 1085 | dependencies: 1086 | path-key: 3.1.1 1087 | shebang-command: 2.0.0 1088 | which: 2.0.2 1089 | 1090 | csstype@3.2.3: {} 1091 | 1092 | debug@4.4.3: 1093 | dependencies: 1094 | ms: 2.1.3 1095 | 1096 | deep-is@0.1.4: {} 1097 | 1098 | esbuild@0.24.2: 1099 | optionalDependencies: 1100 | '@esbuild/aix-ppc64': 0.24.2 1101 | '@esbuild/android-arm': 0.24.2 1102 | '@esbuild/android-arm64': 0.24.2 1103 | '@esbuild/android-x64': 0.24.2 1104 | '@esbuild/darwin-arm64': 0.24.2 1105 | '@esbuild/darwin-x64': 0.24.2 1106 | '@esbuild/freebsd-arm64': 0.24.2 1107 | '@esbuild/freebsd-x64': 0.24.2 1108 | '@esbuild/linux-arm': 0.24.2 1109 | '@esbuild/linux-arm64': 0.24.2 1110 | '@esbuild/linux-ia32': 0.24.2 1111 | '@esbuild/linux-loong64': 0.24.2 1112 | '@esbuild/linux-mips64el': 0.24.2 1113 | '@esbuild/linux-ppc64': 0.24.2 1114 | '@esbuild/linux-riscv64': 0.24.2 1115 | '@esbuild/linux-s390x': 0.24.2 1116 | '@esbuild/linux-x64': 0.24.2 1117 | '@esbuild/netbsd-arm64': 0.24.2 1118 | '@esbuild/netbsd-x64': 0.24.2 1119 | '@esbuild/openbsd-arm64': 0.24.2 1120 | '@esbuild/openbsd-x64': 0.24.2 1121 | '@esbuild/sunos-x64': 0.24.2 1122 | '@esbuild/win32-arm64': 0.24.2 1123 | '@esbuild/win32-ia32': 0.24.2 1124 | '@esbuild/win32-x64': 0.24.2 1125 | 1126 | escape-string-regexp@4.0.0: {} 1127 | 1128 | eslint-scope@8.4.0: 1129 | dependencies: 1130 | esrecurse: 4.3.0 1131 | estraverse: 5.3.0 1132 | 1133 | eslint-visitor-keys@3.4.3: {} 1134 | 1135 | eslint-visitor-keys@4.2.1: {} 1136 | 1137 | eslint@9.39.1: 1138 | dependencies: 1139 | '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) 1140 | '@eslint-community/regexpp': 4.12.2 1141 | '@eslint/config-array': 0.21.1 1142 | '@eslint/config-helpers': 0.4.2 1143 | '@eslint/core': 0.17.0 1144 | '@eslint/eslintrc': 3.3.1 1145 | '@eslint/js': 9.39.1 1146 | '@eslint/plugin-kit': 0.4.1 1147 | '@humanfs/node': 0.16.7 1148 | '@humanwhocodes/module-importer': 1.0.1 1149 | '@humanwhocodes/retry': 0.4.3 1150 | '@types/estree': 1.0.8 1151 | ajv: 6.12.6 1152 | chalk: 4.1.2 1153 | cross-spawn: 7.0.6 1154 | debug: 4.4.3 1155 | escape-string-regexp: 4.0.0 1156 | eslint-scope: 8.4.0 1157 | eslint-visitor-keys: 4.2.1 1158 | espree: 10.4.0 1159 | esquery: 1.6.0 1160 | esutils: 2.0.3 1161 | fast-deep-equal: 3.1.3 1162 | file-entry-cache: 8.0.0 1163 | find-up: 5.0.0 1164 | glob-parent: 6.0.2 1165 | ignore: 5.3.2 1166 | imurmurhash: 0.1.4 1167 | is-glob: 4.0.3 1168 | json-stable-stringify-without-jsonify: 1.0.1 1169 | lodash.merge: 4.6.2 1170 | minimatch: 3.1.2 1171 | natural-compare: 1.4.0 1172 | optionator: 0.9.4 1173 | transitivePeerDependencies: 1174 | - supports-color 1175 | 1176 | espree@10.4.0: 1177 | dependencies: 1178 | acorn: 8.15.0 1179 | acorn-jsx: 5.3.2(acorn@8.15.0) 1180 | eslint-visitor-keys: 4.2.1 1181 | 1182 | esquery@1.6.0: 1183 | dependencies: 1184 | estraverse: 5.3.0 1185 | 1186 | esrecurse@4.3.0: 1187 | dependencies: 1188 | estraverse: 5.3.0 1189 | 1190 | estraverse@5.3.0: {} 1191 | 1192 | esutils@2.0.3: {} 1193 | 1194 | fast-deep-equal@3.1.3: {} 1195 | 1196 | fast-json-stable-stringify@2.1.0: {} 1197 | 1198 | fast-levenshtein@2.0.6: {} 1199 | 1200 | fdir@6.5.0(picomatch@4.0.3): 1201 | optionalDependencies: 1202 | picomatch: 4.0.3 1203 | 1204 | file-entry-cache@8.0.0: 1205 | dependencies: 1206 | flat-cache: 4.0.1 1207 | 1208 | file-type@19.6.0: 1209 | dependencies: 1210 | get-stream: 9.0.1 1211 | strtok3: 9.1.1 1212 | token-types: 6.1.1 1213 | uint8array-extras: 1.5.0 1214 | 1215 | find-up@5.0.0: 1216 | dependencies: 1217 | locate-path: 6.0.0 1218 | path-exists: 4.0.0 1219 | 1220 | flat-cache@4.0.1: 1221 | dependencies: 1222 | flatted: 3.3.3 1223 | keyv: 4.5.4 1224 | 1225 | flatted@3.3.3: {} 1226 | 1227 | get-stream@9.0.1: 1228 | dependencies: 1229 | '@sec-ant/readable-stream': 0.4.1 1230 | is-stream: 4.0.1 1231 | 1232 | glob-parent@6.0.2: 1233 | dependencies: 1234 | is-glob: 4.0.3 1235 | 1236 | globals@14.0.0: {} 1237 | 1238 | graphemer@1.4.0: {} 1239 | 1240 | has-flag@4.0.0: {} 1241 | 1242 | ieee754@1.2.1: {} 1243 | 1244 | ignore@5.3.2: {} 1245 | 1246 | ignore@7.0.5: {} 1247 | 1248 | import-fresh@3.3.1: 1249 | dependencies: 1250 | parent-module: 1.0.1 1251 | resolve-from: 4.0.0 1252 | 1253 | imurmurhash@0.1.4: {} 1254 | 1255 | is-extglob@2.1.1: {} 1256 | 1257 | is-glob@4.0.3: 1258 | dependencies: 1259 | is-extglob: 2.1.1 1260 | 1261 | is-stream@4.0.1: {} 1262 | 1263 | isexe@2.0.0: {} 1264 | 1265 | js-yaml@4.1.1: 1266 | dependencies: 1267 | argparse: 2.0.1 1268 | 1269 | json-buffer@3.0.1: {} 1270 | 1271 | json-schema-traverse@0.4.1: {} 1272 | 1273 | json-stable-stringify-without-jsonify@1.0.1: {} 1274 | 1275 | keyv@4.5.4: 1276 | dependencies: 1277 | json-buffer: 3.0.1 1278 | 1279 | levn@0.4.1: 1280 | dependencies: 1281 | prelude-ls: 1.2.1 1282 | type-check: 0.4.0 1283 | 1284 | locate-path@6.0.0: 1285 | dependencies: 1286 | p-locate: 5.0.0 1287 | 1288 | lodash.merge@4.6.2: {} 1289 | 1290 | minimatch@3.1.2: 1291 | dependencies: 1292 | brace-expansion: 1.1.12 1293 | 1294 | minimatch@9.0.5: 1295 | dependencies: 1296 | brace-expansion: 2.0.2 1297 | 1298 | moment@2.29.4: {} 1299 | 1300 | ms@2.1.3: {} 1301 | 1302 | natural-compare@1.4.0: {} 1303 | 1304 | obsidian@1.10.3(@codemirror/state@6.5.0)(@codemirror/view@6.38.6): 1305 | dependencies: 1306 | '@codemirror/state': 6.5.0 1307 | '@codemirror/view': 6.38.6 1308 | '@types/codemirror': 5.60.8 1309 | moment: 2.29.4 1310 | 1311 | optionator@0.9.4: 1312 | dependencies: 1313 | deep-is: 0.1.4 1314 | fast-levenshtein: 2.0.6 1315 | levn: 0.4.1 1316 | prelude-ls: 1.2.1 1317 | type-check: 0.4.0 1318 | word-wrap: 1.2.5 1319 | 1320 | p-limit@3.1.0: 1321 | dependencies: 1322 | yocto-queue: 0.1.0 1323 | 1324 | p-locate@5.0.0: 1325 | dependencies: 1326 | p-limit: 3.1.0 1327 | 1328 | parent-module@1.0.1: 1329 | dependencies: 1330 | callsites: 3.1.0 1331 | 1332 | path-exists@4.0.0: {} 1333 | 1334 | path-key@3.1.1: {} 1335 | 1336 | peek-readable@5.4.2: {} 1337 | 1338 | picomatch@4.0.3: {} 1339 | 1340 | prelude-ls@1.2.1: {} 1341 | 1342 | punycode@2.3.1: {} 1343 | 1344 | react-dom@19.2.0(react@19.2.0): 1345 | dependencies: 1346 | react: 19.2.0 1347 | scheduler: 0.27.0 1348 | 1349 | react@19.2.0: {} 1350 | 1351 | resolve-from@4.0.0: {} 1352 | 1353 | scheduler@0.27.0: {} 1354 | 1355 | semver@7.7.3: {} 1356 | 1357 | shebang-command@2.0.0: 1358 | dependencies: 1359 | shebang-regex: 3.0.0 1360 | 1361 | shebang-regex@3.0.0: {} 1362 | 1363 | strip-json-comments@3.1.1: {} 1364 | 1365 | strtok3@9.1.1: 1366 | dependencies: 1367 | '@tokenizer/token': 0.3.0 1368 | peek-readable: 5.4.2 1369 | 1370 | style-mod@4.1.3: {} 1371 | 1372 | supports-color@7.2.0: 1373 | dependencies: 1374 | has-flag: 4.0.0 1375 | 1376 | tinyglobby@0.2.15: 1377 | dependencies: 1378 | fdir: 6.5.0(picomatch@4.0.3) 1379 | picomatch: 4.0.3 1380 | 1381 | token-types@6.1.1: 1382 | dependencies: 1383 | '@borewit/text-codec': 0.1.1 1384 | '@tokenizer/token': 0.3.0 1385 | ieee754: 1.2.1 1386 | 1387 | ts-api-utils@2.1.0(typescript@5.9.3): 1388 | dependencies: 1389 | typescript: 5.9.3 1390 | 1391 | tslib@2.8.1: {} 1392 | 1393 | type-check@0.4.0: 1394 | dependencies: 1395 | prelude-ls: 1.2.1 1396 | 1397 | typescript@5.9.3: {} 1398 | 1399 | uint8array-extras@1.5.0: {} 1400 | 1401 | undici-types@6.21.0: {} 1402 | 1403 | uri-js@4.4.1: 1404 | dependencies: 1405 | punycode: 2.3.1 1406 | 1407 | w3c-keyname@2.2.8: {} 1408 | 1409 | which@2.0.2: 1410 | dependencies: 1411 | isexe: 2.0.0 1412 | 1413 | word-wrap@1.2.5: {} 1414 | 1415 | yocto-queue@0.1.0: {} 1416 | --------------------------------------------------------------------------------