Compare commits

..

12 Commits
0.0.2 ... 0.1.0

Author SHA1 Message Date
Jiaxin Peng
024b7f726e Update version to 0.1.0 2023-08-29 13:39:52 +01:00
Jiaxin Peng
722ca7bb56 update packages and modify upload2notion 2023-08-29 13:39:36 +01:00
Jiaxin Peng
fb7294d607 Update version to 0.0.9 2023-08-11 23:11:24 +01:00
Jiaxin Peng
4f06d81588 change colour and size for the icon 2023-08-11 23:11:02 +01:00
Jiaxin Peng
2434ddd532 fix the notification bug 2023-08-10 23:20:26 +01:00
Jiaxin Peng
55804e491a modify the cover image url setting 2023-08-10 22:50:48 +01:00
Jiaxin Peng
679e7ec4f1 add support for cover image and title icon 2023-08-10 22:04:57 +01:00
Jiaxin Peng
9be679f456 amend the template 2023-08-10 09:44:06 +01:00
Jiaxin Peng
a0f5d2d51b update the notion version 2023-08-10 09:30:27 +01:00
Jiaxin Peng
a68a3920ad minor update 2023-08-10 09:26:50 +01:00
Jiaxin Peng
76d3fc9872 Update version to 0.0.3 2023-08-10 09:15:26 +01:00
Jiaxin Peng
e3fb50b205 Update version to 0.0.2 2023-08-10 09:07:22 +01:00
17 changed files with 843 additions and 731 deletions

View File

@@ -6,7 +6,7 @@ on:
- "*" - "*"
env: env:
PLUGIN_NAME: obsidian-to-notion PLUGIN_NAME: share-to-notionnext
jobs: jobs:
build: build:

View File

@@ -6,7 +6,7 @@ on:
- "*" - "*"
env: env:
PLUGIN_NAME: obsidian-to-notion # Change this to match the id of your plugin. PLUGIN_NAME: share-to-notionnext # Change this to match the id of your plugin.
jobs: jobs:
build: build:
@@ -34,7 +34,7 @@ jobs:
id: create_release id: create_release
uses: actions/create-release@v1 uses: actions/create-release@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
VERSION: ${{ github.ref }} VERSION: ${{ github.ref }}
with: with:
tag_name: ${{ github.ref }} tag_name: ${{ github.ref }}
@@ -46,7 +46,7 @@ jobs:
id: upload-zip id: upload-zip
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./${{ env.PLUGIN_NAME }}.zip asset_path: ./${{ env.PLUGIN_NAME }}.zip
@@ -57,7 +57,7 @@ jobs:
id: upload-main id: upload-main
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./main.js asset_path: ./main.js
@@ -68,18 +68,29 @@ jobs:
id: upload-manifest id: upload-manifest
uses: actions/upload-release-asset@v1 uses: actions/upload-release-asset@v1
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
with: with:
upload_url: ${{ steps.create_release.outputs.upload_url }} upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./manifest.json asset_path: ./manifest.json
asset_name: manifest.json asset_name: manifest.json
asset_content_type: application/json asset_content_type: application/json
- name: Upload markdown template
id: upload-md
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./obsidian_template.md
asset_name: template.md
asset_content_type: text/markdown
# - name: Upload styles.css # - name: Upload styles.css
# id: upload-css # id: upload-css
# uses: actions/upload-release-asset@v1 # uses: actions/upload-release-asset@v1
# env: # env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
# with: # with:
# upload_url: ${{ steps.create_release.outputs.upload_url }} # upload_url: ${{ steps.create_release.outputs.upload_url }}
# asset_path: ./styles.css # asset_path: ./styles.css

View File

@@ -1,4 +1,4 @@
# Obsidian to NotionNext # Obsidian share to NotionNext
感谢[原作者](https://github.com/EasyChris/obsidian-to-notion)开发出了这么好用的插件能够将obsidian同步到notion。 但是原仓库只能同步Name和Tags信息如果像我一样通过[NotionNext](https://github.com/tangly1024/NotionNext)来搭建自己的网页,就有一些局限性。每次导入之后都需要进行大量的修改。 感谢[原作者](https://github.com/EasyChris/obsidian-to-notion)开发出了这么好用的插件能够将obsidian同步到notion。 但是原仓库只能同步Name和Tags信息如果像我一样通过[NotionNext](https://github.com/tangly1024/NotionNext)来搭建自己的网页,就有一些局限性。每次导入之后都需要进行大量的修改。
所以我在[原作者](https://github.com/EasyChris/obsidian-to-notion)的基础之上,增加了匹配[NotionNext](https://github.com/tangly1024/NotionNext)模板的功能。这样可以直接在Obsidian编辑整理好之后一键发布。 所以我在[原作者](https://github.com/EasyChris/obsidian-to-notion)的基础之上,增加了匹配[NotionNext](https://github.com/tangly1024/NotionNext)模板的功能。这样可以直接在Obsidian编辑整理好之后一键发布。
@@ -11,6 +11,7 @@
### 准备安装 ### 准备安装
在安装插件之前,你必须配置好了以下内容: 在安装插件之前,你必须配置好了以下内容:
1. 你的NotionNext数据库。 1. 你的NotionNext数据库。
2. 根据原作者的readme.md配置好了Notion API并且已经和你的NotionNext仓库关联。 2. 根据原作者的readme.md配置好了Notion API并且已经和你的NotionNext仓库关联。
3. NotionNext Database ID 3. NotionNext Database ID
@@ -25,7 +26,14 @@
- summary - summary
- password - password
- icon - icon
**如果你是直接复制的NotionNext的模板这些内容应该已经有了。我在原作者的基础之上对本插件的内容进行了更改所以你只需要保证你的数据库有如上内容并且所有的字母都是小写**
在最新的0.0.6版本,我还增加了以下内容:
- titleicon 标题的emoji图标,暂时只能支持emoji
- coverurl 封面图片的url url可以是任何一种图片的url 例如jpgpngsvg等等。
**如果你是直接复制的NotionNext的模板这些内容应该已经有了。我在原作者的基础之上对本插件的内容进行了更改所以你只需要保证你的数据库有如上内容并且所有的字母都是小写**
**⚠️⚠️⚠️:表头全部小写!!!顺序无所谓!** **⚠️⚠️⚠️:表头全部小写!!!顺序无所谓!**
### 安装插件 ### 安装插件
@@ -37,24 +45,36 @@
### 使用插件 ### 使用插件
在仓库中我上传了一个模板你可以直接复制到你的模板文件夹中。然后使用Obsidian的模板功能一键生成新笔记。 在仓库中我上传了一个模板你可以直接复制到你的模板文件夹中。然后使用Obsidian的模板功能一键生成新笔记。
如果你不想使用模板你也可以直接在Obsidian中创建一个新的文件然后复制下边的内容。然后保存。 如果你不想使用模板你也可以直接在Obsidian中创建一个新的文件然后复制下边的内容。然后保存。
```markdown ```markdown
--- ---
type: Post # Post or Page, the default is Post # default value has been set.
slug: test # slug for url, the default is # for any unwanted value, you can delete it or set it to empty.
stats: Draft # Draft, Invisible, Published # for example, if you donot want to set password, you can delete password: "1234" or set it to password: ""
category: test # 我已经在插件中设置了默认值,如果有不需要的选项,可以直接删除。
summary: this is a summary for test post # 例如你不需要密码选项你可以将password: "1234"删除,或者将它设置为空。
icon: fa-solid fa-camera # you can ignore this # !!!!!!!!!!!!
password: "1234" # if you donot want to set password, you can delete this line # 现在阶段一定不要修改表头的名字, please do not change the name of the header in YAML front matter
# !!!!!!!!!!!!
titleicon: 📎 # emoji icon, default is 📜, 默认是📜
coverurl: https://img.jxpeng.dev/2023/08/843e27a210847f05a0f7cfb121fec100.jpg # default is empty 默认是空
type: Post # Post or Page, default is Post 默认是Post
slug: test # slug for url, default is empty 默认是空
stats: Draft # Draft, Invisible, Published, default is Draft 默认是Draft
category: test # default is 'Obsidian' 默认是'Obsidian'
summary: this is a summary for test post # default is empty 默认是空
icon: fa-solid fa-camera # you can ignore this, default is empty 默认是空,可直接删除
password: "1234" # if you donot want to set password, you can delete this line, default is empty 默认是空,可直接删除
# 现在必须开启tags选项否则会报错
tags: tags:
- test # tags for post - test # tags for post
- web # add more tags if you want - web # add more tags if you want
--- ---
Contents Below Contents Below
``` ```
模板使用如下: 模板使用如下:
![](https://img.jxpeng.dev/2023/08/5ba5e7aeb86650c060c620786371717c.mp4) ![](https://img.jxpeng.dev/2023/08/5ba5e7aeb86650c060c620786371717c.mp4)
@@ -64,7 +84,9 @@ Contents Below
--- ---
**以下为原README.md** **以下为原README.md**
非常感谢原作者的开发,我只是在原作者的基础之上进行了修改,增加了一些功能。如果你觉得这个插件对你有帮助,可以给[原作者](https://github.com/EasyChris/obsidian-to-notion)一个star。 非常感谢原作者的开发,我只是在原作者的基础之上进行了修改,增加了一些功能。如果你觉得这个插件对你有帮助,可以给[原作者](https://github.com/EasyChris/obsidian-to-notion)一个star。
# Obsidian to Notion # Obsidian to Notion

View File

@@ -1,5 +1,6 @@
# Obsidian to NotionNext # Obsidian to NotionNext
[中文文档](README-zh.md) [中文文档](README-zh.md)
Thanks to the [original author](https://github.com/EasyChris/obsidian-to-notion) for developing such a useful plugin that can synchronize Obsidian to Notion. However, the original repository can only sync Name and Tags information. For those like me who use [NotionNext](https://github.com/tangly1024/NotionNext) to set up their website, this presents some limitations. Every time I import, I need to make a lot of modifications. Thanks to the [original author](https://github.com/EasyChris/obsidian-to-notion) for developing such a useful plugin that can synchronize Obsidian to Notion. However, the original repository can only sync Name and Tags information. For those like me who use [NotionNext](https://github.com/tangly1024/NotionNext) to set up their website, this presents some limitations. Every time I import, I need to make a lot of modifications.
Thus, based on the [original author's work](https://github.com/EasyChris/obsidian-to-notion), I've added a feature to match the [NotionNext](https://github.com/tangly1024/NotionNext) template. This way, you can edit directly in Obsidian and publish with a single click after organizing. Thus, based on the [original author's work](https://github.com/EasyChris/obsidian-to-notion), I've added a feature to match the [NotionNext](https://github.com/tangly1024/NotionNext) template. This way, you can edit directly in Obsidian and publish with a single click after organizing.
@@ -26,8 +27,14 @@ Before installing the plugin, you must have set up the following:
- summary - summary
- password - password
- icon - icon
**If you've directly copied the NotionNext template, these contents should already exist. I've made changes to this plugin based on the original author's work. All you need to ensure is that your database has the above content, and every letter is in lowercase!!!**
**⚠️⚠️⚠️: All headers are in lowercase!!! The order doesn't matter!** In version 0.0.6, I also add the following contents:
- titleicon: the unique icon for each post, it can only support emoji currently.
- cover url: the cover image for each post, it should be end with a type of image, such as .jpg, .png, .gif, etc.
**If you've directly copied the NotionNext template, these contents should already exist. I've made changes to this plugin based on the original author's work. All you need to ensure is that your database has the above content, and every letter is in lowercase!!!**
**⚠️⚠️⚠️: All headers are in lowercase!!! The order doesn't matter!**
### Plugin Installation ### Plugin Installation
1. Close Obsidian. 1. Close Obsidian.
@@ -42,19 +49,31 @@ In the repository, I have uploaded a template which you can directly copy into y
If you don't want to use the template, you can also directly create a new file in Obsidian and then copy the content below. Then save it. If you don't want to use the template, you can also directly create a new file in Obsidian and then copy the content below. Then save it.
```markdown ```markdown
--- ---
type: Post # Post or Page, the default is Post # default value has been set.
slug: test # slug for url, the default is # for any unwanted value, you can delete it or set it to empty.
stats: Draft # Draft, Invisible, Published # for example, if you donot want to set password, you can delete password: "1234" or set it to password: ""
category: test # 我已经在插件中设置了默认值,如果有不需要的选项,可以直接删除。
summary: this is a summary for test post # 例如你不需要密码选项你可以将password: "1234"删除,或者将它设置为空。
icon: fa-solid fa-camera # you can ignore this # !!!!!!!!!!!!
password: "1234" # if you donot want to set password, you can delete this line # 现在阶段一定不要修改表头的名字, please do not change the name of the header in YAML front matter
# !!!!!!!!!!!!
titleicon: 📎 # emoji icon, default is 📜, 默认是📜
coverurl: https://img.jxpeng.dev/2023/08/843e27a210847f05a0f7cfb121fec100.jpg # default is empty 默认是空
type: Post # Post or Page, default is Post 默认是Post
slug: test # slug for url, default is empty 默认是空
stats: Draft # Draft, Invisible, Published, default is Draft 默认是Draft
category: test # default is 'Obsidian' 默认是'Obsidian'
summary: this is a summary for test post # default is empty 默认是空
icon: fa-solid fa-camera # you can ignore this, default is empty 默认是空,可直接删除
password: "1234" # if you donot want to set password, you can delete this line, default is empty 默认是空,可直接删除
# 现在必须开启tags选项否则会报错
tags: tags:
- test # tags for post - test # tags for post
- web # add more tags if you want - web # add more tags if you want
--- ---
Contents Below Contents Below
``` ```
Usage of the template is as follows: Usage of the template is as follows:
![](https://img.jxpeng.dev/2023/08/189384bf03c562030f4828343e1b7a6c.gif) ![](https://img.jxpeng.dev/2023/08/189384bf03c562030f4828343e1b7a6c.gif)
@@ -63,7 +82,9 @@ Usage of the template is as follows:
![](https://img.jxpeng.dev/2023/08/6063dd06eea4ee6939b5058dd129a698.gif) ![](https://img.jxpeng.dev/2023/08/6063dd06eea4ee6939b5058dd129a698.gif)
--- ---
**Original README.md** **Original README.md**
Many Thanks for the original author's work. I've only made some changes to the original author's work. If you find this plugin useful, please give the [original author](https://github.com/EasyChris/obsidian-to-notion) a star. Many Thanks for the original author's work. I've only made some changes to the original author's work. If you find this plugin useful, please give the [original author](https://github.com/EasyChris/obsidian-to-notion) a star.
# Obsidian to Notion # Obsidian to Notion

View File

@@ -1,195 +0,0 @@
import { Notice, requestUrl,TFile,normalizePath, App } from "obsidian";
import { Client } from "@notionhq/client";
import { markdownToBlocks, } from "@tryfabric/martian";
import * as yamlFrontMatter from "yaml-front-matter";
import * as yaml from "yaml"
import MyPlugin from "main";
export class Upload2Notion {
app: MyPlugin;
notion: Client;
agent: any;
constructor(app: MyPlugin) {
this.app = app;
}
async deletePage(notionID:string){
const response = await requestUrl({
url: `https://api.notion.com/v1/blocks/${notionID}`,
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.app.settings.notionAPI,
'Notion-Version': '2022-02-22',
},
body: ''
})
return response;
}
// 因为需要解析notion的block进行对比非常的麻烦
// 暂时就直接删除新建一个page
async updatePage(notionID:string, title:string, allowTags:boolean, tags:string[], type:string, slug:string, stats:string, category:string, summary:string, paword:string, favicon:string, datetime:string, childArr:any) {
await this.deletePage(notionID)
const res = await this.createPage(title, allowTags, tags, type, slug, stats, category, summary, paword, favicon, datetime, childArr)
return res
}
async createPage(title:string, allowTags:boolean, tags:string[], type:string, slug:string, stats:string, category:string, summary:string, pawrod:string, favicon:string, datetime:string, childArr: any) {
const bodyString:any = {
parent: {
database_id: this.app.settings.databaseID
},
properties: {
title: {
title: [
{
text: {
content: title
},
},
],
},
tags: {
multi_select: allowTags && tags !== undefined ? tags.map(tag => {
return {"name": tag}
}) : [],
},
type: {
select: {
name: type || 'Post'
}
},
slug: {
rich_text: [
{
text: {
content: slug || 'obsidian'
}
}
]
},
status: {
select: {
name: stats || 'Draft'
}
},
category: {
select: {
name: category || 'Obsidian'
}
},
summary: {
rich_text: [
{
text: {
content: summary || ''
}
}
]
},
password: {
rich_text: [
{
text: {
content: pawrod || ''
}
}
]
},
icon: {
rich_text: [
{
text: {
content: favicon || ''
}
}
]
},
date: {
date: {
start: datetime || new Date().toISOString()
}
}
},
children: childArr,
}
if(this.app.settings.bannerUrl) {
bodyString.cover = {
type: "external",
external: {
url: this.app.settings.bannerUrl
}
}
}
try {
const response = await requestUrl({
url: `https://api.notion.com/v1/pages`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'User-Agent': 'obsidian.md',
'Authorization': 'Bearer ' + this.app.settings.notionAPI,
'Notion-Version': '2022-06-28',
},
body: JSON.stringify(bodyString),
})
return response;
} catch (error) {
new Notice(`network error ${error}`)
}
}
async syncMarkdownToNotion(title:string, allowTags:boolean, tags:string[], type:string, slug:string, stats:string, category:string, summary:string, paword:string, favicon:string, datetime:string, markdown: string, nowFile: TFile, app:App, settings:any): Promise<any> {
let res:any
const yamlObj:any = yamlFrontMatter.loadFront(markdown);
const __content = yamlObj.__content
const file2Block = markdownToBlocks(__content);
const frontmasster =await app.metadataCache.getFileCache(nowFile)?.frontmatter
const notionID = frontmasster ? frontmasster.notionID : null
if(notionID){
res = await this.updatePage(notionID, title, allowTags, tags, type, slug, stats, category, summary, paword, favicon, datetime, file2Block);
} else {
res = await this.createPage(title, allowTags, tags, type, slug, stats, category, summary, paword, favicon, datetime, file2Block);
}
if (res.status === 200) {
await this.updateYamlInfo(markdown, nowFile, res, app, settings)
} else {
new Notice(`${res.text}`)
}
return res
}
async updateYamlInfo(yamlContent: string, nowFile: TFile, res: any,app:App, settings:any) {
const yamlObj:any = yamlFrontMatter.loadFront(yamlContent);
let {url, id} = res.json
// replace www to notionID
const {notionID} = settings;
if(notionID!=="") {
// replace url str "www" to notionID
url = url.replace("www.notion.so", `${notionID}.notion.site`)
}
yamlObj.link = url;
try {
await navigator.clipboard.writeText(url)
} catch (error) {
new Notice(`复制链接失败,请手动复制${error}`)
}
yamlObj.notionID = id;
const __content = yamlObj.__content;
delete yamlObj.__content
const yamlhead = yaml.stringify(yamlObj)
// if yamlhead hava last \n remove it
const yamlhead_remove_n = yamlhead.replace(/\n$/, '')
// if __content have start \n remove it
const __content_remove_n = __content.replace(/^\n/, '')
const content = '---\n' +yamlhead_remove_n +'\n---\n' + __content_remove_n;
try {
await nowFile.vault.modify(nowFile, content)
} catch (error) {
new Notice(`write file error ${error}`)
}
}
}

View File

@@ -11,11 +11,11 @@ if you want to view the source, please visit the github repository of this plugi
const prod = (process.argv[2] === 'production'); const prod = (process.argv[2] === 'production');
esbuild.build({ const ctx = await esbuild.context({
banner: { banner: {
js: banner, js: banner,
}, },
entryPoints: ['main.ts'], entryPoints: ['src/main.ts'],
bundle: true, bundle: true,
external: [ external: [
'obsidian', 'obsidian',
@@ -43,10 +43,14 @@ esbuild.build({
'@codemirror/view', '@codemirror/view',
...builtins], ...builtins],
format: 'cjs', format: 'cjs',
watch: !prod,
target: 'es2016', target: 'es2016',
logLevel: "info", logLevel: "info",
sourcemap: prod ? false : 'inline', sourcemap: prod ? false : 'inline',
treeShaking: true, treeShaking: true,
outfile: 'main.js', outfile: 'main.js',
}).catch(() => process.exit(1)); })
await ctx.watch();
// await ctx.dispose();

View File

@@ -1,10 +1,10 @@
{ {
"id": "share-to-notionnext", "id": "share-to-notionnext",
"name": "Share markdown to NotionNext", "name": "Share to NotionNext",
"version": "0.0.2", "version": "0.1.0",
"minAppVersion": "0.0.1", "minAppVersion": "0.0.1",
"description": "This is a plugin for Obsidian. This plugin share obsidian md file to notion with notion api for NotionNext web deploy.", "description": "Shares obsidian md file to notion with notion api for NotionNext web deploy, motivated by EasyChris/obsidian-to-notion.",
"author": "jxpeng98", "author": "EasyChris, jxpeng98",
"authorUrl": "https://github.com/jxpeng98/obsidian-to-NotionNext", "authorUrl": "https://github.com/jxpeng98/obsidian-to-NotionNext",
"isDesktopOnly": false "isDesktopOnly": false
} }

View File

@@ -1,15 +0,0 @@
---
type: Post // Post or Page
slug: test // slug for url
stats: Draft // Draft, Invisible, Published
category: test
summary: this is a summary for test post
icon: fa-solid fa-camera // you can ignore this
password: "1234" // if you donot want to set password, you can delete this line
tags:
- test // tags for post
- web // add more tags if you want
---
Contents Below

25
obsidian_template.md Normal file
View File

@@ -0,0 +1,25 @@
---
# default value has been set.
# for any unwanted value, you can delete it or set it to empty.
# for example, if you donot want to set password, you can delete password: "1234" or set it to password: ""
# 我已经在插件中设置了默认值,如果有不需要的选项,可以直接删除。
# 例如你不需要密码选项你可以将password: "1234"删除,或者将它设置为空。
# !!!!!!!!!!!!
# 现在阶段一定不要修改表头的名字, please do not change the name of the header in YAML front matter
# !!!!!!!!!!!!
titleicon: 📎 # default is 📜, 默认是📜
coverurl: https://img.jxpeng.dev/2023/08/843e27a210847f05a0f7cfb121fec100.jpg # default is empty 默认是空
type: Post # Post or Page, default is Post 默认是Post
slug: test # slug for url, default is empty 默认是空
stats: Draft # Draft, Invisible, Published, default is Draft 默认是Draft
category: test # default is 'Obsidian' 默认是'Obsidian'
summary: this is a summary for test post # default is empty 默认是空
icon: fa-solid fa-camera # you can ignore this, default is empty 默认是空,可直接删除
password: "1234" # if you donot want to set password, you can delete this line, default is empty 默认是空,可直接删除
# 现在必须开启tags选项否则会报错
tags:
- test # tags for post
- web # add more tags if you want
---
Contents Below

View File

@@ -1,8 +1,8 @@
{ {
"name": "share-to-notionnext", "name": "share-to-notionnext",
"version": "0.0.2", "version": "0.1.0",
"type": "module", "type": "module",
"description": "This is a plugin for Obsidian. This plugin share obsidian md file to notion with notion api for NotionNext web deploy.", "description": "Shares obsidian md file to notion with notion api for NotionNext web deploy, motivated by EasyChris/obsidian-to-notion.",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"dev": "node esbuild.config.mjs", "dev": "node esbuild.config.mjs",
@@ -13,19 +13,20 @@
"author": "", "author": "",
"license": "GNU GPLv3", "license": "GNU GPLv3",
"devDependencies": { "devDependencies": {
"@types/node": "^17.0.35", "@types/node": "^20.5.7",
"@types/yaml-front-matter": "^4.1.0", "@types/yaml-front-matter": "^4.1.0",
"@typescript-eslint/eslint-plugin": "^5.2.0", "@typescript-eslint/eslint-plugin": "^6.5.0",
"@typescript-eslint/parser": "^5.2.0", "@typescript-eslint/parser": "^6.5.0",
"builtin-modules": "^3.2.0", "builtin-modules": "^3.2.0",
"esbuild": "0.13.12", "esbuild": "0.19.2",
"obsidian": "latest", "obsidian": "latest",
"tslib": "2.3.1", "tslib": "2.6.2",
"typescript": "4.4.4" "typescript": "5.2.2"
}, },
"dependencies": { "dependencies": {
"@tryfabric/martian": "^1.2.0", "@tryfabric/martian": "^1.2.0",
"https-proxy-agent": "^5.0.1", "https-proxy-agent": "^7.0.1",
"process": "^0.11.10",
"yaml": "^2.2.2", "yaml": "^2.2.2",
"yaml-front-matter": "^4.1.1" "yaml-front-matter": "^4.1.1"
} }

239
src/Upload2Notion.ts Normal file
View File

@@ -0,0 +1,239 @@
import { App, Notice, requestUrl, TFile } from "obsidian";
import { Client } from '@notionhq/client';
import { markdownToBlocks, } from "@tryfabric/martian";
import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main";
export class Upload2Notion {
plugin: MyPlugin;
notion: Client;
agent: any;
constructor(plugin: MyPlugin) {
this.plugin = plugin;
}
async deletePage(notionID: string) {
return requestUrl({
url: `https://api.notion.com/v1/blocks/${notionID}`,
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.plugin.settings.notionAPI,
'Notion-Version': '2022-06-28',
},
body: ''
});
}
async getDataBase(databaseID: string) {
const response = await requestUrl({
url: `https://api.notion.com/v1/databases/${databaseID}`,
method: 'GET',
headers: {
'Authorization': 'Bearer ' + this.plugin.settings.notionAPI,
'Notion-Version': '2022-06-28',
}
}
)
// Check if cover is present in the JSON response and then get the URL
if (response.json.cover && response.json.cover.external) {
return response.json.cover.external.url;
} else {
return null; // or some other default value, if you prefer
}
}
// 因为需要解析notion的block进行对比非常的麻烦
// 暂时就直接删除新建一个page
async updatePage(notionID: string, title: string, allowTags: boolean, emoji: string, cover: string, tags: string[], type: string, slug: string, stats: string, category: string, summary: string, paword: string, favicon: string, datetime: string, childArr: any) {
await this.deletePage(notionID)
const databasecover = await this.getDataBase(this.plugin.settings.databaseID)
if (cover == null) {
cover = databasecover
}
return await this.createPage(title, allowTags, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, childArr)
}
async createPage(title: string, allowTags: boolean, emoji: string, cover: string, tags: string[], type: string, slug: string, stats: string, category: string, summary: string, pawrod: string, favicon: string, datetime: string, childArr: any) {
const bodyString: any = {
parent: {
database_id: this.plugin.settings.databaseID
},
icon: {
emoji: emoji || '📜'
},
properties: {
title: {
title: [
{
text: {
content: title
},
},
],
},
tags: {
multi_select: allowTags && tags !== undefined ? tags.map(tag => {
return { "name": tag }
}) : [],
},
type: {
select: {
name: type || 'Post'
}
},
slug: {
rich_text: [
{
text: {
content: slug || ''
}
}
]
},
status: {
select: {
name: stats || 'Draft'
}
},
category: {
select: {
name: category || 'Obsidian'
}
},
summary: {
rich_text: [
{
text: {
content: summary || ''
}
}
]
},
password: {
rich_text: [
{
text: {
content: pawrod || ''
}
}
]
},
icon: {
rich_text: [
{
text: {
content: favicon || ''
}
}
]
},
date: {
date: {
start: datetime || new Date().toISOString()
}
}
},
children: childArr,
}
if (cover) {
bodyString.cover = {
type: "external",
external: {
url: cover
}
}
}
if (!bodyString.cover && this.plugin.settings.bannerUrl) {
bodyString.cover = {
type: "external",
external: {
url: this.plugin.settings.bannerUrl
}
}
}
try {
return await requestUrl({
url: `https://api.notion.com/v1/pages`,
method: 'POST',
headers: {
'Content-Type': 'application/json',
// 'User-Agent': 'obsidian.md',
'Authorization': 'Bearer ' + this.plugin.settings.notionAPI,
'Notion-Version': '2022-06-28',
},
body: JSON.stringify(bodyString),
})
} catch (error) {
new Notice(`network error ${error}`)
}
}
async syncMarkdownToNotion(title: string, allowTags: boolean, emoji: string, cover: string, tags: string[], type: string, slug: string, stats: string, category: string, summary: string, paword: string, favicon: string, datetime: string, markdown: string, nowFile: TFile, app: App, settings: any): Promise<any> {
let res: any
const yamlContent: any = yamlFrontMatter.loadFront(markdown);
const __content = yamlContent.__content
const file2Block = markdownToBlocks(__content);
const frontmasster = app.metadataCache.getFileCache(nowFile)?.frontmatter
const notionID = frontmasster ? frontmasster.notionID : null
if (notionID) {
res = await this.updatePage(notionID, title, allowTags, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, file2Block);
} else {
res = await this.createPage(title, allowTags, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, file2Block);
}
if (res.status === 200) {
await this.updateYamlInfo(markdown, nowFile, res, app, settings)
} else {
new Notice(`${res.text}`)
}
return res
}
async updateYamlInfo(yamlContent: string, nowFile: TFile, res: any, app: App, settings: any) {
let { url, id } = res.json
// replace www to notionID
const { notionID } = settings;
if (notionID !== "") {
// replace url str "www" to notionID
url = url.replace("www.notion.so", `${notionID}.notion.site`)
}
await app.fileManager.processFrontMatter(nowFile, yamlContent => {
if (yamlContent['notionID']) {
delete yamlContent['notionID']
}
if (yamlContent['link']) {
delete yamlContent['link']
}
// add new notionID and link
yamlContent.notionID = id;
yamlContent.link = url;
});
try {
await navigator.clipboard.writeText(url)
} catch (error) {
new Notice(`复制链接失败,请手动复制${error}`)
}
// const __content = yamlContent.__content;
// delete yamlContent.__content
// const yamlhead = yaml.stringify(yamlContent)
// // if yamlhead hava last \n remove it
// const yamlhead_remove_n = yamlhead.replace(/\n$/, '')
// // if __content have start \n remove it
// const __content_remove_n = __content.replace(/^\n/, '')
// const content = '---\n' +yamlhead_remove_n +'\n---\n' + __content_remove_n;
// try {
// await nowFile.vault.modify(nowFile, content)
// } catch (error) {
// new Notice(`write file error ${error}`)
// }
}
}

View File

@@ -3,7 +3,7 @@ import { addIcon } from 'obsidian';
const icons: Record<string, string> = { const icons: Record<string, string> = {
'notion-logo': ` 'notion-logo': `
<!-- License: Apache. Made by lawnchairlauncher: https://github.com/lawnchairlauncher/lawnicons --> <!-- License: Apache. Made by lawnchairlauncher: https://github.com/lawnchairlauncher/lawnicons -->
<svg width="100px" height="100px" viewBox="0 0 192 192" xmlns="http://www.w3.org/2000/svg" fill="none"><path fill="#000000" fill-rule="evenodd" d="m138.462 21.522 27.784 19.588.044.033.275.201c1.713 1.256 3.349 2.455 4.452 3.83 1.411 1.76 1.884 3.644 1.884 5.877v104.706c0 3.587-.635 7.178-3.058 9.934-2.451 2.789-6.145 4.067-10.732 4.394l-.018.001-98.629 5.971-.021.001c-3.242.154-6.094.035-8.669-.907-2.688-.984-4.719-2.727-6.604-5.129l-.01-.012-19.979-25.979-.012-.017c-3.81-5.086-5.723-9.348-5.723-14.509V34.509c0-3.12.688-6.394 2.745-9.033 2.124-2.727 5.356-4.328 9.503-4.686l.058-.005 84.854-4.344c5.192-.445 8.938-.576 12.286.185 3.459.787 6.208 2.452 9.57 4.896ZM56.43 157.336h.002v3.3c0 1.904.47 2.337.613 2.452.296.235 1.203.652 3.642.518l97.449-5.371c1.928-.106 2.256-.649 2.348-.801l.005-.008c.29-.476.486-1.407.486-3.357V60.001c0-1.635-.334-2.218-.421-2.327l-.005-.007-.002-.003-.006-.002a.117.117 0 0 1-.012-.004c-.053-.019-.263-.078-.724-.037l-.057.005-101.622 5.668c-.624.056-.973.163-1.152.242-.142.062-.173.104-.181.116l-.002.002c-.066.085-.36.586-.36 2.321v91.361Zm9.085-106.705 87.074-4.506-21.028-15.375-.039-.031c-1.259-.98-2.507-1.854-4.12-2.46-1.588-.597-3.695-.993-6.669-.734l-.05.005-87.009 4.898h-.01a6.453 6.453 0 0 0-.893.116L49.934 48.56c2.037 1.646 3.109 2.146 4.337 2.367 1.538.277 3.52.167 7.722-.115l3.522-.237v.056Zm-34.231-3.586v83.893c0 .538.175 1.061.498 1.49l13.174 17.464V61.224a2.47 2.47 0 0 0-.877-1.889l-.08-.068-12.715-12.222Zm109.871 35.062c.451 2.04 0 4.082-2.041 4.315l-3.393.673v49.881c-2.947 1.586-5.66 2.492-7.927 2.492-3.622 0-4.528-1.134-7.239-4.53l-.003-.003L98.36 100.02v33.78l7.02 1.59s0 4.082-5.664 4.082l-15.615.906c-.455-.91 0-3.176 1.582-3.627l4.078-1.131V90.955l-5.66-.459c-.454-2.04.677-4.987 3.85-5.216l16.754-1.128 23.09 35.367V88.231l-5.885-.677c-.455-2.499 1.356-4.315 3.618-4.536l15.627-.91v-.001Z" clip-rule="evenodd"/></svg> <svg width="110px" height="110px" viewBox="0 0 192 192" xmlns="http://www.w3.org/2000/svg" fill="none"><path fill="#817f7a" fill-rule="evenodd" d="m138.462 21.522 27.784 19.588.044.033.275.201c1.713 1.256 3.349 2.455 4.452 3.83 1.411 1.76 1.884 3.644 1.884 5.877v104.706c0 3.587-.635 7.178-3.058 9.934-2.451 2.789-6.145 4.067-10.732 4.394l-.018.001-98.629 5.971-.021.001c-3.242.154-6.094.035-8.669-.907-2.688-.984-4.719-2.727-6.604-5.129l-.01-.012-19.979-25.979-.012-.017c-3.81-5.086-5.723-9.348-5.723-14.509V34.509c0-3.12.688-6.394 2.745-9.033 2.124-2.727 5.356-4.328 9.503-4.686l.058-.005 84.854-4.344c5.192-.445 8.938-.576 12.286.185 3.459.787 6.208 2.452 9.57 4.896ZM56.43 157.336h.002v3.3c0 1.904.47 2.337.613 2.452.296.235 1.203.652 3.642.518l97.449-5.371c1.928-.106 2.256-.649 2.348-.801l.005-.008c.29-.476.486-1.407.486-3.357V60.001c0-1.635-.334-2.218-.421-2.327l-.005-.007-.002-.003-.006-.002a.117.117 0 0 1-.012-.004c-.053-.019-.263-.078-.724-.037l-.057.005-101.622 5.668c-.624.056-.973.163-1.152.242-.142.062-.173.104-.181.116l-.002.002c-.066.085-.36.586-.36 2.321v91.361Zm9.085-106.705 87.074-4.506-21.028-15.375-.039-.031c-1.259-.98-2.507-1.854-4.12-2.46-1.588-.597-3.695-.993-6.669-.734l-.05.005-87.009 4.898h-.01a6.453 6.453 0 0 0-.893.116L49.934 48.56c2.037 1.646 3.109 2.146 4.337 2.367 1.538.277 3.52.167 7.722-.115l3.522-.237v.056Zm-34.231-3.586v83.893c0 .538.175 1.061.498 1.49l13.174 17.464V61.224a2.47 2.47 0 0 0-.877-1.889l-.08-.068-12.715-12.222Zm109.871 35.062c.451 2.04 0 4.082-2.041 4.315l-3.393.673v49.881c-2.947 1.586-5.66 2.492-7.927 2.492-3.622 0-4.528-1.134-7.239-4.53l-.003-.003L98.36 100.02v33.78l7.02 1.59s0 4.082-5.664 4.082l-15.615.906c-.455-.91 0-3.176 1.582-3.627l4.078-1.131V90.955l-5.66-.459c-.454-2.04.677-4.987 3.85-5.216l16.754-1.128 23.09 35.367V88.231l-5.885-.677c-.455-2.499 1.356-4.315 3.618-4.536l15.627-.91v-.001Z" clip-rule="evenodd"/></svg>
` `
}; };

View File

@@ -1,18 +1,7 @@
import { import {App, Editor, MarkdownView, Notice, Plugin, PluginSettingTab, Setting} from "obsidian";
App, import {addIcons} from 'src/icon';
Editor, import {Upload2Notion} from "src/Upload2Notion";
MarkdownView, import {NoticeMConfig} from "src/Message";
Modal,
Notice,
Plugin,
PluginSettingTab,
Setting,
normalizePath
} from "obsidian";
import {addIcons} from 'icon';
import { Upload2Notion } from "Upload2Notion";
import {NoticeMConfig} from "Message";
import { CLIENT_RENEG_LIMIT } from "tls";
// Remember to rename these classes and interfaces! // Remember to rename these classes and interfaces!
@@ -48,7 +37,7 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
"Share to NotionNext", "Share to NotionNext",
async (evt: MouseEvent) => { async (evt: MouseEvent) => {
// Called when the user clicks the icon. // Called when the user clicks the icon.
this.upload(); await this.upload();
} }
); );
@@ -60,13 +49,13 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
id: "share-to-notionnext", id: "share-to-notionnext",
name: "share to notionnext", name: "share to notionnext",
editorCallback: async (editor: Editor, view: MarkdownView) => { editorCallback: async (editor: Editor, view: MarkdownView) => {
this.upload() await this.upload()
}, },
}); });
// This adds a settings tab so the user can configure various aspects of the plugin // This adds a settings tab so the user can configure various aspects of the plugin
this.addSettingTab(new SampleSettingTab(this.app, this)); this.addSettingTab(new ObsidianSettingTab(this.app, this));
} }
@@ -80,12 +69,12 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
); );
return; return;
} }
const { markDownData, nowFile, tags, type, slug, stats, category, summary, paword, favicon, datetime } =await this.getNowFileMarkdownContent(this.app); const { markDownData, nowFile, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime } =await this.getNowFileMarkdownContent(this.app);
if (markDownData) { if (markDownData) {
const { basename } = nowFile; const { basename } = nowFile;
const upload = new Upload2Notion(this); const upload = new Upload2Notion(this);
const res = await upload.syncMarkdownToNotion(basename, allowTags, tags, type, slug, stats, category, summary, paword, favicon, datetime, markDownData, nowFile, this.app, this.settings) const res = await upload.syncMarkdownToNotion(basename, allowTags, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, markDownData, nowFile, this.app, this.settings)
if(res.status === 200){ if(res.status === 200){
new Notice(`${langConfig["sync-success"]}${basename}`) new Notice(`${langConfig["sync-success"]}${basename}`)
}else { }else {
@@ -97,6 +86,8 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
async getNowFileMarkdownContent(app: App) { async getNowFileMarkdownContent(app: App) {
const nowFile = app.workspace.getActiveFile(); const nowFile = app.workspace.getActiveFile();
const { allowTags } = this.settings; const { allowTags } = this.settings;
let emoji = ''
let cover = ''
let tags = [] let tags = []
let type = '' let type = ''
let slug = '' let slug = ''
@@ -110,6 +101,8 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
const FileCache = app.metadataCache.getFileCache(nowFile) const FileCache = app.metadataCache.getFileCache(nowFile)
try { try {
if(allowTags) { if(allowTags) {
emoji = FileCache.frontmatter.titleicon;
cover = FileCache.frontmatter.coverurl;
tags = FileCache.frontmatter.tags; tags = FileCache.frontmatter.tags;
type = FileCache.frontmatter.type; type = FileCache.frontmatter.type;
slug = FileCache.frontmatter.slug; slug = FileCache.frontmatter.slug;
@@ -128,6 +121,8 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
return { return {
markDownData, markDownData,
nowFile, nowFile,
emoji,
cover,
tags, tags,
type, type,
slug, slug,
@@ -157,7 +152,7 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
} }
} }
class SampleSettingTab extends PluginSettingTab { class ObsidianSettingTab extends PluginSettingTab {
plugin: ObsidianSyncNotionPlugin; plugin: ObsidianSyncNotionPlugin;
constructor(app: App, plugin: ObsidianSyncNotionPlugin) { constructor(app: App, plugin: ObsidianSyncNotionPlugin) {
@@ -170,23 +165,18 @@ class SampleSettingTab extends PluginSettingTab {
containerEl.empty(); containerEl.empty();
containerEl.createEl("h2", {
text: "Settings for obsidian to NotionNext plugin.",
});
new Setting(containerEl) new Setting(containerEl)
.setName("Notion API Token") .setName("Notion API Token")
.setDesc("It's a secret") .setDesc("It's a secret")
.addText((text) =>{ .addText((text) =>{
let t = text text.inputEl.type = 'password';
return text
.setPlaceholder("Enter your Notion API Token") .setPlaceholder("Enter your Notion API Token")
.setValue(this.plugin.settings.notionAPI) .setValue(this.plugin.settings.notionAPI)
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.notionAPI = value; this.plugin.settings.notionAPI = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}) })
// t.inputEl.type = 'password'
return t
}); });
@@ -194,17 +184,15 @@ class SampleSettingTab extends PluginSettingTab {
.setName("Database ID") .setName("Database ID")
.setDesc("It's a secret") .setDesc("It's a secret")
.addText((text) => { .addText((text) => {
let t = text text.inputEl.type = 'password';
return text
.setPlaceholder("Enter your Database ID") .setPlaceholder("Enter your Database ID")
.setValue(this.plugin.settings.databaseID) .setValue(this.plugin.settings.databaseID)
.onChange(async (value) => { .onChange(async (value) => {
this.plugin.settings.databaseID = value; this.plugin.settings.databaseID = value;
await this.plugin.saveSettings(); await this.plugin.saveSettings();
}) })
// t.inputEl.type = 'password'
return t
} }
); );
// notionDatabaseID.controlEl.querySelector('input').type='password' // notionDatabaseID.controlEl.querySelector('input').type='password'

View File

View File

@@ -18,5 +18,7 @@
}, },
"include": [ "include": [
"**/*.ts" "**/*.ts"
, "Upload2Notion.ts" ] ,
"src/Upload2Notion.ts"
]
} }

865
yarn.lock

File diff suppressed because it is too large Load Diff