From e04949e75c5d30cef89e263c8f3dd4f4f0840558 Mon Sep 17 00:00:00 2001 From: delphi Date: Tue, 11 Mar 2025 17:02:31 +0800 Subject: [PATCH] fix api link copy bug --- README.md | 213 ++++++++++++++++++++++++++------------- src/cuboxApi.ts | 28 ++--- src/main.ts | 33 +++--- src/templateProcessor.ts | 15 +-- 4 files changed, 164 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index c773152..2b18492 100644 --- a/README.md +++ b/README.md @@ -1,94 +1,171 @@ -# Obsidian Sample Plugin +# Obsidian Cubox Sync -This is a sample plugin for Obsidian (https://obsidian.md). +![Obsidian Cubox Sync](https://img.shields.io/badge/Obsidian-Cubox%20Sync-7963E6) -This project uses TypeScript to provide type checking and documentation. -The repo depends on the latest plugin API (obsidian.d.ts) in TypeScript Definition format, which contains TSDoc comments describing what it does. +> 将 Cubox 中的文章和标注同步到 Obsidian,打造无缝知识管理流程。 -This sample plugin demonstrates some of the basic functionality the plugin API can do. -- Adds a ribbon icon, which shows a Notice when clicked. -- Adds a command "Open Sample Modal" which opens a Modal. -- Adds a plugin setting tab to the settings page. -- Registers a global click event and output 'click' to the console. -- Registers a global interval which logs 'setInterval' to the console. +Obsidian Cubox Sync 是一个强大的 Obsidian 插件,它允许您将 Cubox 中收藏的文章、网页和标注内容自动同步到 Obsidian 笔记中。通过这个插件,您可以将 Cubox 作为信息收集工具,而 Obsidian 作为知识整理和思考工具,构建完整的个人知识管理系统。 -## First time developing plugins? +## ✨ 主要功能 -Quick starting guide for new plugin devs: +- **自动同步**:定时从 Cubox 同步文章和标注到 Obsidian +- **灵活过滤**:支持按文件夹、内容类型、状态和标签过滤同步内容 +- **自定义模板**:使用 Mustache 模板语法自定义文件名、前置元数据和内容格式 +- **高亮标注**:将 Cubox 中的高亮内容同步到 Obsidian 笔记中 +- **增量同步**:智能识别新增和更新的内容,避免重复同步 +- **手动触发**:支持通过命令或按钮手动触发同步 -- Check if [someone already developed a plugin for what you want](https://obsidian.md/plugins)! There might be an existing plugin similar enough that you can partner up with. -- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it). -- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder. -- Install NodeJS, then run `npm i` in the command line under your repo folder. -- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`. -- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`. -- Reload Obsidian to load the new version of your plugin. -- Enable plugin in settings window. -- For updates to the Obsidian API run `npm update` in the command line under your repo folder. +## 🚀 安装方法 -## Releasing new releases +### 从 Obsidian 社区插件库安装 -- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release. -- Update your `versions.json` file with `"new-plugin-version": "minimum-obsidian-version"` so older versions of Obsidian can download an older version of your plugin that's compatible. -- Create new GitHub release using your new version number as the "Tag version". Use the exact version number, don't include a prefix `v`. See here for an example: https://github.com/obsidianmd/obsidian-sample-plugin/releases -- Upload the files `manifest.json`, `main.js`, `styles.css` as binary attachments. Note: The manifest.json file must be in two places, first the root path of your repository and also in the release. -- Publish the release. +1. 打开 Obsidian 设置 +2. 进入 "第三方插件" 选项卡 +3. 关闭 "安全模式" +4. 点击 "浏览" 按钮 +5. 搜索 "Cubox Sync" +6. 点击安装 -> You can simplify the version bump process by running `npm version patch`, `npm version minor` or `npm version major` after updating `minAppVersion` manually in `manifest.json`. -> The command will bump version in `manifest.json` and `package.json`, and add the entry for the new version to `versions.json` +### 手动安装 -## Adding your plugin to the community plugin list +1. 下载最新版本的 `main.js`、`manifest.json` 和 `styles.css` 文件 +2. 在您的 Obsidian 库中创建 `.obsidian/plugins/obsidian-cubox-sync` 文件夹 +3. 将下载的文件复制到该文件夹中 +4. 在 Obsidian 设置中启用插件 -- Check the [plugin guidelines](https://docs.obsidian.md/Plugins/Releasing/Plugin+guidelines). -- Publish an initial version. -- Make sure you have a `README.md` file in the root of your repo. -- Make a pull request at https://github.com/obsidianmd/obsidian-releases to add your plugin. +## ⚙️ 配置说明 -## How to use +### 连接设置 -- Clone this repo. -- Make sure your NodeJS is at least v16 (`node --version`). -- `npm i` or `yarn` to install dependencies. -- `npm run dev` to start compilation in watch mode. +1. **Cubox 服务器域名**:选择您使用的 Cubox 服务器域名(cubox.cc 或 cubox.pro) +2. **Cubox API 密钥**:输入您的 Cubox API 密钥(可在 Cubox 网页版设置中创建) -## Manually installing the plugin +### 过滤设置 -- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`. +- **文件夹过滤**:选择要同步的 Cubox 文件夹 +- **类型过滤**:选择要同步的内容类型(文章、视频、图片等) +- **状态过滤**:选择要同步的内容状态(已读、未读等) +- **标签过滤**:选择要同步的标签 -## Improve code quality with eslint (optional) -- [ESLint](https://eslint.org/) is a tool that analyzes your code to quickly find problems. You can run ESLint against your plugin to find common bugs and ways to improve your code. -- To use eslint with this project, make sure to install eslint from terminal: - - `npm install -g eslint` -- To use eslint to analyze this project use this command: - - `eslint main.ts` - - eslint will then create a report with suggestions for code improvement by file and line number. -- If your source code is in a folder, such as `src`, you can use eslint with this command to analyze all files in that folder: - - `eslint .\src\` +### 同步设置 -## Funding URL +- **同步频率**:设置自动同步的时间间隔(分钟),设为 0 表示仅手动同步 +- **目标文件夹**:设置同步内容保存到 Obsidian 的哪个文件夹 +- **文件名模板**:设置生成的笔记文件名格式 +- **前置元数据**:设置笔记的前置元数据字段 +- **内容模板**:设置笔记内容的格式 +- **日期格式**:设置日期时间的显示格式 -You can include funding URLs where people who use your plugin can financially support it. +## 🔍 模板变量 -The simple way is to set the `fundingUrl` field to your link in your `manifest.json` file: +### 文件名模板变量 -```json -{ - "fundingUrl": "https://buymeacoffee.com" -} +- `{{title}}` - 文章标题 +- `{{article_title}}` - 原始文章标题 +- `{{create_time}}` - 创建时间 +- `{{update_time}}` - 更新时间 +- `{{domain}}` - 域名 +- `{{type}}` - 内容类型 + +### 前置元数据变量 + +- `title` - 文章标题 +- `article_title` - 原始文章标题 +- `tags` - 标签列表 +- `create_time` - 创建时间 +- `update_time` - 更新时间 +- `domain` - 域名 +- `url` - 原始链接 +- `cubox_url` - Cubox 链接 +- `description` - 描述 +- `words_count` - 字数统计 +- `type` - 内容类型 +- `id` - 文章 ID + +### 内容模板变量 + +- `{{title}}` - 文章标题 +- `{{description}}` - 描述 +- `{{content}}` - 文章内容 +- `{{content_highlighted}}` - 带高亮的文章内容 +- `{{highlights.length}}` - 高亮数量 +- `{{#highlights}}` ... `{{/highlights}}` - 高亮内容循环 + - `{{text}}` - 高亮文本 + - `{{note}}` - 高亮笔记 + - `{{cubox_url}}` - 高亮链接 + - `{{color}}` - 高亮颜色 + - `{{create_time}}` - 高亮创建时间 + +## 📝 使用示例 + +### 基本内容模板示例 + +``` +# {{{title}}} + +{{{description}}} + +[Read in Cubox]({{{cubox_url}}}) +[Read Original]({{{url}}}) + +{{{content}}} + +{{#highlights.length}} +## Annotations + +{{#highlights}} +> {{{text}}} +{{{note}}} +[Link️]({{{cubox_url}}}) + +{{/highlights}} +{{/highlights.length}} ``` -If you have multiple URLs, you can also do: +### 前置元数据示例 -```json -{ - "fundingUrl": { - "Buy Me a Coffee": "https://buymeacoffee.com", - "GitHub Sponsor": "https://github.com/sponsors", - "Patreon": "https://www.patreon.com/" - } -} +``` +title,url,tags,create_time,domain ``` -## API Documentation +或使用别名: -See https://github.com/obsidianmd/obsidian-api +``` +title,url,tags,create_time::date,domain::source +``` + +## 🔄 同步流程 + +1. 插件会根据设置的频率自动同步,或者您可以手动触发同步 +2. 同步时会根据过滤条件获取 Cubox 中的文章 +3. 对于每篇文章,插件会根据模板生成文件名、前置元数据和内容 +4. 生成的笔记会保存到指定的目标文件夹中 +5. 同步完成后,状态栏会显示最后同步时间 + +## 🛠️ 开发者信息 + +### 构建项目 + +1. 克隆仓库 +2. 安装依赖:`npm install` +3. 开发模式:`npm run dev` +4. 构建生产版本:`npm run build` + +### 主要依赖 + +- Mustache:用于模板渲染 +- Luxon:用于日期时间处理 + +## 📄 许可证 + +本项目采用 MIT 许可证。详情请参阅 [LICENSE](LICENSE) 文件。 + +## 🙏 致谢 + +- [Obsidian](https://obsidian.md/) - 知识管理工具 +- [Cubox](https://cubox.pro/) - 网络内容收藏工具 +- 所有贡献者和用户 + +--- + +如有问题或建议,请在 GitHub 仓库中提交 Issue。 diff --git a/src/cuboxApi.ts b/src/cuboxApi.ts index ffa2ea5..e9a23d9 100644 --- a/src/cuboxApi.ts +++ b/src/cuboxApi.ts @@ -14,7 +14,7 @@ export interface CuboxArticle { update_time: string; word_count: number; content?: string; - cubox_url: string; + cubox_url: string; highlights?: CuboxHighlight[]; tags?: string[]; type: string; @@ -177,8 +177,8 @@ export class CuboxApi { body: JSON.stringify(requestBody) }) as ListResponse; - const articles = response.data; - const hasMore = articles.length >= pageSize; + const articles = response.data ?? []; + const hasMore = articles && articles.length >= pageSize; return { articles, @@ -203,24 +203,8 @@ export class CuboxApi { return response.data; } catch (error) { console.error(`获取文章 ${articleId} 详情失败:`, error); - new Notice(`获取文章详情失败`); - return null; - } - } - /** - * 获取文章的高亮内容 - * @param articleId 文章ID - */ - async getHighlights(articleId: string): Promise { - try { - // 这里需要实现获取高亮的API调用 - // 暂时返回空数组 - return []; - } catch (error) { - console.error(`获取文章 ${articleId} 高亮内容失败:`, error); - new Notice(`获取高亮内容失败`); - return []; + return null; } } @@ -232,7 +216,7 @@ export class CuboxApi { const path = '/c/api/third-party/group/list'; const response = await this.request(path) as FoldersResponse; - return response.data; + return response.data ?? []; } catch (error) { console.error('获取 Cubox 文件夹列表失败:', error); new Notice('获取 Cubox 文件夹列表失败'); @@ -248,7 +232,7 @@ export class CuboxApi { const path = '/c/api/third-party/tag/list'; const response = await this.request(path) as TagsResponse; - return response.data; + return response.data ?? []; } catch (error) { console.error('获取 Cubox 标签列表失败:', error); new Notice('获取 Cubox 标签列表失败'); diff --git a/src/main.ts b/src/main.ts index 4c21c35..0b07617 100644 --- a/src/main.ts +++ b/src/main.ts @@ -51,7 +51,6 @@ interface CuboxSyncSettings { lastSyncCardId: string | null; lastCardUpdateTime: string | null; syncing: boolean; - skipExistingFiles: boolean; // 是否跳过已存在的文件 } const DEFAULT_SETTINGS: CuboxSyncSettings = { @@ -75,7 +74,6 @@ const DEFAULT_SETTINGS: CuboxSyncSettings = { lastSyncCardId: null, lastCardUpdateTime: null, syncing: false, - skipExistingFiles: false } @@ -153,15 +151,17 @@ export default class CuboxSyncPlugin extends Plugin { // 如果频率大于0,设置新的定时器 if (this.settings.syncFrequency > 0) { + const frequency = Math.min(this.settings.syncFrequency, 1440); // 最大24小时 + this.syncIntervalId = window.setInterval( async () => await this.syncCubox(), - this.settings.syncFrequency * 60 * 1000 + frequency * 60 * 1000 ); this.registerInterval(this.syncIntervalId); } } - async syncCubox(continueLastSync: boolean = true) { + async syncCubox() { // 如果已经在同步中,则跳过 if (this.settings.syncing) { new Notice('Sync is in progress, please wait.'); @@ -169,7 +169,7 @@ export default class CuboxSyncPlugin extends Plugin { } try { - // 设置同步状态为 true + // 设置同步状态为进行中 this.settings.syncing = true; await this.saveSettings(); @@ -180,8 +180,8 @@ export default class CuboxSyncPlugin extends Plugin { await this.ensureTargetFolder(); // 如果选择继续上次同步,则使用保存的 lastSyncCardId - let lastCardId: string | null = continueLastSync ? this.settings.lastSyncCardId : null; - let lastCardUpdateTime: string | null = continueLastSync ? this.settings.lastCardUpdateTime : null; + let lastCardId: string | null = this.settings.lastSyncCardId; + let lastCardUpdateTime: string | null = this.settings.lastCardUpdateTime; let hasMore = true; let syncCount = 0; let errorCount = 0; @@ -250,7 +250,7 @@ export default class CuboxSyncPlugin extends Plugin { const filePath = `${this.settings.targetFolder}/${filename}.md`; // 检查文件是否已存在,如果设置了跳过已存在文件则跳过 - if (this.settings.skipExistingFiles && await this.app.vault.adapter.exists(filePath)) { + if (await this.app.vault.adapter.exists(filePath)) { console.log(`文件已存在,跳过: ${filePath}`); skipCount++; continue; @@ -282,9 +282,11 @@ export default class CuboxSyncPlugin extends Plugin { this.settings.syncing = false; await this.saveSettings(); + // 更新状态栏 + this.statusBarItem.setText(`上次同步: ${this.formatLastSyncTime()}`); + const message = `Cubox sync completed: ${syncCount} articles synchronized${skipCount > 0 ? `, ${skipCount} skipped` : ''}${errorCount > 0 ? `, ${errorCount} errors` : ''}`; new Notice(message); - this.statusBarItem.setText(`上次同步: ${this.formatLastSyncTime()} (成功)`); } catch (error) { console.error('同步 Cubox 数据失败:', error); new Notice('Cubox sync failed. Please check settings or network.'); @@ -402,7 +404,7 @@ class CuboxSyncSettingTab extends PluginSettingTab { await this.plugin.saveSettings(); // 更新 Cubox API 配置 - this.plugin.updateCuboxApiConfig(this.plugin.settings.domain, value); + this.plugin.updateCuboxApiConfig(this.plugin.settings.domain, apiKey); }); // 如果未选择域名,则禁用输入框 @@ -574,17 +576,6 @@ class CuboxSyncSettingTab extends PluginSettingTab { // 同步设置部分 containerEl.createEl('h3', {text: 'Sync'}); - new Setting(containerEl) - .setName('Skip Existing Files') - .setDesc('If enabled, files that already exist in the target folder will be skipped during sync.') - .addToggle(toggle => toggle - .setValue(this.plugin.settings.skipExistingFiles) - .onChange(async (value) => { - this.plugin.settings.skipExistingFiles = value; - await this.plugin.saveSettings(); - }) - ); - new Setting(containerEl) .setName('Sync Interval') .setDesc('Auto sync interval (in minutes). 0 means manual sync. Each item syncs only once. Subsequent updates won\'t be synced, and modifications in Obsidian won\'t affect Cubox. We recommend avoiding frequent updates.') diff --git a/src/templateProcessor.ts b/src/templateProcessor.ts index ed89673..429853c 100644 --- a/src/templateProcessor.ts +++ b/src/templateProcessor.ts @@ -177,20 +177,7 @@ export class TemplateProcessor { articleView.content_highlighted = this.generateHighlightedContent(article.content, article.highlights); } - // 3. 调试输出 (可以在生产环境中移除) - console.log('ArticleView for template rendering:', JSON.stringify({ - title: articleView.title, - highlights_length: articleView.highlights_length, - highlights_count: articleView.highlights.length, - has_highlighted_content: 'content_highlighted' in articleView, - first_highlight: articleView.highlights.length > 0 ? { - text: articleView.highlights[0].text, - note: articleView.highlights[0].note, - cubox_url: articleView.highlights[0].cubox_url - } : null - }, null, 2)); - - // 4. 使用 Mustache 渲染模板 + // 3. 使用 Mustache 渲染模板 return Mustache.render(template, articleView); }