obsidian

obsidian

插件:Copy document as HTML

复制的图片是base64

下载node.js
https://nodejs.org/zh-cn/download/

在终端输入 node -v

bat 复制代码
C:\Users\MI>node -v
v24.15.0
ts 复制代码
git clone https://github.com/obsidianmd/obsidian-sample-plugin.git my-note-plugin

cd my-note-plugin
npm install

项目根目录下的 manifest.json,修改 id、name 等基本信息

ts 复制代码
{
    "id": "my-note-plugin",
    "name": "My Note Plugin",
    "author": "Your Name",
    "version": "1.0.0",
    "minAppVersion": "0.15.0",
    "isDesktopOnly": true
}

在插件项目目录下运行:

xml 复制代码
npm run dev

main.ts:

ts 复制代码
import { App, Modal, Notice, Plugin, PluginSettingTab, Setting, requestUrl, TFile } from 'obsidian';

// --- 定义插件的设置项结构---
interface MyPluginSettings {
    apiBaseUrl: string;
}

// --- 定义从后端API获取的数据结构---
interface NoteItem {
    id: number;
    title: string;
}

// --- 插件主类,必须继承自Plugin---
export default class MyNotePlugin extends Plugin {
    settings: MyPluginSettings;

    async onload() {
        await this.loadSettings();

        // 添加一个设置页面
        this.addSettingTab(new MySettingTab(this.app, this));

        // 添加一个命令,这是插件的入口
        this.addCommand({
            id: 'fetch-and-edit-note',
            name: '从后端拉取笔记并编辑',
            callback: () => this.fetchAndEditNote()
        });
    }

    // 主要业务逻辑:拉取笔记列表,选择后在临时文件编辑,保存时自动回写
    async fetchAndEditNote() {
        // 1. 从后端获取所有笔记的ID和标题列表
        let noteList: NoteItem[];
        try {
            const response = await requestUrl({
                url: `${this.settings.apiBaseUrl}/note/list`,
                method: 'GET',
            });
            noteList = response.json;
            if (noteList.length === 0) {
                new Notice("没有从后端获取到任何笔记");
                return;
            }
        } catch (error) {
            console.error(error);
            new Notice("获取笔记列表失败,请检查API地址是否正确");
            return;
        }

        // 2. 弹出一个模态框让用户选择要编辑的笔记
        const selectedId = await this.showNoteSelectionModal(noteList);
        if (selectedId === null) return;

        // 3. 根据用户选择的ID,拉取具体的Markdown内容
        let markdownContent: string;
        try {
            const response = await requestUrl({
                url: `${this.settings.apiBaseUrl}/note/${selectedId}/study`,
                method: 'GET',
            });
            markdownContent = response.json.markdown || "";
        } catch (error) {
            console.error(error);
            new Notice("获取笔记内容失败");
            return;
        }

        // 4. 在Obsidian Vault中创建一个临时文件,并将拉取的内容写入
        const tempFileName = `temp_edit_note_${selectedId}.md`;
        let tempFile: TFile;
        try {
            tempFile = await this.app.vault.create(tempFileName, markdownContent);
        } catch (error) {
            console.error(error);
            new Notice("创建临时文件失败");
            return;
        }

        // 5. 在编辑器中打开这个临时文件,让用户编辑
        await this.app.workspace.openLinkText(tempFileName, '', false);

        // 6. 监听文件修改事件,当临时文件被保存时,将内容回传给后端
        const saveHandler = this.registerEvent(
            this.app.vault.on('modify', async (file) => {
                if (file.path !== tempFileName) return;
                if (!(file instanceof TFile)) return;

                const updatedContent = await this.app.vault.read(file);
                try {
                    await requestUrl({
                        url: `${this.settings.apiBaseUrl}/note/${selectedId}/study`,
                        method: 'PUT',
                        contentType: 'application/json',
                        body: JSON.stringify({ markdown: updatedContent }),
                    });
                    new Notice(`笔记 "${noteList.find(n => n.id === selectedId)?.title}" 已成功回写至数据库`);
                    // 回写成功后,关闭并删除临时文件
                    await this.app.workspace.getLeaf().setViewState({ type: "empty" });
                    await this.app.vault.delete(file);
                } catch (error) {
                    console.error(error);
                    new Notice("保存笔记到后端失败,请检查API连接");
                }
            })
        );
    }

    // 显示选择笔记的模态框,返回用户选择的笔记ID
    showNoteSelectionModal(notes: NoteItem[]): Promise<number | null> {
        return new Promise((resolve) => {
            const modal = new NoteSelectionModal(this.app, notes, (id) => resolve(id));
            modal.open();
        });
    }

    // 加载插件设置(从data.json)
    async loadSettings() {
        this.settings = Object.assign({ apiBaseUrl: 'http://localhost:8080/api' }, await this.loadData());
    }

    // 保存插件设置到data.json
    async saveSettings() {
        await this.saveData(this.settings);
    }
}

// --- 选择笔记的模态框实现(使用FuzzySuggestModal)---
class NoteSelectionModal extends Modal {
    notes: NoteItem[];
    onSubmit: (id: number) => void;

    constructor(app: App, notes: NoteItem[], onSubmit: (id: number) => void) {
        super(app);
        this.notes = notes;
        this.onSubmit = onSubmit;
    }

    onOpen() {
        const { contentEl } = this;
        contentEl.createEl("h2", { text: "选择要编辑的笔记" });
        const listEl = contentEl.createEl("ul");
        this.notes.forEach(note => {
            const li = listEl.createEl("li");
            li.createEl("button", { text: `${note.id}. ${note.title}` }).onclick = () => {
                this.close();
                this.onSubmit(note.id);
            };
        });
    }

    onClose() {
        const { contentEl } = this;
        contentEl.empty();
    }
}

// --- 插件设置页面,让用户配置后端API地址---
class MySettingTab extends PluginSettingTab {
    plugin: MyNotePlugin;

    constructor(app: App, plugin: MyNotePlugin) {
        super(app, plugin);
        this.plugin = plugin;
    }

    display(): void {
        const { containerEl } = this;
        containerEl.empty();

        new Setting(containerEl)
            .setName('后端API基础地址')
            .setDesc('你的Java Spring Boot API基础URL,例如 http://localhost:8080/api')
            .addText(text => text
                .setPlaceholder('http://localhost:8080/api')
                .setValue(this.plugin.settings.apiBaseUrl)
                .onChange(async (value) => {
                    this.plugin.settings.apiBaseUrl = value;
                    await this.plugin.saveSettings();
                }));
    }
}

在插件项目根目录下运行

xml 复制代码
npm run build

文件目录:

xml 复制代码
你的笔记仓库根目录/
├── .obsidian/
│   ├── plugins/          # 插件文件夹
│   │   ├── my-note-plugin/  # 你的插件专属文件夹
│   │   │   ├── main.js
│   │   │   ├── manifest.json
│   │   │   └── styles.css (如果有)
│   │   └── ...
│   └── ...
相关推荐
哆哆啦001 天前
使用 Obsidian + GitHub Actions + GitHub Pages 搭建内容发布流
数据库·笔记·github·obsidian
哆哆啦004 天前
obsidian远程同步方案:infiniCloud+remotely save方案
笔记·git·obsidian
码途漫谈4 天前
把笔记变成可生长的知识系统:Obsidian 技术介绍
笔记·ai·obsidian
撞强5 天前
多端obsidian同步配置(手机+WIN10+Docker)
知识·obsidian·webdav·多平台·多端
林小卫很行10 天前
Obsidian 入门40:把我的写作工作流Skill免费分享给你
人工智能·经验分享·ai写作·obsidian
林小卫很行11 天前
Obsidian 入门42:官方出品的剪藏插件,公众号文章也能一键存进你的库
知识图谱·知识管理·obsidian
胖墩会武术16 天前
Obsidian 与 Obsidian Skills 小白入门
人工智能·ai·obsidian·obsidian skills
BNTang20 天前
Obsidian CLI 来了
obsidian
安逸sgr21 天前
Hermes Agent + Obsidian 打造第二大脑(三):Docker 部署详解——从零到生产环境的完整实战指南!
运维·docker·容器·obsidian·hermes·hermesagent