Compare commits

...

6 Commits

11 changed files with 241 additions and 24 deletions

View File

@@ -2,8 +2,11 @@ name: Release
on:
push:
branches:
- main
tags:
- "*"
- "v*.*.*"
- "[0-9]+.[0-9]+.[0-9]+"
env:
PLUGIN_NAME: share-to-notionnext # Change this to match the id of your plugin.

View File

@@ -1,6 +1,6 @@
# Changelog
## [Unreleased]
## v2.8.0-beta.1 (2025-10-31)
### Added

View File

@@ -22,6 +22,21 @@ In the plugin settings, you can add and configure the Notion databases you want
- [3⃣ Custom Database](#3⃣-custom-database)
- [Finalizing Configuration](#finalizing-configuration)
## Auto Sync Frontmatter Entry
If you enable auto sync, the plugin needs a frontmatter entry that lists which configured databases should receive updates. You can customise the name of this entry in **Settings → Auto Sync Frontmatter Key** (default: `autosync-database`). Use any text you like—letters, numbers, emojis, or other scripts are all supported.
In your note's frontmatter, add the configured key and list the database abbreviations you created in the settings:
```yaml
---
title: My Article
autosync-database: [blog, ideas]
---
```
The entry can be a YAML list or comma-separated string, and manual uploads will automatically add the current database abbreviation if it is missing. If you change the key name in settings, update your frontmatter to match the new value.
## 1⃣ General Database
This is the most basic database type and is suitable for most users.

View File

@@ -22,6 +22,25 @@ The plugin supports automatic syncing that monitors your notes for changes and a
3. Enable the toggle
4. Configure the "Auto Sync Delay" (default: 5 seconds, minimum: 2 seconds)
### Prepare the Frontmatter
Auto sync reads the database list from the frontmatter key you configured in **Settings → Auto Sync Frontmatter Key** (default: `autosync-database`). To make sure your notes can sync automatically:
- Add the configured key to your note's frontmatter
- List one or more database abbreviations that you defined in the plugin settings
- Keep the list updated if you change the databases a note should sync to
Example with the default key:
```yaml
---
title: My Article
autosync-database: [blog, portfolio]
---
```
If you change the key name in the settings, update your frontmatter to match. The plugin will prompt you when the entry is missing.
### How Auto Sync Works
When auto sync is enabled:
@@ -32,7 +51,7 @@ When auto sync is enabled:
### Auto Sync Scenarios
#### Scenario A: New Document (Not Yet Synced)
#### Scenario A: Missing Auto Sync Entry
```yaml
---
@@ -41,18 +60,34 @@ tags: [blog, tech]
---
```
**Behavior:**
- ✅ Detects that the configured auto sync key is missing
- ✅ Shows notice: `⚠️ Auto sync skipped: Add a "{key}" entry in the frontmatter to choose databases`
- ✅ No sync operation performed
- 📝 **Action Required:** Add the configured key (default `autosync-database`) to the frontmatter with the database abbreviations
#### Scenario B: New Document (Not Yet Synced)
```yaml
---
title: My New Article
aytosync-database: [blog]
---
```
**Behavior:**
- ✅ Detects no NotionID present
- ✅ Shows notice: "⚠️ Auto sync skipped: This document has not been synced to Notion, please upload manually first"
- ✅ No sync operation performed
- 📝 **Action Required:** Manually sync the document first using the command palette
#### Scenario B: Synced to One Database
#### Scenario C: Synced to One Database
```yaml
---
title: My Article
NotionID-blog: abc123
autosync-database: [blog]
---
```
@@ -62,7 +97,7 @@ NotionID-blog: abc123
- ✅ Shows success/failure notification from the upload command
- 📝 **No Action Required:** Changes are automatically synced
#### Scenario C: Synced to Multiple Databases
#### Scenario D: Synced to Multiple Databases
```yaml
---
@@ -80,6 +115,21 @@ NotionID-notes: ghi789
- ✅ Shows individual result notifications for each database
- 📝 **No Action Required:** Changes are automatically synced to all linked databases
#### Scenario E: Custom Frontmatter Key
```yaml
---
title: My Article
NotionID-blog: abc123
NotionID-portfolio: def456
🚀-sync-targets: [blog, portfolio]
---
```
**Behavior:**
- ✅ Uses your custom key (for example `🚀-sync-targets`) configured in settings
- ✅ Syncs to the listed databases when NotionIDs are present
- 📝 **Remember:** Update both the setting and your frontmatter if you rename the key
### Auto Sync Best Practices
1. **First Sync Manually**: Always perform the first sync manually to establish the NotionID link

View File

@@ -37,6 +37,8 @@ export const en = {
NotionLinkDisplayDesc: "Default is ON, if you want to hide the link in the front matter, please turn it off",
AutoSync: "Auto Sync",
AutoSyncDesc: "Automatically sync to Notion when frontmatter or content is modified (requires existing NotionID)",
AutoSyncFrontmatterKey: "Auto Sync Frontmatter Key",
AutoSyncFrontmatterKeyDesc: "Set the frontmatter property name that lists which databases should auto sync (defaults to autosync-database).",
AutoSyncDelay: "Auto Sync Delay (seconds)",
AutoSyncDelayDesc: "How many seconds to wait after document modification before triggering auto sync (default: 5 seconds, minimum: 2 seconds)",
AutoSyncDelayText: "Enter delay in seconds",
@@ -86,6 +88,7 @@ export const en = {
CheckConsole: "Check the console for more information \n opt+cmd+i/ctrl+shift+i",
SettingsMigrated: "✨ Plugin settings updated! Auto sync feature added, check plugin settings",
AutoSyncNoNotionID: "⚠️ Auto sync skipped: This document has not been synced to Notion, please upload manually first",
AutoSyncMissingDatabaseList: "⚠️ Auto sync skipped: Add a \"{key}\" entry in the frontmatter to choose databases",
AutoSyncMultipleSync: "🔄 Auto sync: Syncing to {count} database(s)...",
AutoSyncFailed: "Auto sync to {database} failed: {error}",
AutoSyncError: "Auto sync failed for {filename}: {error}",

View File

@@ -35,6 +35,8 @@ export const ja = {
NotionLinkDisplayDesc: "デフォルトはONです。front matterにリンクを非表示にしたい場合は、オフにしてください",
AutoSync: "自動同期",
AutoSyncDesc: "frontmatter またはコンテンツが変更されたときに自動的に Notion に同期しますNotionID が必要)",
AutoSyncFrontmatterKey: "自動同期 frontmatter キー",
AutoSyncFrontmatterKeyDesc: "自動同期するデータベースを列挙する frontmatter のプロパティ名を設定します(デフォルトは autosync-database。",
AutoSyncDelay: "自動同期遅延時間(秒)",
AutoSyncDelayDesc: "ドキュメントの変更後、自動同期をトリガーするまでの待機時間デフォルト5秒、最小2秒",
AutoSyncDelayText: "遅延秒数を入力",
@@ -78,6 +80,7 @@ export const ja = {
CheckConsole: "詳細情報を確認するには、コンソールを開いてください \n opt+cmd+i/ctrl+shift+i",
SettingsMigrated: "✨ プラグイン設定が更新されました!自動同期機能が追加されました。設定を確認してください",
AutoSyncNoNotionID: "⚠️ 自動同期をスキップ:このドキュメントは Notion に同期されていません。まず手動でアップロードしてください",
AutoSyncMissingDatabaseList: "⚠️ 自動同期をスキップfrontmatter に \"{key}\" 項目を追加してください",
AutoSyncMultipleSync: "🔄 自動同期:{count} 個のデータベースに同期中...",
AutoSyncFailed: "{database} への自動同期に失敗しました:{error}",
AutoSyncError: "{filename} の自動同期に失敗しました:{error}",

View File

@@ -37,6 +37,8 @@ export const zh = {
NotionLinkDisplayDesc: "默认开启如果你不想在front matter中显示链接请关闭",
AutoSync: "自动同步",
AutoSyncDesc: "当检测到文档的 frontmatter 或内容发生修改时,自动同步到 Notion需要文档已有 NotionID",
AutoSyncFrontmatterKey: "自动同步 Frontmatter 键名",
AutoSyncFrontmatterKeyDesc: "设置用于列出自动同步数据库的 frontmatter 属性名称(默认 autosync-database。",
AutoSyncDelay: "自动同步延迟时间(秒)",
AutoSyncDelayDesc: "文档修改后等待多少秒才触发自动同步避免频繁同步默认5秒最小2秒",
AutoSyncDelayText: "输入延迟秒数",
@@ -81,6 +83,7 @@ export const zh = {
CheckConsole: "opt+cmd+i/ctrl+shift+i\n打开控制台查看更多信息",
SettingsMigrated: "✨ 插件设置已更新!新增自动同步功能,请在设置中查看",
AutoSyncNoNotionID: "⚠️ 自动同步跳过:此文档未同步到 Notion请先手动上传",
AutoSyncMissingDatabaseList: "⚠️ 自动同步跳过:请在 frontmatter 中添加 \"{key}\" 项来指定数据库",
AutoSyncMultipleSync: "🔄 自动同步:正在同步到 {count} 个数据库...",
AutoSyncFailed: "自动同步到 {database} 失败:{error}",
AutoSyncError: "自动同步 {filename} 失败:{error}",

View File

@@ -4,6 +4,7 @@ import { i18nConfig } from "src/lang/I18n";
import ribbonCommands from "src/commands/NotionCommands";
import { ObsidianSettingTab, PluginSettings, DEFAULT_SETTINGS, DatabaseDetails } from "src/ui/settingTabs";
import { uploadCommandNext, uploadCommandGeneral, uploadCommandCustom } from "src/upload/uploadCommand";
import { DEFAULT_AUTO_SYNC_DATABASE_KEY, parseAutoSyncDatabaseList, resolveAutoSyncKey } from "src/utils/frontmatter";
// Remember to rename these classes and interfaces!
@@ -17,6 +18,7 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
private syncingFiles: Set<string> = new Set();
private lastFrontmatterCache: Map<string, any> = new Map();
private lastContentHashCache: Map<string, string> = new Map();
private missingAutoSyncNoticeShown: Set<string> = new Set();
async onload() {
await this.loadSettings();
@@ -67,6 +69,7 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
}
this.lastFrontmatterCache.clear();
this.lastContentHashCache.clear();
this.missingAutoSyncNoticeShown.clear();
}
async loadSettings() {
@@ -89,6 +92,10 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
if (typeof this.settings.NotionLinkDisplay !== 'boolean') {
this.settings.NotionLinkDisplay = DEFAULT_SETTINGS.NotionLinkDisplay;
}
if (typeof this.settings.autoSyncFrontmatterKey !== 'string') {
this.settings.autoSyncFrontmatterKey = DEFAULT_AUTO_SYNC_DATABASE_KEY;
}
this.settings.autoSyncFrontmatterKey = resolveAutoSyncKey(this.settings.autoSyncFrontmatterKey);
// Ensure databaseDetails exists
if (!this.settings.databaseDetails || typeof this.settings.databaseDetails !== 'object') {
@@ -99,7 +106,8 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
const needsSave = !loadedData ||
loadedData.autoSync === undefined ||
loadedData.autoSyncDelay === undefined ||
loadedData.NotionLinkDisplay === undefined;
loadedData.NotionLinkDisplay === undefined ||
loadedData.autoSyncFrontmatterKey === undefined;
if (needsSave) {
const migratedFields = [];
@@ -131,6 +139,7 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
autoSync: this.settings.autoSync,
autoSyncDelay: this.settings.autoSyncDelay,
NotionLinkDisplay: this.settings.NotionLinkDisplay,
autoSyncFrontmatterKey: this.settings.autoSyncFrontmatterKey,
databaseCount: Object.keys(this.settings.databaseDetails || {}).length
});
}
@@ -153,6 +162,20 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
console.warn('[Settings] Invalid databaseDetails, resetting to empty object');
this.settings.databaseDetails = {};
}
if (typeof this.settings.autoSyncFrontmatterKey !== 'string' || this.settings.autoSyncFrontmatterKey.trim().length === 0) {
console.warn('[Settings] Invalid autoSyncFrontmatterKey, resetting to default');
this.settings.autoSyncFrontmatterKey = DEFAULT_AUTO_SYNC_DATABASE_KEY;
} else {
this.settings.autoSyncFrontmatterKey = resolveAutoSyncKey(this.settings.autoSyncFrontmatterKey);
}
}
resetAutoSyncNoticeCache() {
this.missingAutoSyncNoticeShown.clear();
}
getAutoSyncFrontmatterKey(): string {
return resolveAutoSyncKey(this.settings.autoSyncFrontmatterKey);
}
async addDatabaseDetails(dbDetails: DatabaseDetails) {
@@ -327,21 +350,52 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
}
}
// Find all databases this file belongs to by checking for NotionID-{abName}
const foundDatabases: Array<{ dbDetails: DatabaseDetails, notionId: string }> = [];
const autoSyncKey = this.getAutoSyncFrontmatterKey();
const autoSyncTargets = parseAutoSyncDatabaseList(frontMatter[autoSyncKey]);
if (autoSyncTargets.length === 0) {
if (!this.missingAutoSyncNoticeShown.has(file.path)) {
const message = i18nConfig.AutoSyncMissingDatabaseList.replace('{key}', autoSyncKey);
new Notice(message, 10000);
this.missingAutoSyncNoticeShown.add(file.path);
}
console.log(`[AutoSync] No auto sync database list found in ${file.path} using key "${autoSyncKey}"`);
return;
}
this.missingAutoSyncNoticeShown.delete(file.path);
const dbByShortName = new Map<string, DatabaseDetails>();
for (const key in this.settings.databaseDetails) {
const dbDetails = this.settings.databaseDetails[key];
const notionIDKey = `NotionID-${dbDetails.abName}`;
dbByShortName.set(dbDetails.abName.toLowerCase(), dbDetails);
}
const foundDatabases: Array<{ dbDetails: DatabaseDetails, notionId: string }> = [];
const unresolvedTargets: string[] = [];
for (const target of autoSyncTargets) {
const lookupKey = target.toLowerCase();
const dbDetails = dbByShortName.get(lookupKey);
if (!dbDetails) {
unresolvedTargets.push(target);
continue;
}
const notionIDKey = `NotionID-${dbDetails.abName}`;
if (frontMatter[notionIDKey]) {
foundDatabases.push({
dbDetails: dbDetails,
dbDetails,
notionId: String(frontMatter[notionIDKey])
});
}
}
if (unresolvedTargets.length > 0) {
console.log(`[AutoSync] Frontmatter auto sync targets not found in settings: ${unresolvedTargets.join(", ")}`);
}
// If no NotionID found, notify user to upload manually first
if (foundDatabases.length === 0) {
console.log(`[AutoSync] No NotionID found in ${file.path}, skipping auto sync`);
@@ -403,4 +457,3 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
}
}
}

View File

@@ -5,6 +5,7 @@ import { SettingModal } from "./settingModal";
import { PreviewModal } from "./PreviewModal";
import { EditModal } from "./EditModal";
import { DeleteModal } from "./DeleteModal";
import { DEFAULT_AUTO_SYNC_DATABASE_KEY } from "src/utils/frontmatter";
export interface PluginSettings {
NextButton: boolean;
@@ -15,6 +16,7 @@ export interface PluginSettings {
NotionLinkDisplay: boolean;
autoSync: boolean;
autoSyncDelay: number;
autoSyncFrontmatterKey: string;
proxy: string;
GeneralButton: boolean;
tagButton: boolean;
@@ -53,6 +55,7 @@ export const DEFAULT_SETTINGS: PluginSettings = {
NotionLinkDisplay: true,
autoSync: false,
autoSyncDelay: 5,
autoSyncFrontmatterKey: DEFAULT_AUTO_SYNC_DATABASE_KEY,
proxy: "",
GeneralButton: true,
tagButton: true,
@@ -94,6 +97,20 @@ export class ObsidianSettingTab extends PluginSettingTab {
this.createSettingEl(containerEl, i18nConfig.AutoSync, i18nConfig.AutoSyncDesc, 'toggle', i18nConfig.AutoSync, this.plugin.settings.autoSync, 'autoSync')
new Setting(containerEl)
.setName(i18nConfig.AutoSyncFrontmatterKey)
.setDesc(i18nConfig.AutoSyncFrontmatterKeyDesc)
.addText((text) =>
text
.setPlaceholder(DEFAULT_AUTO_SYNC_DATABASE_KEY)
.setValue(this.plugin.settings.autoSyncFrontmatterKey ?? "")
.onChange(async (value) => {
this.plugin.settings.autoSyncFrontmatterKey = value;
await this.plugin.saveSettings();
this.plugin.resetAutoSyncNoticeCache();
})
);
// Auto Sync Delay setting - only visible when autoSync is enabled
this.autoSyncDelayContainer = containerEl.createDiv();
const delaySetting = new Setting(this.autoSyncDelayContainer)
@@ -350,4 +367,3 @@ export class ObsidianSettingTab extends PluginSettingTab {
}
}

View File

@@ -2,6 +2,7 @@ import { App, Notice, TFile } from "obsidian";
import ObsidianSyncNotionPlugin from "../main";
import { DatabaseDetails } from "../ui/settingTabs";
import { i18nConfig } from "src/lang/I18n";
import { ensureAutoSyncDatabaseEntry } from "src/utils/frontmatter";
export async function updateYamlInfo(
yamlContent: string,
@@ -17,6 +18,7 @@ export async function updateYamlInfo(
const { abName } = dbDetails
const notionIDKey = `NotionID-${abName}`;
const linkKey = `link-${abName}`;
const autoSyncKey = plugin.getAutoSyncFrontmatterKey();
if (notionUser !== "") {
// replace url str "www" to notionID
@@ -33,6 +35,12 @@ export async function updateYamlInfo(
// add new notionID and link
yamlContent[notionIDKey] = id;
(NotionLinkDisplay) ? yamlContent[linkKey] = url : null;
// ensure auto sync database list contains current short name
yamlContent[autoSyncKey] = ensureAutoSyncDatabaseEntry(
yamlContent[autoSyncKey],
abName
);
});
try {

63
src/utils/frontmatter.ts Normal file
View File

@@ -0,0 +1,63 @@
export const DEFAULT_AUTO_SYNC_DATABASE_KEY = "autosync-database";
export function resolveAutoSyncKey(rawKey: unknown): string {
if (typeof rawKey === "string") {
const trimmed = rawKey.trim();
if (trimmed.length > 0) {
return trimmed;
}
}
return DEFAULT_AUTO_SYNC_DATABASE_KEY;
}
function toCandidateList(value: unknown): string[] {
if (Array.isArray(value)) {
return value.map(item => String(item ?? "").trim());
}
if (typeof value === "string") {
if (value.includes(",")) {
return value.split(",").map(item => item.trim());
}
return [value.trim()];
}
return [];
}
export function parseAutoSyncDatabaseList(value: unknown): string[] {
const candidates = toCandidateList(value)
.map(name => name.replace(/^\[|\]$/g, "").trim()) // strip stray brackets
.filter(Boolean);
const seen = new Map<string, string>();
for (const name of candidates) {
const key = name.toLowerCase();
if (!seen.has(key)) {
seen.set(key, name);
}
}
return Array.from(seen.values());
}
export function ensureAutoSyncDatabaseEntry(value: unknown, abName: string): string[] {
const current = parseAutoSyncDatabaseList(value);
const lower = abName.toLowerCase();
let contains = false;
const updated = current.map(name => {
if (name.toLowerCase() === lower) {
contains = true;
return abName;
}
return name;
});
if (!contains) {
updated.push(abName);
}
return updated;
}