feat: export to Joplin (#3607)
This commit is contained in:
parent
424eb09995
commit
eef141cbe7
@ -171,6 +171,7 @@
|
|||||||
"topics.export.obsidian_export_failed": "Export failed",
|
"topics.export.obsidian_export_failed": "Export failed",
|
||||||
"topics.export.obsidian_show_md_files": "Show MD Files",
|
"topics.export.obsidian_show_md_files": "Show MD Files",
|
||||||
"topics.export.obsidian_selected_path": "Selected Path",
|
"topics.export.obsidian_selected_path": "Selected Path",
|
||||||
|
"topics.export.joplin": "Export to Joplin",
|
||||||
"topics.list": "Topic List",
|
"topics.list": "Topic List",
|
||||||
"topics.move_to": "Move to",
|
"topics.move_to": "Move to",
|
||||||
"topics.pinned": "Pinned Topics",
|
"topics.pinned": "Pinned Topics",
|
||||||
@ -441,6 +442,8 @@
|
|||||||
"error.notion.no_api_key": "Notion ApiKey or Notion DatabaseID is not configured",
|
"error.notion.no_api_key": "Notion ApiKey or Notion DatabaseID is not configured",
|
||||||
"error.yuque.export": "Failed to export to Yuque. Please check connection status and configuration according to documentation",
|
"error.yuque.export": "Failed to export to Yuque. Please check connection status and configuration according to documentation",
|
||||||
"error.yuque.no_config": "Yuque Token or Yuque Url is not configured",
|
"error.yuque.no_config": "Yuque Token or Yuque Url is not configured",
|
||||||
|
"error.joplin.no_config": "Joplin Authorization Token or URL is not configured",
|
||||||
|
"error.joplin.export": "Failed to export to Joplin. Please keep Joplin running and check connection status or configuration",
|
||||||
"group.delete.content": "Deleting a group message will delete the user's question and all assistant's answers",
|
"group.delete.content": "Deleting a group message will delete the user's question and all assistant's answers",
|
||||||
"group.delete.title": "Delete Group Message",
|
"group.delete.title": "Delete Group Message",
|
||||||
"ignore.knowledge.base": "Web search mode is enabled, ignore knowledge base",
|
"ignore.knowledge.base": "Web search mode is enabled, ignore knowledge base",
|
||||||
@ -473,6 +476,7 @@
|
|||||||
"success.markdown.export.specified": "Successfully exported the Markdown file",
|
"success.markdown.export.specified": "Successfully exported the Markdown file",
|
||||||
"success.notion.export": "Successfully exported to Notion",
|
"success.notion.export": "Successfully exported to Notion",
|
||||||
"success.yuque.export": "Successfully exported to Yuque",
|
"success.yuque.export": "Successfully exported to Yuque",
|
||||||
|
"success.joplin.export": "Successfully exported to Joplin",
|
||||||
"switch.disabled": "Please wait for the current reply to complete",
|
"switch.disabled": "Please wait for the current reply to complete",
|
||||||
"topic.added": "New topic added",
|
"topic.added": "New topic added",
|
||||||
"upgrade.success.button": "Restart",
|
"upgrade.success.button": "Restart",
|
||||||
@ -790,6 +794,21 @@
|
|||||||
"title": "Obsidian Configuration",
|
"title": "Obsidian Configuration",
|
||||||
"api_key": "Obsidian API Key",
|
"api_key": "Obsidian API Key",
|
||||||
"api_key_placeholder": "Please enter the Obsidian API Key"
|
"api_key_placeholder": "Please enter the Obsidian API Key"
|
||||||
|
},
|
||||||
|
"joplin": {
|
||||||
|
"check": {
|
||||||
|
"button": "Check",
|
||||||
|
"empty_url": "Please enter Joplin Clipper Service URL",
|
||||||
|
"empty_token": "Please enter Joplin Authorization Token",
|
||||||
|
"fail": "Joplin connection verification failed",
|
||||||
|
"success": "Joplin connection verification successful"
|
||||||
|
},
|
||||||
|
"title": "Joplin Configuration",
|
||||||
|
"help": "In Joplin options, enable the web clipper (no browser extension needed), confirm the port, and copy the auth token here.",
|
||||||
|
"url": "Joplin Web Clipper Service URL",
|
||||||
|
"url_placeholder": "http://127.0.0.1:41184/",
|
||||||
|
"token": "Joplin Authorization Token",
|
||||||
|
"token_placeholder": "Joplin Authorization Token"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"display.assistant.title": "Assistant Settings",
|
"display.assistant.title": "Assistant Settings",
|
||||||
|
|||||||
@ -171,6 +171,7 @@
|
|||||||
"topics.export.obsidian_export_failed": "エクスポート失敗",
|
"topics.export.obsidian_export_failed": "エクスポート失敗",
|
||||||
"topics.export.obsidian_show_md_files": "mdファイルを表示",
|
"topics.export.obsidian_show_md_files": "mdファイルを表示",
|
||||||
"topics.export.obsidian_selected_path": "選択済みパス",
|
"topics.export.obsidian_selected_path": "選択済みパス",
|
||||||
|
"topics.export.joplin": "Joplin にエクスポート",
|
||||||
"topics.list": "トピックリスト",
|
"topics.list": "トピックリスト",
|
||||||
"topics.move_to": "移動先",
|
"topics.move_to": "移動先",
|
||||||
"topics.pinned": "トピックを固定",
|
"topics.pinned": "トピックを固定",
|
||||||
@ -441,6 +442,8 @@
|
|||||||
"error.notion.no_api_key": "Notion ApiKey または Notion DatabaseID が設定されていません",
|
"error.notion.no_api_key": "Notion ApiKey または Notion DatabaseID が設定されていません",
|
||||||
"error.yuque.export": "語雀へのエクスポートに失敗しました。接続状態と設定を確認してください",
|
"error.yuque.export": "語雀へのエクスポートに失敗しました。接続状態と設定を確認してください",
|
||||||
"error.yuque.no_config": "語雀Token または 知識ベースID が設定されていません",
|
"error.yuque.no_config": "語雀Token または 知識ベースID が設定されていません",
|
||||||
|
"error.joplin.no_config": "Joplin 認証トークン または URL が設定されていません",
|
||||||
|
"error.joplin.export": "Joplin へのエクスポートに失敗しました。Joplin が実行中であることを確認してください",
|
||||||
"group.delete.content": "分組メッセージを削除するとユーザーの質問と助け手の回答がすべて削除されます",
|
"group.delete.content": "分組メッセージを削除するとユーザーの質問と助け手の回答がすべて削除されます",
|
||||||
"group.delete.title": "分組メッセージを削除",
|
"group.delete.title": "分組メッセージを削除",
|
||||||
"loading.notion.preparing": "Notionへのエクスポートを準備中...",
|
"loading.notion.preparing": "Notionへのエクスポートを準備中...",
|
||||||
@ -473,6 +476,7 @@
|
|||||||
"success.markdown.export.specified": "Markdown ファイルを正常にエクスポートしました",
|
"success.markdown.export.specified": "Markdown ファイルを正常にエクスポートしました",
|
||||||
"success.notion.export": "Notionへのエクスポートに成功しました",
|
"success.notion.export": "Notionへのエクスポートに成功しました",
|
||||||
"success.yuque.export": "語雀へのエクスポートに成功しました",
|
"success.yuque.export": "語雀へのエクスポートに成功しました",
|
||||||
|
"success.joplin.export": "Joplin へのエクスポートに成功しました",
|
||||||
"switch.disabled": "現在の応答が完了するまで切り替えを無効にします",
|
"switch.disabled": "現在の応答が完了するまで切り替えを無効にします",
|
||||||
"topic.added": "新しいトピックが追加されました",
|
"topic.added": "新しいトピックが追加されました",
|
||||||
"upgrade.success.button": "再起動",
|
"upgrade.success.button": "再起動",
|
||||||
@ -790,6 +794,21 @@
|
|||||||
"title": "Obsidian 設定",
|
"title": "Obsidian 設定",
|
||||||
"api_key": "Obsidian API Key",
|
"api_key": "Obsidian API Key",
|
||||||
"api_key_placeholder": "Obsidian API Key を入力してください"
|
"api_key_placeholder": "Obsidian API Key を入力してください"
|
||||||
|
},
|
||||||
|
"joplin": {
|
||||||
|
"check": {
|
||||||
|
"button": "確認",
|
||||||
|
"empty_url": "Joplin 剪輯服務 URL を先に入力してください",
|
||||||
|
"empty_token": "Joplin 認証トークン を先に入力してください",
|
||||||
|
"fail": "Joplin 接続確認に失敗しました",
|
||||||
|
"success": "Joplin 接続確認に成功しました"
|
||||||
|
},
|
||||||
|
"title": "Joplin 設定",
|
||||||
|
"help": "Joplin オプションで、剪輯サービスを有効にしてください。ポート番号を確認し、認証トークンをコピーしてください",
|
||||||
|
"url": "Joplin 剪輯服務 URL",
|
||||||
|
"url_placeholder": "http://127.0.0.1:41184/",
|
||||||
|
"token": "Joplin 認証トークン",
|
||||||
|
"token_placeholder": "Joplin 認証トークンを入力してください"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"display.assistant.title": "アシスタント設定",
|
"display.assistant.title": "アシスタント設定",
|
||||||
|
|||||||
@ -171,6 +171,7 @@
|
|||||||
"topics.export.obsidian_export_failed": "Экспорт не удалось",
|
"topics.export.obsidian_export_failed": "Экспорт не удалось",
|
||||||
"topics.export.obsidian_show_md_files": "Показать файлы MD",
|
"topics.export.obsidian_show_md_files": "Показать файлы MD",
|
||||||
"topics.export.obsidian_selected_path": "Выбранный путь",
|
"topics.export.obsidian_selected_path": "Выбранный путь",
|
||||||
|
"topics.export.joplin": "Экспорт в Joplin",
|
||||||
"topics.list": "Список топиков",
|
"topics.list": "Список топиков",
|
||||||
"topics.move_to": "Переместить в",
|
"topics.move_to": "Переместить в",
|
||||||
"topics.pinned": "Закрепленные темы",
|
"topics.pinned": "Закрепленные темы",
|
||||||
@ -447,6 +448,8 @@
|
|||||||
"error.notion.no_api_key": "Notion ApiKey или Notion DatabaseID не настроен",
|
"error.notion.no_api_key": "Notion ApiKey или Notion DatabaseID не настроен",
|
||||||
"error.yuque.export": "Ошибка экспорта в Yuque, пожалуйста, проверьте состояние подключения и настройки в документации",
|
"error.yuque.export": "Ошибка экспорта в Yuque, пожалуйста, проверьте состояние подключения и настройки в документации",
|
||||||
"error.yuque.no_config": "Yuque Token или Yuque Url не настроен",
|
"error.yuque.no_config": "Yuque Token или Yuque Url не настроен",
|
||||||
|
"error.joplin.no_config": "Joplin Authorization Token или URL не настроен",
|
||||||
|
"error.joplin.export": "Не удалось экспортировать в Joplin, пожалуйста, убедитесь, что Joplin запущен и проверьте состояние подключения или настройки",
|
||||||
"group.delete.content": "Удаление группы сообщений удалит пользовательский вопрос и все ответы помощника",
|
"group.delete.content": "Удаление группы сообщений удалит пользовательский вопрос и все ответы помощника",
|
||||||
"group.delete.title": "Удалить группу сообщений",
|
"group.delete.title": "Удалить группу сообщений",
|
||||||
"ignore.knowledge.base": "Режим сети включен, игнорировать базу знаний",
|
"ignore.knowledge.base": "Режим сети включен, игнорировать базу знаний",
|
||||||
@ -479,6 +482,7 @@
|
|||||||
"success.markdown.export.specified": "Файл Markdown успешно экспортирован",
|
"success.markdown.export.specified": "Файл Markdown успешно экспортирован",
|
||||||
"success.notion.export": "Успешный экспорт в Notion",
|
"success.notion.export": "Успешный экспорт в Notion",
|
||||||
"success.yuque.export": "Успешный экспорт в Yuque",
|
"success.yuque.export": "Успешный экспорт в Yuque",
|
||||||
|
"success.joplin.export": "Успешный экспорт в Joplin",
|
||||||
"switch.disabled": "Пожалуйста, дождитесь завершения текущего ответа",
|
"switch.disabled": "Пожалуйста, дождитесь завершения текущего ответа",
|
||||||
"topic.added": "Новый топик добавлен",
|
"topic.added": "Новый топик добавлен",
|
||||||
"upgrade.success.button": "Перезапустить",
|
"upgrade.success.button": "Перезапустить",
|
||||||
@ -790,6 +794,21 @@
|
|||||||
"title": "Настройка Obsidian",
|
"title": "Настройка Obsidian",
|
||||||
"api_key": "API Key Obsidian",
|
"api_key": "API Key Obsidian",
|
||||||
"api_key_placeholder": "Введите API Key Obsidian"
|
"api_key_placeholder": "Введите API Key Obsidian"
|
||||||
|
},
|
||||||
|
"joplin": {
|
||||||
|
"check": {
|
||||||
|
"button": "Проверить",
|
||||||
|
"empty_url": "Сначала введите URL Joplin",
|
||||||
|
"empty_token": "Сначала введите токен Joplin",
|
||||||
|
"fail": "Не удалось проверить подключение к Joplin",
|
||||||
|
"success": "Подключение к Joplin успешно проверено"
|
||||||
|
},
|
||||||
|
"title": "Настройка Joplin",
|
||||||
|
"help": "Включите Joplin опцию, проверьте порт и скопируйте токен",
|
||||||
|
"url": "URL Joplin",
|
||||||
|
"url_placeholder": "http://127.0.0.1:41184/",
|
||||||
|
"token": "Токен Joplin",
|
||||||
|
"token_placeholder": "Введите токен Joplin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"display.assistant.title": "Настройки ассистентов",
|
"display.assistant.title": "Настройки ассистентов",
|
||||||
|
|||||||
@ -171,6 +171,7 @@
|
|||||||
"topics.export.obsidian_export_failed": "导出失败",
|
"topics.export.obsidian_export_failed": "导出失败",
|
||||||
"topics.export.obsidian_show_md_files": "显示md文件",
|
"topics.export.obsidian_show_md_files": "显示md文件",
|
||||||
"topics.export.obsidian_selected_path": "已选择路径",
|
"topics.export.obsidian_selected_path": "已选择路径",
|
||||||
|
"topics.export.joplin": "导出到 Joplin",
|
||||||
"topics.list": "话题列表",
|
"topics.list": "话题列表",
|
||||||
"topics.move_to": "移动到",
|
"topics.move_to": "移动到",
|
||||||
"topics.pinned": "固定话题",
|
"topics.pinned": "固定话题",
|
||||||
@ -441,6 +442,8 @@
|
|||||||
"error.notion.no_api_key": "未配置 Notion API Key 或 Notion Database ID",
|
"error.notion.no_api_key": "未配置 Notion API Key 或 Notion Database ID",
|
||||||
"error.yuque.export": "导出语雀错误,请检查连接状态并对照文档检查配置",
|
"error.yuque.export": "导出语雀错误,请检查连接状态并对照文档检查配置",
|
||||||
"error.yuque.no_config": "未配置语雀 Token 或 知识库 URL",
|
"error.yuque.no_config": "未配置语雀 Token 或 知识库 URL",
|
||||||
|
"error.joplin.no_config": "未配置 Joplin 授权令牌 或 URL",
|
||||||
|
"error.joplin.export": "导出 Joplin 失败,请保持 Joplin 已运行并检查连接状态或检查配置",
|
||||||
"group.delete.content": "删除分组消息会删除用户提问和所有助手的回答",
|
"group.delete.content": "删除分组消息会删除用户提问和所有助手的回答",
|
||||||
"group.delete.title": "删除分组消息",
|
"group.delete.title": "删除分组消息",
|
||||||
"ignore.knowledge.base": "联网模式开启,忽略知识库",
|
"ignore.knowledge.base": "联网模式开启,忽略知识库",
|
||||||
@ -473,6 +476,7 @@
|
|||||||
"success.markdown.export.specified": "成功导出 Markdown 文件",
|
"success.markdown.export.specified": "成功导出 Markdown 文件",
|
||||||
"success.notion.export": "成功导出到 Notion",
|
"success.notion.export": "成功导出到 Notion",
|
||||||
"success.yuque.export": "成功导出到语雀",
|
"success.yuque.export": "成功导出到语雀",
|
||||||
|
"success.joplin.export": "成功导出到 Joplin",
|
||||||
"switch.disabled": "请等待当前回复完成后操作",
|
"switch.disabled": "请等待当前回复完成后操作",
|
||||||
"topic.added": "话题添加成功",
|
"topic.added": "话题添加成功",
|
||||||
"upgrade.success.button": "重启",
|
"upgrade.success.button": "重启",
|
||||||
@ -790,6 +794,21 @@
|
|||||||
"title": "Obsidian 配置",
|
"title": "Obsidian 配置",
|
||||||
"api_key": "Obsidian API Key",
|
"api_key": "Obsidian API Key",
|
||||||
"api_key_placeholder": "请输入 Obsidian API Key"
|
"api_key_placeholder": "请输入 Obsidian API Key"
|
||||||
|
},
|
||||||
|
"joplin": {
|
||||||
|
"check": {
|
||||||
|
"button": "检查",
|
||||||
|
"empty_url": "请先输入 Joplin 剪裁服务监听 URL",
|
||||||
|
"empty_token": "请先输入 Joplin 授权令牌",
|
||||||
|
"fail": "Joplin 连接验证失败",
|
||||||
|
"success": "Joplin 连接验证成功"
|
||||||
|
},
|
||||||
|
"title": "Joplin 配置",
|
||||||
|
"help": "在 Joplin 选项中,启用网页剪裁服务(无需安装浏览器插件),确认端口号,并复制授权令牌",
|
||||||
|
"url": "Joplin 剪裁服务监听 URL",
|
||||||
|
"url_placeholder": "http://127.0.0.1:41184/",
|
||||||
|
"token": "Joplin 授权令牌",
|
||||||
|
"token_placeholder": "请输入 Joplin 授权令牌"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"display.assistant.title": "助手设置",
|
"display.assistant.title": "助手设置",
|
||||||
|
|||||||
@ -171,6 +171,7 @@
|
|||||||
"topics.export.obsidian_export_failed": "匯出失敗",
|
"topics.export.obsidian_export_failed": "匯出失敗",
|
||||||
"topics.export.obsidian_show_md_files": "顯示md文件",
|
"topics.export.obsidian_show_md_files": "顯示md文件",
|
||||||
"topics.export.obsidian_selected_path": "已選擇路徑",
|
"topics.export.obsidian_selected_path": "已選擇路徑",
|
||||||
|
"topics.export.joplin": "匯出到 Joplin",
|
||||||
"topics.list": "話題列表",
|
"topics.list": "話題列表",
|
||||||
"topics.move_to": "移動到",
|
"topics.move_to": "移動到",
|
||||||
"topics.pinned": "固定話題",
|
"topics.pinned": "固定話題",
|
||||||
@ -441,6 +442,8 @@
|
|||||||
"error.notion.no_api_key": "未設定 Notion API Key 或 Notion Database ID",
|
"error.notion.no_api_key": "未設定 Notion API Key 或 Notion Database ID",
|
||||||
"error.yuque.export": "匯出語雀錯誤,請檢查連接狀態並對照文件檢查設定",
|
"error.yuque.export": "匯出語雀錯誤,請檢查連接狀態並對照文件檢查設定",
|
||||||
"error.yuque.no_config": "未設定語雀 Token 或知識庫 Url",
|
"error.yuque.no_config": "未設定語雀 Token 或知識庫 Url",
|
||||||
|
"error.joplin.no_config": "未設定 Joplin 授權Token 或 URL",
|
||||||
|
"error.joplin.export": "匯出 Joplin 失敗,請保持 Joplin 已運行並檢查連接狀態或檢查設定",
|
||||||
"group.delete.content": "刪除分組訊息會刪除使用者提問和所有助手的回答",
|
"group.delete.content": "刪除分組訊息會刪除使用者提問和所有助手的回答",
|
||||||
"group.delete.title": "刪除分組訊息",
|
"group.delete.title": "刪除分組訊息",
|
||||||
"ignore.knowledge.base": "網路模式開啟,忽略知識庫",
|
"ignore.knowledge.base": "網路模式開啟,忽略知識庫",
|
||||||
@ -473,6 +476,7 @@
|
|||||||
"success.markdown.export.specified": "成功導出 Markdown 文件",
|
"success.markdown.export.specified": "成功導出 Markdown 文件",
|
||||||
"success.notion.export": "成功匯出到 Notion",
|
"success.notion.export": "成功匯出到 Notion",
|
||||||
"success.yuque.export": "成功匯出到語雀",
|
"success.yuque.export": "成功匯出到語雀",
|
||||||
|
"success.joplin.export": "成功匯出到 Joplin",
|
||||||
"switch.disabled": "請等待當前回覆完成",
|
"switch.disabled": "請等待當前回覆完成",
|
||||||
"topic.added": "新話題已新增",
|
"topic.added": "新話題已新增",
|
||||||
"upgrade.success.button": "重新啟動",
|
"upgrade.success.button": "重新啟動",
|
||||||
@ -790,6 +794,21 @@
|
|||||||
"title": "Obsidian 設定",
|
"title": "Obsidian 設定",
|
||||||
"api_key": "Obsidian API Key",
|
"api_key": "Obsidian API Key",
|
||||||
"api_key_placeholder": "請輸入 Obsidian API Key"
|
"api_key_placeholder": "請輸入 Obsidian API Key"
|
||||||
|
},
|
||||||
|
"joplin": {
|
||||||
|
"check": {
|
||||||
|
"button": "檢查",
|
||||||
|
"empty_url": "請先輸入 Joplin 剪輯服務 URL",
|
||||||
|
"empty_token": "請先輸入 Joplin 授權Token",
|
||||||
|
"fail": "Joplin 連接驗證失敗",
|
||||||
|
"success": "Joplin 連接驗證成功"
|
||||||
|
},
|
||||||
|
"title": "Joplin 設定",
|
||||||
|
"help": "在 Joplin 選項中,啟用剪輯服務(無需安裝瀏覽器外掛),確認埠編號,並複製授權Token",
|
||||||
|
"url": "Joplin 剪輯服務 URL",
|
||||||
|
"url_placeholder": "http://127.0.0.1:41184/",
|
||||||
|
"token": "Joplin 授權Token",
|
||||||
|
"token_placeholder": "請輸入 Joplin 授權Token"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"display.assistant.title": "助手設定",
|
"display.assistant.title": "助手設定",
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import { Message, Model } from '@renderer/types'
|
|||||||
import { Assistant, Topic } from '@renderer/types'
|
import { Assistant, Topic } from '@renderer/types'
|
||||||
import { captureScrollableDivAsBlob, captureScrollableDivAsDataURL, removeTrailingDoubleSpaces } from '@renderer/utils'
|
import { captureScrollableDivAsBlob, captureScrollableDivAsDataURL, removeTrailingDoubleSpaces } from '@renderer/utils'
|
||||||
import {
|
import {
|
||||||
|
exportMarkdownToJoplin,
|
||||||
exportMarkdownToNotion,
|
exportMarkdownToNotion,
|
||||||
exportMarkdownToYuque,
|
exportMarkdownToYuque,
|
||||||
exportMessageAsMarkdown,
|
exportMessageAsMarkdown,
|
||||||
@ -222,6 +223,15 @@ const MessageMenubar: FC<Props> = (props) => {
|
|||||||
const title = getMessageTitle(message)
|
const title = getMessageTitle(message)
|
||||||
await ObsidianExportPopup.show({ title, markdown })
|
await ObsidianExportPopup.show({ title, markdown })
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('chat.topics.export.joplin'),
|
||||||
|
key: 'joplin',
|
||||||
|
onClick: async () => {
|
||||||
|
const title = getMessageTitle(message)
|
||||||
|
const markdown = messageToMarkdown(message)
|
||||||
|
exportMarkdownToJoplin(title, markdown)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,7 @@ import { Assistant, Topic } from '@renderer/types'
|
|||||||
import { removeSpecialCharactersForFileName } from '@renderer/utils'
|
import { removeSpecialCharactersForFileName } from '@renderer/utils'
|
||||||
import { copyTopicAsMarkdown } from '@renderer/utils/copy'
|
import { copyTopicAsMarkdown } from '@renderer/utils/copy'
|
||||||
import {
|
import {
|
||||||
|
exportMarkdownToJoplin,
|
||||||
exportMarkdownToYuque,
|
exportMarkdownToYuque,
|
||||||
exportTopicAsMarkdown,
|
exportTopicAsMarkdown,
|
||||||
exportTopicToNotion,
|
exportTopicToNotion,
|
||||||
@ -263,6 +264,14 @@ const Topics: FC<Props> = ({ assistant: _assistant, activeTopic, setActiveTopic
|
|||||||
const markdown = await topicToMarkdown(topic)
|
const markdown = await topicToMarkdown(topic)
|
||||||
await ObsidianExportPopup.show({ title: topic.name, markdown })
|
await ObsidianExportPopup.show({ title: topic.name, markdown })
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t('chat.topics.export.joplin'),
|
||||||
|
key: 'joplin',
|
||||||
|
onClick: async () => {
|
||||||
|
const markdown = await topicToMarkdown(topic)
|
||||||
|
exportMarkdownToJoplin(topic.name, markdown)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||||
|
import JoplinSettings from './JoplinSettings'
|
||||||
import MarkdownExportSettings from './MarkdownExportSettings'
|
import MarkdownExportSettings from './MarkdownExportSettings'
|
||||||
import NotionSettings from './NotionSettings'
|
import NotionSettings from './NotionSettings'
|
||||||
import ObsidianSettings from './ObsidianSettings'
|
import ObsidianSettings from './ObsidianSettings'
|
||||||
@ -35,6 +36,13 @@ const DataSettings: FC = () => {
|
|||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
const [menu, setMenu] = useState<string>('data')
|
const [menu, setMenu] = useState<string>('data')
|
||||||
|
|
||||||
|
//joplin icon needs to be updated into iconfont
|
||||||
|
const JoplinIcon = () => (
|
||||||
|
<svg viewBox="0 0 24 24" width="16" height="16" fill="grey" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M20.97 0h-8.9a.15.15 0 00-.16.15v2.83c0 .1.08.17.18.17h1.22c.49 0 .89.38.93.86V17.4l-.01.36-.05.29-.04.13a2.06 2.06 0 01-.38.7l-.02.03a2.08 2.08 0 01-.37.34c-.5.35-1.17.5-1.92.43a4.66 4.66 0 01-2.67-1.22 3.96 3.96 0 01-1.34-2.42c-.1-.78.14-1.47.65-1.93l.07-.05c.37-.31.84-.5 1.39-.55a.09.09 0 00.01 0l.3-.01.35.01h.02a4.39 4.39 0 011.5.44c.15.08.17 0 .18-.06V9.63a.26.26 0 00-.2-.26 7.5 7.5 0 00-6.76 1.61 6.37 6.37 0 00-2.03 5.5 8.18 8.18 0 002.71 5.08A9.35 9.35 0 0011.81 24c1.88 0 3.62-.64 4.9-1.81a6.32 6.32 0 002.06-4.3l.01-10.86V4.08a.95.95 0 01.95-.93h1.22a.17.17 0 00.17-.17V.15a.15.15 0 00-.15-.15z" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{ key: 'data', title: 'settings.data.data.title', icon: <DatabaseOutlined style={{ fontSize: 16 }} /> },
|
{ key: 'data', title: 'settings.data.data.title', icon: <DatabaseOutlined style={{ fontSize: 16 }} /> },
|
||||||
{ key: 'webdav', title: 'settings.data.webdav.title', icon: <CloudSyncOutlined style={{ fontSize: 16 }} /> },
|
{ key: 'webdav', title: 'settings.data.webdav.title', icon: <CloudSyncOutlined style={{ fontSize: 16 }} /> },
|
||||||
@ -53,6 +61,12 @@ const DataSettings: FC = () => {
|
|||||||
key: 'obsidian',
|
key: 'obsidian',
|
||||||
title: 'settings.data.obsidian.title',
|
title: 'settings.data.obsidian.title',
|
||||||
icon: <i className="iconfont icon-obsidian" />
|
icon: <i className="iconfont icon-obsidian" />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'joplin',
|
||||||
|
title: 'settings.data.joplin.title',
|
||||||
|
//joplin icon needs to be updated into iconfont
|
||||||
|
icon: <JoplinIcon />
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -191,6 +205,7 @@ const DataSettings: FC = () => {
|
|||||||
{menu === 'notion' && <NotionSettings />}
|
{menu === 'notion' && <NotionSettings />}
|
||||||
{menu === 'yuque' && <YuqueSettings />}
|
{menu === 'yuque' && <YuqueSettings />}
|
||||||
{menu === 'obsidian' && <ObsidianSettings />}
|
{menu === 'obsidian' && <ObsidianSettings />}
|
||||||
|
{menu === 'joplin' && <JoplinSettings />}
|
||||||
</SettingContainer>
|
</SettingContainer>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|||||||
117
src/renderer/src/pages/settings/DataSettings/JoplinSettings.tsx
Normal file
117
src/renderer/src/pages/settings/DataSettings/JoplinSettings.tsx
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { InfoCircleOutlined } from '@ant-design/icons'
|
||||||
|
import { HStack } from '@renderer/components/Layout'
|
||||||
|
import MinApp from '@renderer/components/MinApp'
|
||||||
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
|
import { RootState, useAppDispatch } from '@renderer/store'
|
||||||
|
import { setJoplinToken, setJoplinUrl } from '@renderer/store/settings'
|
||||||
|
import { Button, Tooltip } from 'antd'
|
||||||
|
import Input from 'antd/es/input/Input'
|
||||||
|
import { FC } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useSelector } from 'react-redux'
|
||||||
|
|
||||||
|
import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
|
||||||
|
|
||||||
|
const JoplinSettings: FC = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const { theme } = useTheme()
|
||||||
|
const dispatch = useAppDispatch()
|
||||||
|
|
||||||
|
const joplinToken = useSelector((state: RootState) => state.settings.joplinToken)
|
||||||
|
const joplinUrl = useSelector((state: RootState) => state.settings.joplinUrl)
|
||||||
|
|
||||||
|
const handleJoplinTokenChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
dispatch(setJoplinToken(e.target.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleJoplinUrlChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
dispatch(setJoplinUrl(e.target.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleJoplinUrlBlur = (e: React.FocusEvent<HTMLInputElement>) => {
|
||||||
|
let url = e.target.value
|
||||||
|
// 确保URL以/结尾,但只在失去焦点时执行
|
||||||
|
if (url && !url.endsWith('/')) {
|
||||||
|
url = `${url}/`
|
||||||
|
dispatch(setJoplinUrl(url))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleJoplinConnectionCheck = async () => {
|
||||||
|
try {
|
||||||
|
if (!joplinToken) {
|
||||||
|
window.message.error(t('settings.data.joplin.check.empty_token'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!joplinUrl) {
|
||||||
|
window.message.error(t('settings.data.joplin.check.empty_url'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetch(`${joplinUrl}notes?limit=1&token=${joplinToken}`)
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
|
||||||
|
if (!response.ok || data?.error) {
|
||||||
|
window.message.error(t('settings.data.joplin.check.fail'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
window.message.success(t('settings.data.joplin.check.success'))
|
||||||
|
} catch (e) {
|
||||||
|
window.message.error(t('settings.data.joplin.check.fail'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleJoplinHelpClick = () => {
|
||||||
|
MinApp.start({
|
||||||
|
id: 'joplin-help',
|
||||||
|
name: 'Joplin Help',
|
||||||
|
url: 'https://joplinapp.org/help/apps/clipper'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingGroup theme={theme}>
|
||||||
|
<SettingTitle>{t('settings.data.joplin.title')}</SettingTitle>
|
||||||
|
<SettingDivider />
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitle>{t('settings.data.joplin.url')}</SettingRowTitle>
|
||||||
|
<HStack alignItems="center" gap="5px" style={{ width: 315 }}>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
value={joplinUrl || ''}
|
||||||
|
onChange={handleJoplinUrlChange}
|
||||||
|
onBlur={handleJoplinUrlBlur}
|
||||||
|
style={{ width: 315 }}
|
||||||
|
placeholder={t('settings.data.joplin.url_placeholder')}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
</SettingRow>
|
||||||
|
<SettingDivider />
|
||||||
|
<SettingRow>
|
||||||
|
<SettingRowTitle style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
|
<span>{t('settings.data.joplin.token')}</span>
|
||||||
|
<Tooltip title={t('settings.data.joplin.help')} placement="left">
|
||||||
|
<InfoCircleOutlined
|
||||||
|
style={{ color: 'var(--color-text-2)', cursor: 'pointer', marginLeft: 4 }}
|
||||||
|
onClick={handleJoplinHelpClick}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</SettingRowTitle>
|
||||||
|
<HStack alignItems="center" gap="5px" style={{ width: 315 }}>
|
||||||
|
<Input
|
||||||
|
type="password"
|
||||||
|
value={joplinToken || ''}
|
||||||
|
onChange={handleJoplinTokenChange}
|
||||||
|
style={{ width: 250 }}
|
||||||
|
placeholder={t('settings.data.joplin.token_placeholder')}
|
||||||
|
/>
|
||||||
|
<Button onClick={handleJoplinConnectionCheck}>{t('settings.data.joplin.check.button')}</Button>
|
||||||
|
</HStack>
|
||||||
|
</SettingRow>
|
||||||
|
</SettingGroup>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default JoplinSettings
|
||||||
@ -84,6 +84,8 @@ export interface SettingsState {
|
|||||||
yuqueRepoId: string | null
|
yuqueRepoId: string | null
|
||||||
obsidianApiKey: string | null
|
obsidianApiKey: string | null
|
||||||
obsidianUrl: string | null
|
obsidianUrl: string | null
|
||||||
|
joplinToken: string | null
|
||||||
|
joplinUrl: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold' | 'grid'
|
export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold' | 'grid'
|
||||||
@ -152,7 +154,9 @@ const initialState: SettingsState = {
|
|||||||
yuqueUrl: '',
|
yuqueUrl: '',
|
||||||
yuqueRepoId: '',
|
yuqueRepoId: '',
|
||||||
obsidianApiKey: '',
|
obsidianApiKey: '',
|
||||||
obsidianUrl: ''
|
obsidianUrl: '',
|
||||||
|
joplinToken: '',
|
||||||
|
joplinUrl: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingsSlice = createSlice({
|
const settingsSlice = createSlice({
|
||||||
@ -353,6 +357,12 @@ const settingsSlice = createSlice({
|
|||||||
},
|
},
|
||||||
setObsidianUrl: (state, action: PayloadAction<string>) => {
|
setObsidianUrl: (state, action: PayloadAction<string>) => {
|
||||||
state.obsidianUrl = action.payload
|
state.obsidianUrl = action.payload
|
||||||
|
},
|
||||||
|
setJoplinToken: (state, action: PayloadAction<string>) => {
|
||||||
|
state.joplinToken = action.payload
|
||||||
|
},
|
||||||
|
setJoplinUrl: (state, action: PayloadAction<string>) => {
|
||||||
|
state.joplinUrl = action.payload
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -420,7 +430,9 @@ export const {
|
|||||||
setYuqueRepoId,
|
setYuqueRepoId,
|
||||||
setYuqueUrl,
|
setYuqueUrl,
|
||||||
setObsidianApiKey,
|
setObsidianApiKey,
|
||||||
setObsidianUrl
|
setObsidianUrl,
|
||||||
|
setJoplinToken,
|
||||||
|
setJoplinUrl
|
||||||
} = settingsSlice.actions
|
} = settingsSlice.actions
|
||||||
|
|
||||||
export default settingsSlice.reducer
|
export default settingsSlice.reducer
|
||||||
|
|||||||
@ -378,3 +378,42 @@ export const exportMarkdownToObsidian = async (
|
|||||||
window.message.error(i18n.t('chat.topics.export.obsidian_export_failed'))
|
window.message.error(i18n.t('chat.topics.export.obsidian_export_failed'))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const exportMarkdownToJoplin = async (title: string, content: string) => {
|
||||||
|
const { joplinUrl, joplinToken } = store.getState().settings
|
||||||
|
|
||||||
|
if (!joplinUrl || !joplinToken) {
|
||||||
|
window.message.error(i18n.t('message.error.joplin.no_config'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const baseUrl = joplinUrl.endsWith('/') ? joplinUrl : `${joplinUrl}/`
|
||||||
|
const response = await fetch(`${baseUrl}notes?token=${joplinToken}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
title: title,
|
||||||
|
body: content,
|
||||||
|
source: 'Cherry Studio'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('service not available')
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json()
|
||||||
|
if (data?.error) {
|
||||||
|
throw new Error('response error')
|
||||||
|
}
|
||||||
|
|
||||||
|
window.message.success(i18n.t('message.success.joplin.export'))
|
||||||
|
return
|
||||||
|
} catch (error) {
|
||||||
|
window.message.error(i18n.t('message.error.joplin.export'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user