Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .github/workflows/changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ jobs:
RETRY_DELAY=5
ATTEMPT=0
SUCCESS=false

while [ $ATTEMPT -lt $MAX_RETRIES ] && [ "$SUCCESS" = "false" ]; do
ATTEMPT=$((ATTEMPT+1))
echo "Attempt $ATTEMPT of $MAX_RETRIES"
Expand Down Expand Up @@ -144,9 +144,8 @@ jobs:
exit 1
fi
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
COMMIT_MESSAGES_RAW: ${{ steps.commit_messages.outputs.commits }}

GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
COMMIT_MESSAGES_RAW: ${{ steps.commit_messages.outputs.commits }}

- name: Update CHANGELOG.md
# Only run if AI generated text
Expand Down Expand Up @@ -194,7 +193,6 @@ jobs:

echo "CHANGELOG.md updated."


- name: Commit and Push CHANGELOG.md
# Only run if AI generated text
if: steps.ai_changelog.outputs.changelog_entry != ''
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: "18.x"
registry-url: "https://npm.pkg.github.com"
scope: "@nutstore"
node-version: '18.x'
registry-url: 'https://npm.pkg.github.com'
scope: '@nutstore'

- name: Install pnpm
uses: pnpm/action-setup@v2
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/update-changelog-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- name: Checkout main branch
uses: actions/checkout@v4
with:
ref: main # Explicitly checkout main branch
ref: main # Explicitly checkout main branch
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

Expand All @@ -31,31 +31,31 @@ jobs:
echo "After removing refs/tags/: $VERSION"
VERSION=${VERSION#v}
echo "After removing v prefix: $VERSION"

echo "Current content before replacement:"
cat CHANGELOG.md

# Only replace "Unreleased" in the heading, not in other places
sed -i "s/## \[Unreleased\]/## [$VERSION]/" CHANGELOG.md

echo "Content after replacement:"
cat CHANGELOG.md

# Check if there are changes
if git diff --quiet CHANGELOG.md; then
echo "No changes were made to CHANGELOG.md"
exit 1
fi

# Commit and push changes
git add CHANGELOG.md
git commit -m "docs: update changelog version to $VERSION [skip ci]"
git push origin HEAD:main

# Update release description with CHANGELOG link
CHANGELOG_URL="https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md#$VERSION"
RELEASE_ID=$(jq --raw-output .release.id "$GITHUB_EVENT_PATH")

# Add CHANGELOG link to release description
curl \
-X PATCH \
Expand Down
94 changes: 58 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,49 +1,71 @@
# 🔄 Nutstore Sync

## Introduction | 简介

This plugin enables two-way synchronization between Obsidian notes and Nutstore via WebDAV protocol.
## 简介 | Introduction

此插件允许您通过 WebDAV 协议将 Obsidian 笔记与坚果云进行双向同步。
_This plugin enables two-way synchronization between Obsidian notes and Nutstore via WebDAV protocol._

---

## ✨ Key Features | 主要特性

- 🔄 **Two-way Sync**: Efficiently synchronize your notes across devices
- ⚡ **Incremental Sync**: Fast updates that only transfer changed files, making large vaults sync quickly
- 🔐 **Single Sign-On**: Connect to Nutstore with simple authorization instead of manually entering WebDAV credentials
- 📁 **WebDAV Explorer**: Visual file browser for remote file management
- 🔀 **Smart Conflict Resolution**:
- Character-level comparison to automatically merge changes when possible
- Option to use timestamp-based resolution (newest file wins)
- 🚀 **Loose Sync Mode**: Optimize performance for vaults with thousands of notes
- 📦 **Large File Handling**: Set size limits to skip large files for better performance
- 📊 **Sync Status Tracking**: Clear visual indicators of sync progress and completion
- 📝 **Detailed Logging**: Comprehensive logs for troubleshooting

<br>

- 🔄 **双向同步**: 高效地在多设备间同步笔记
- ⚡ **增量同步**: 只传输更改过的文件,使大型笔记库也能快速同步
- 🔐 **单点登录**: 通过简单授权连接坚果云,无需手动输入 WebDAV 凭据
- 📁 **WebDAV 文件浏览器**: 远程文件管理的可视化界面
- 🔀 **智能冲突解决**:
- 字符级比较自动合并可能的更改
- 支持基于时间戳的解决方案(最新文件优先)
- 🚀 **宽松同步模式**: 优化对包含数千笔记的仓库的性能
- 📦 **大文件处理**: 设置大小限制以跳过大文件,提升性能
- 📊 **同步状态跟踪**: 清晰的同步进度和完成提示
- 📝 **详细日志**: 全面的故障排查日志
## ✨ 主要特性 | Key Features

- 🔄 **双向同步 | Two-way Sync**
高效地在多设备间同步笔记。
_Efficiently synchronize your notes across devices._
- ⚡ **增量同步 | Incremental Sync**
只传输更改过的文件,使大型笔记库也能快速同步。
_Fast updates that only transfer changed files, making large vaults sync quickly._
- 🔐 **单点登录 | Single Sign-On**
通过简单授权连接坚果云,无需手动输入 WebDAV 凭据。
_Connect to Nutstore with simple authorization instead of manually entering WebDAV credentials._
- 📁 **WebDAV 文件浏览器 | WebDAV Explorer**
远程文件管理的可视化界面。
_Visual file browser for remote file management._
- 🔀 **智能冲突解决 | Smart Conflict Resolution**
字符级比较自动合并可能的更改;支持基于时间戳的解决方案(最新文件优先)。
_Character-level comparison to automatically merge changes when possible. Option to use timestamp-based resolution (newest file wins)._
- 🚀 **宽松同步模式 | Loose Sync Mode**
优化对包含数千笔记的仓库的性能。
_Optimize performance for vaults with thousands of notes._
- 📦 **大文件处理 | Large File Handling**
设置大小限制以跳过大文件,提升性能。
_Set size limits to skip large files for better performance._
- 📊 **同步状态跟踪 | Sync Status Tracking**
清晰的同步进度和完成提示。
_Clear visual indicators of sync progress and completion._
- 📝 **详细日志 | Detailed Logging**
全面的故障排查日志。
_Comprehensive logs for troubleshooting._
- 🤖 **AI 智能助手 | AI Agent**
内置 AI 助手,可通过自然语言读取、编辑和管理 Vault 中的文件。
_Built-in AI assistant that can read, edit, and manage files in your vault through natural language._

---

## ⚠️ Important Notes | 注意事项
## 🤖 AI 智能助手 | AI Agent

AI 助手是一个内置的智能代理,让你通过自然语言管理 Obsidian Vault。支持任意兼容 OpenAI 接口的服务商,可自主完成复杂的多步骤任务。
_The AI Agent is a built-in assistant that lets you manage your Obsidian vault through natural language. It supports any OpenAI-compatible provider and can handle complex, multi-step tasks autonomously._

Agent 在做出任何更改前都会请求用户确认。你可以逐条批准、按操作类型批准当前会话,或在设置中开启 *YOLO 模式* 全部自动通过。
_Before the agent makes any changes, it asks for your approval. You can approve individual operations, approve an operation type for the entire session, or enable _YOLO mode_ in settings to auto-approve everything._

- ⏳ Initial sync may take longer (especially with many files)
- 💾 Please backup before syncing
**配置方法 | Setup:**

1. 打开插件设置 → **AI** 标签页
_Open plugin settings → **AI** tab_
2. 添加 AI 服务商(支持任意兼容 OpenAI 接口的端点)并填写模型名称
_Add an AI provider (any OpenAI-compatible endpoint) and fill in the model name_
3. 从左侧边栏打开 AI 对话框,开始对话
_Open the AI chat panel from the left sidebar and start chatting_

---

<br>
## ⚠️ 注意事项 | Important Notes

- ⏳ 首次同步可能需要较长时间 (文件比较多时)
- 💾 请在同步之前备份
- ⏳ **首次同步 | Initial Sync**
首次同步可能需要较长时间(文件比较多时)。
_Initial sync may take longer (especially with many files)._
- 💾 **数据备份 | Backup**
请在同步之前备份。
_Please backup before syncing._
8 changes: 7 additions & 1 deletion esbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ const renamePlugin = {
name: 'rename-plugin',
setup(build) {
build.onEnd(async () => {
fs.renameSync(prod ? './dist/main.css' : './main.css', './styles.css')
const source = prod ? './dist/main.css' : './main.css'
if (fs.existsSync(source)) {
fs.renameSync(source, './styles.css')
}
})
},
}
Expand Down Expand Up @@ -60,6 +63,9 @@ const context = await esbuild.context({
}),
renamePlugin,
],
alias: {
'node:zlib': './src/shims/node-zlib.ts',
},
})

if (prod) {
Expand Down
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
"scripts": {
"dev": "run-p dev:*",
"dev:plugin": "node esbuild.config.mjs",
"dev:chatbox": "pnpm --filter chatbox dev",
"dev:webdav-explorer": "pnpm --filter webdav-explorer dev",
"build": "run-s build:webdav-explorer build:plugin",
"build": "run-s build:webdav-explorer build:chatbox build:plugin",
"build:chatbox": "pnpm --filter chatbox build",
"build:webdav-explorer": "pnpm --filter webdav-explorer build",
"build:plugin": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production && swc ./dist/main.js -o main.js",
"version": "node version-bump.mjs && git add manifest.json versions.json",
"test": "vitest"
},
"devDependencies": {
"@ai-sdk/openai": "^3.0.48",
"@deanc/esbuild-plugin-postcss": "^1.0.2",
"@electron/remote": "^2.1.2",
"@nutstore/sso-js": "^0.0.8",
Expand All @@ -28,13 +31,15 @@
"@typescript-eslint/parser": "5.29.0",
"@unocss/postcss": "66.1.0-beta.3",
"@vitest/coverage-v8": "^3.1.2",
"ai": "^6.0.149",
"assert": "^2.1.0",
"async-mutex": "^0.5.0",
"blob-polyfill": "^9.0.20240710",
"bottleneck": "^2.19.5",
"buffer": "^6.0.3",
"builtin-modules": "3.3.0",
"bytes-iec": "^3.1.1",
"chatbox": "workspace: *",
"consola": "^3.4.0",
"core-js": "^3.41.0",
"crypto-browserify": "^3.12.1",
Expand All @@ -50,6 +55,7 @@
"http-status-codes": "^2.3.0",
"i18next": "^24.2.2",
"js-base64": "^3.7.7",
"just-bash": "^2.14.0",
"localforage": "^1.10.0",
"lodash-es": "^4.17.21",
"node-diff3": "^3.1.2",
Expand All @@ -69,7 +75,8 @@
"uuid": "^13.0.0",
"vitest": "^3.1.2",
"webdav": "^5.7.1",
"webdav-explorer": "workspace: *"
"webdav-explorer": "workspace: *",
"zod": "^4.3.6"
},
"browser": {
"path": "path-browserify",
Expand Down
33 changes: 33 additions & 0 deletions packages/chatbox/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "chatbox",
"version": "0.0.0",
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
}
},
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "rslib build",
"dev": "rslib build --watch"
},
"devDependencies": {
"@rsbuild/plugin-babel": "^1.0.4",
"@rsbuild/plugin-solid": "^1.0.5",
"@rslib/core": "^0.5.3",
"@solid-primitives/i18n": "^2.2.0",
"@solid-primitives/media": "^2.3.0",
"@unocss/postcss": "66.1.0-beta.3",
"typescript": "^5.8.2",
"unocss": "66.1.0-beta.3"
},
"dependencies": {
"solid-js": "^1.9.5"
}
}
5 changes: 5 additions & 0 deletions packages/chatbox/postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import UnoCSS from '@unocss/postcss'

export default {
plugins: [UnoCSS()],
}
32 changes: 32 additions & 0 deletions packages/chatbox/rslib.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { pluginBabel } from '@rsbuild/plugin-babel'
import { pluginSolid } from '@rsbuild/plugin-solid'
import { defineConfig } from '@rslib/core'

export default defineConfig({
source: {
entry: {
index: ['./src/**'],
},
},
tools: {
rspack: {
plugins: [],
},
},
lib: [
{
bundle: false,
dts: true,
format: 'esm',
},
],
output: {
target: 'web',
},
plugins: [
pluginBabel({
include: /\.(?:jsx|tsx)$/,
}),
pluginSolid(),
],
})
Loading
Loading