Compare commits

..

95 Commits
1.0.1 ... 2.4.0

Author SHA1 Message Date
Jiaxin Peng
1f58670906 Update version to 2.4.0 2024-08-19 11:24:23 +01:00
Jiaxin Peng
fc47d36e8d Merge pull request #32 from jxpeng98/long-notes-support
Long notes support
2024-08-18 22:39:00 +01:00
Jiaxin Peng
cd4e610c46 modify type of pageProperties 2024-08-18 18:42:59 +01:00
Jiaxin Peng
b05c248c44 feat: support long notes syncing 2024-08-18 17:16:08 +01:00
Jiaxin Peng
800e218dee Update version to 2.3.5 2024-08-18 13:08:04 +01:00
Jiaxin Peng
3b774552af update martian 2024-08-18 13:07:52 +01:00
b8a02c49f1 Update version to 2.3.4 2024-07-28 01:13:39 +01:00
Jiaxin Peng
046a67f22c Merge pull request #29 from jxpeng98/debug-general
General: use node-fetch to post notes
2024-07-28 01:08:08 +01:00
426f05dfbd General: use node-fetch to post notes 2024-07-28 01:07:35 +01:00
Jiaxin Peng
0addaa0d7f Merge pull request #28 from jxpeng98/debug-next
Next: use node-fetch to post notes for a better debug
2024-07-28 00:54:05 +01:00
1d5792c966 Next: use node-fetch to post notes for a better debug 2024-07-28 00:52:27 +01:00
Jiaxin Peng
193d3f6f87 Merge pull request #27 from jxpeng98/better-debug
custom: node-fetch replaces requestUrl
2024-07-27 23:37:27 +01:00
e9c2b4aada Custom: use node-fetch to post notes rather than requestUrl for better debug 2024-07-27 23:36:00 +01:00
b5ac42e6e3 use node-fetch to post notes for a better debug 2024-07-27 22:28:21 +01:00
60d002677b use json to parse the error information 2024-07-27 21:38:09 +01:00
3c3ec6881e Update version to 2.3.3 2024-07-15 14:19:11 +01:00
Jiaxin Peng
a80155fafc Merge pull request #26 from jxpeng98/notice-info
change the success notice colour
2024-07-15 14:17:01 +01:00
3a814b8390 feat: change the success notice colour 2024-07-15 14:15:10 +01:00
bb522db9a2 fix: readme gif link update 2024-07-15 14:13:09 +01:00
201f72ae0e update notice 2024-07-14 23:57:01 +01:00
be6f192dd8 Update readme file 2024-07-14 21:51:53 +01:00
4ce1019094 Update Readme links 2024-07-14 20:27:02 +01:00
095c608511 update readme with new instruction 2024-07-14 17:58:21 +01:00
2d8d7fb11a Update version to 2.3.2 2024-07-14 17:52:59 +01:00
36725cb699 fix: Text property in custom database 2024-07-14 17:52:43 +01:00
Jiaxin Peng
654e7e854d 更新 README.md 2024-07-10 00:32:00 +01:00
Jiaxin Peng
fa67f08512 Update version to 2.3.1 2024-07-05 09:50:31 +01:00
Jiaxin Peng
7e42ef49a7 Merge pull request #24 from jxpeng98/notion-link
feat: add notion link display
2024-07-05 09:49:27 +01:00
Jiaxin Peng
5a95aa0acc feat: add notion link display 2024-07-05 09:48:35 +01:00
Jiaxin Peng
ded01ce678 Update version to 2.3.0 2024-07-04 20:57:35 +01:00
Jiaxin Peng
3535d9fc70 Merge pull request #23 from jxpeng98/edit-modal
edit modal update for customised database
2024-07-04 20:55:41 +01:00
Jiaxin Peng
9c93e38aff reformat code and update readme file 2024-07-04 20:51:07 +01:00
Jiaxin Peng
bfde5dfe1a finish the edit modal 2024-07-04 20:28:28 +01:00
Jiaxin Peng
d8eb3c8984 feat: cancel function works well 2024-07-04 18:12:31 +01:00
Jiaxin Peng
1cee2b67a6 update edit modal with better display setting. 2024-07-04 17:06:23 +01:00
Jiaxin Peng
82529ce56a back up for edit modal 2024-07-04 10:00:46 +01:00
Jiaxin Peng
70f30f0713 Add support for notion link display in the front matter. 2024-07-03 22:35:47 +01:00
Jiaxin Peng
24ebb0bf8a Back up edit modal 2024-07-03 22:01:46 +01:00
Jiaxin Peng
d7372c7c55 Improve preview function for customised database 2024-07-03 16:05:24 +01:00
Jiaxin Peng
f7bcf71020 create retrieve function to get all created properties for customised database 2024-07-03 14:26:02 +01:00
Jiaxin Peng
72146afe48 update settingModal.ts refactor the customise database and improve the creating logic 2024-07-03 13:39:56 +01:00
Jiaxin Peng
9bcdb42edb settingmodal save mapping for reference 2024-07-03 11:06:54 +01:00
Jiaxin Peng
16e0827991 update the basic function of edit modal 2024-07-03 08:47:33 +01:00
Jiaxin Peng
863a6fb0dc more clear logic customised database setting code 2024-07-02 23:56:35 +01:00
Jiaxin Peng
5799ffe79b Update custom function and adjust the custom interface ui 2024-07-02 23:48:37 +01:00
Jiaxin Peng
a8ebab7fcb change tags titleicon and summary to optional function in next database 2024-03-17 14:36:55 +00:00
Jiaxin Peng
e0ec27ad07 update readme 2024-03-12 22:04:34 +00:00
Jiaxin Peng
85220105b0 update README 2024-03-08 15:23:17 +00:00
Jiaxin Peng
9afdb24a82 typo 2024-02-26 11:57:28 +00:00
Jiaxin Peng
3c7ab13995 update readme 2024-02-26 11:53:02 +00:00
Jiaxin Peng
31b72f3bd3 Update version to 2.2.4 2024-02-19 23:08:47 +00:00
Jiaxin Peng
ee5aada1b9 Update version to 2.2.3 2024-02-19 20:51:47 +00:00
Jiaxin Peng
9e15044f6a Update version to 2.2.2 2024-02-16 23:10:22 +00:00
Jiaxin Peng
9d891dae55 Update version to 2.2.1 2024-01-29 20:43:14 +00:00
Jiaxin Peng
bc665253ec Update version to 2.2.0 2024-01-29 19:03:09 +00:00
Jiaxin Peng
125c75f7c0 Merge pull request #13 from jxpeng98/custom
add the customisation function
2024-01-29 19:01:37 +00:00
Jiaxin Peng
8248640b5f fix the extra argument in the uploadcommand 2024-01-29 11:55:52 +00:00
Jiaxin Peng
0198898086 update the readme and changelog 2024-01-29 11:53:18 +00:00
Jiaxin Peng
8c6aea8250 Complete the basic function of customisation 2024-01-29 11:12:07 +00:00
Jiaxin Peng
a179d3da52 almost finish the structure 2024-01-29 00:55:06 +00:00
Jiaxin Peng
569d8eb699 fix the error of the display of title custom button 2024-01-28 00:26:58 +00:00
Jiaxin Peng
2a58dd3258 complete the custom ui design 2024-01-28 00:11:11 +00:00
Jiaxin Peng
8c65e471eb Update version to 2.1.2 2024-01-09 11:44:40 +00:00
Jiaxin Peng
388693d5b4 Update version to 2.1.1 2024-01-09 11:38:03 +00:00
Jiaxin Peng
dda6710fe9 remove redundant settingtabs 2024-01-09 11:37:47 +00:00
Jiaxin Peng
76ea9a4aee Update version to 2.1.0 2024-01-09 11:34:11 +00:00
Jiaxin Peng
856f83e8a1 Add confirmation delete button and modify edit modal interface. 2024-01-09 11:33:39 +00:00
Jiaxin Peng
f670dd972a Update version to 2.0.1 2024-01-03 00:37:47 +00:00
Jiaxin Peng
e6de2d036a add preview and edit function 2024-01-03 00:37:34 +00:00
Jiaxin Peng
dd131f5df1 readme update 2023-12-29 00:41:02 +00:00
Jiaxin Peng
a9fb81fc1b readme error 2023-12-29 00:39:18 +00:00
Jiaxin Peng
4f21e3ff91 Update version to 2.0.0 2023-12-29 00:36:48 +00:00
Jiaxin Peng
4abbbecd37 modify the notionapi variable 2023-12-29 00:36:29 +00:00
Jiaxin Peng
35746f1c51 Merge pull request #12 from jxpeng98/add-button
rebuild the plugin and add new button for multi database support
2023-12-29 00:26:16 +00:00
Jiaxin Peng
9ac64268fc Merge branch 'master' into add-button 2023-12-29 00:26:02 +00:00
Jiaxin Peng
3123d91e97 complete the new version for add button 2023-12-29 00:21:21 +00:00
Jiaxin Peng
cf08703fea complete the basic notionnext function 2023-12-28 00:55:06 +00:00
Jiaxin Peng
fb463ceaae modify language map 2023-12-27 00:48:39 +00:00
Jiaxin Peng
306573d81e merge 1.1.2 2023-12-27 00:37:54 +00:00
Jiaxin Peng
fb5f7b502d finish some command coding 2023-12-27 00:27:32 +00:00
Jiaxin Peng
59c15f1206 modify setting modal 2023-12-26 21:19:24 +00:00
Jiaxin Peng
370444fb94 basic display of modal 2023-12-26 09:03:29 +00:00
Jiaxin Peng
11aaf14c0b update general tabs 2023-12-25 22:47:24 +00:00
Jiaxin Peng
f3fb251cf7 set the class next tabs 2023-12-25 22:43:11 +00:00
Jiaxin Peng
913fa4dd03 Update version to 1.1.2 2023-12-06 15:22:53 +00:00
Jiaxin Peng
f638310f31 modify workflow 2023-12-06 15:22:09 +00:00
Jiaxin Peng
c2421f7a84 add the property 'status' for NotionNext 2023-12-06 15:19:57 +00:00
Jiaxin Peng
6c30d0c2b4 adjust the limitation of blocks 2023-12-06 14:40:15 +00:00
Jiaxin Peng
ccb5a20abe add custom frontmatter 2023-11-23 22:11:04 +00:00
Jiaxin Peng
630e3a20c2 Update version to 1.1.1 2023-11-23 11:54:50 +00:00
Jiaxin Peng
040b72b760 Update version to 1.1.0 2023-11-20 22:01:20 +00:00
Jiaxin Peng
7df4b5e9c8 Merge pull request #9 from jxpeng98/package-test
package update and add tag syncing button
2023-11-20 21:59:53 +00:00
Jiaxin Peng
3f35a56b6e rebuild the locale file and add tag button to control tag syncing function 2023-11-20 21:57:30 +00:00
Jiaxin Peng
3c7e266a35 setting tab bug fix 2023-11-20 17:07:04 +00:00
Jiaxin Peng
8201c706ea remove block limit and update packages 2023-11-16 21:09:31 +00:00
37 changed files with 3665 additions and 1501 deletions

View File

@@ -1,4 +1,4 @@
name: Release Obsidian-to-NotionNext plugin
name: Release
on:
push:
@@ -7,18 +7,26 @@ on:
env:
PLUGIN_NAME: share-to-notionnext # Change this to match the id of your plugin.
CHANGELOG_FILENAME: CHANGELOG.md
jobs:
build:
name: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: "16.x"
node-version: "18"
# - name: Generate changelog
# id: changelog
# uses: saadmk11/changelog-ci@v1.1.2
# with:
# github_token: ${{ secrets.REPO_ACCESS_TOKEN }}
# release_version: ${{ github.ref }}
# changelog_filename: CHANGELOG.md
- name: Build
id: build
@@ -40,6 +48,7 @@ jobs:
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
body_path: ${{ env.CHANGELOG_FILENAME }}
draft: false
prerelease: false

View File

@@ -1,5 +1,4 @@
name: Test Obsidian-to-NotionNext plugin
name: Test and Build
on:
push:
@@ -15,11 +14,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: "16.x"
node-version: "18"
- name: Build
id: build

8
CHANGELOG.md Normal file
View File

@@ -0,0 +1,8 @@
## Features
- Support WebP format images. 支持 WebP 格式图片。
- Support sync with long notes. 支持长文章同步。
Long articles or notes have been split into multiple parts, and the parts are synchronized in order.
长文章同步是通过将长文章或笔记拆分为多个部分,按顺序同步这些部分。

View File

@@ -1,11 +1,47 @@
# 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)的基础之上,增加了匹配[NotionNext](https://github.com/tangly1024/NotionNext)模板的功能。这样可以直接在Obsidian编辑整理好之后一键发布。
## 更新说明
### 1.1.2
- 修复了一个拼写错误,导致无法同步`status`到NotionNext。现在你可以使用`stats`或者`status`来同步文章的状态到NotionNext。这个更新不会影响到同步到General数据库的功能。
- **`stats``status`都可以使用,但是你只能使用其中一个。**
```yaml
stats: Draft # Draft, Invisible, Published, default is Draft 默认是Draft
# or
status: Draft # Draft, Invisible, Published, default is Draft 默认是Draft
# both of them will work, but you can only use one of them.
```
### 1.1.1
- 修复了在日语系统下,无法显示设置按钮的错误。
- 增加日语翻译。
### 1.1.0
- 修复设置中自定义标题的显示问题。
- 增加了标签同步的控制按钮。以减少空标签集造成的上传失败。
如果你不想使用同步标签功能你可以在设置中关闭。不论你在md文件表头添加了tags与否都不会将标签同步到Notion数据库中。
如果你需要同步标签到Notion数据库中**你需要通过如下格式添加表填到md表头中。**
```yaml
tags: #空标签选项1
tags: [test,test1,test2] # 用中括号将所有的标签括起来选项2
tags:
- test
- test1
- test2 # 每个标签单独一行选项3
```
### 1.0.1
- 修复了自定义标题在设置菜单中显示错误的问题。
### 1.0.0 (大更新)
- 这个版本开始,你可以**任意修改第一列表头的名字 (标题列,默认:'title')**。 **注意你必须保证你的Notion数据库中有'tags这一列的存在并且在markdown文件中把`tags:`添加到你的YAML表头中。否则会返回`400 错误`。但是你可以`tags:`设置为空。**
![](https://minioapi.pjx.ac.cn/img1/2023/11/4a298b9be3990e9d2201bf2f50ca5a0a.png)
@@ -16,6 +52,7 @@
![](https://minioapi.pjx.ac.cn/img1/2023/11/becb60fc44783842da4b3cf4c322f363.gif)
### 0.2.6
- 增加一个按钮用来控制是否在名列列表中显示如下数据库。
![](https://minioapi.pjx.ac.cn/img1/2023/11/147c6a4eaa34da41a6f102558ed77106.png)
@@ -24,8 +61,11 @@
![](https://minioapi.pjx.ac.cn/img1/2023/11/70b8e4fc2148688ccbd6cfc53ce339a2.png)
### 0.2.3
- 修复了一个bug现在可以正常更新了。
### 0.2.2
- 支持NotionNext 和 普通Notion 数据库的同步。
- 你可以有一个NotionNext和一个普通Notion数据库。
- 普通Notion数据库的同步方式和原作者的一样只同步`title``tags`。**如果使用普通数据库功能,你需要保证你的数据库中有`title``tags`这两个表头。必须使用小写。**
@@ -33,26 +73,37 @@
![](https://minioapi.pjx.ac.cn/img1/2023/11/712a12081d855aa60f82a7b46913ab7e.gif)
![](https://minioapi.pjx.ac.cn/img1/2023/11/9de76cecceef74c78884ddfc1c221659.gif)
### 0.2.1
- 重构代码。
### 0.2.0
- 从这个版本开始,重构了交互逻辑。现在当你点击左侧的按钮,会弹出可同步的数据库选项。你可以选择你想要同步的数据库,然后点击同步按钮。**但是现在仅支持同步单个数据库如果你的数据库不是NotionNext你可以不用更新。**
### 0.1.10
- 修正了设置中的中文显示。
### 0.1.8
- 重新整理了代码,增加了一个仓库转换按钮为之后的多数据库支持作准备。**但是现在还不支持多数据库如果你的数据库不是NotionNext你可以不用更新。**
### 0.1.7
- [x] 删除了设置中`convert tags`的选项。 你现在可以直接添加tags而不需要考虑是否开启tags选项。如果你不需要tags可以直接删除tags选项或者留白。
## 使用方式
### 注意事项
本插件现在暂时只能用于匹配[NotionNext](https://github.com/tangly1024/NotionNext)。如果你不是使用这个模板,你只会无限返回`error 400`.
所以没有NotionNext需求的请使用原作者的[Obsidian-to-notion](https://github.com/EasyChris/obsidian-to-notion)
### 准备安装
在安装插件之前,你必须配置好了以下内容:
1. 你的NotionNext数据库。
@@ -71,29 +122,33 @@
- icon
在最新的0.0.6版本,我还增加了以下内容:
- titleicon 标题的emoji图标,暂时只能支持emoji
- coverurl 封面图片的url url可以是任何一种图片的url 例如jpgpngsvg等等。
**如果你是直接复制的NotionNext的模板这些内容应该已经有了。我在原作者的基础之上对本插件的内容进行了更改所以你只需要保证你的数据库有如上内容并且所有的字母都是小写**
**⚠️⚠️⚠️:表头全部小写!!!顺序无所谓!**
### 安装插件
#### 社区插件市场下载
打开`Obsidian -> 设置 -> 社区插件 -> 浏览 -> 搜索Share to NotionNext`, 然后点击安装。
#### 手动安装
1. 关闭Obsidian
2. 从Release下载插件文件解压到你的obsidian插件目录下。
3. 重新打开Obsidian进入设置启用插件。
4. 在设置中找到Obsidian to NotionNext填入你的NotionNext Database ID 和 API token。
### 使用插件
在仓库中我上传了一个模板你可以直接复制到你的模板文件夹中。然后使用Obsidian的模板功能一键生成新笔记。
如果你不想使用模板你也可以直接在Obsidian中创建一个新的文件然后复制下边的内容。然后保存。
```markdown
---
# default value has been set.
@@ -124,6 +179,7 @@ Contents Below
```
模板使用如下:
![](https://minioapi.pjx.ac.cn/img1/2023/09/354b950e5777b48832c2475e4a31f2cc.gif)
@@ -137,6 +193,7 @@ Contents Below
非常感谢原作者的开发,我只是在原作者的基础之上进行了修改,增加了一些功能。如果你觉得这个插件对你有帮助,可以给[原作者](https://github.com/EasyChris/obsidian-to-notion)一个star。
# Obsidian to Notion
[![](https://github.com/Easychris/obsidian-to-notion/actions/workflows/CI.yml/badge.svg)](https://github.com/Easychris/obsidian-to-notion/actions/workflows/CI.yml)
[![Release Obsidian plugin](https://github.com/Easychris/obsidian-to-notion/actions/workflows/release.yml/badge.svg)](https://github.com/Easychris/obsidian-to-notion/actions/workflows/release.yml)
[![GitHub license](https://camo.githubusercontent.com/400c4e52df43f6a0ab8a89b74b1a78d1a64da56a7848b9110c9d2991bb7c3105/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d47504c76332d626c75652e737667)](https://raw.githubusercontent.com/EasyChris/obsidian-to-notion/master/LICENSE)
@@ -152,31 +209,40 @@ Obsidian share to Notion [English](README.md)
![](./doc/1.gif)
# 使用方式
## 安装插件
### 市场下载
插件市场搜索 noiton 即可下载
![](https://afox-1256168983.cos.ap-shanghai.myqcloud.com/20220628214145.png)
### BRAT
插件中中心搜索 BRAT
添加 `EasyChris/obsidian-to-notion` 到 BRAT 插件安装列表中
返回插件中心启用即可
### 手动安装
```
cd YOUR_OBSIDIAN_FOLDER/.obsidian/plugins/
git clone https://github.com/EasyChris/obsidian-to-notion.git
```
## 申请 Notion API
官方参考文档:[https://developers.notion.com/docs](https://developers.notion.com/docs)
### 第 1 步创建integration。
### 第 1 步创建integration
转到 [https://www.notion.com/my-integrations](https://www.notion.com/my-integrations)
创建完成之后,复制`secrets toekn`
![](https://files.readme.io/2ec137d-093ad49-create-integration.gif)
### 第2步与你的集成共享一个数据库
新建一个的page权限为公开
在page中新建一个数据库 -> 需要`full page database`
![](./doc/3.gif)
@@ -191,9 +257,6 @@ git clone https://github.com/EasyChris/obsidian-to-notion.git
![](https://afox-1256168983.cos.ap-shanghai.myqcloud.com/20220618102029.png)
### 第三步复制database ID
```
@@ -202,27 +265,28 @@ https://www.notion.so/myworkspace/a8aec43384f447ed84390e8e42c2e089?v=...
```
## 打开插件配置
将得到的 `NOTION_API_KEY``DATABASE_ID`填入配置当中
![](./doc/2.png)
## 上传文件内容到notion
点击上传notion的按钮
![](./doc/4.png)
上传成功之后会自动生成一个分享链接
![](./doc/5.png)
## 页面 Banner 链接[可选]
默认可以不填写
横幅URL必须是图像URL例如https//i.imgur.com/xxx.jpg
## Notion ID [可选]
Notion ID是你想分享文件的页面ID。
如果你不写它notion将分享到默认的链接https://www.notion.so/myworkspace/a8aec43384f447ed84390访问这个页面将重定向到你的网站页面
如果你写了Notion ID它将分享到页面链接如https://your_user_name.notion.site/myworkspace/a8aec43384f447ed84390不需要重定向网址
如果你不写它notion将分享到默认的链接<https://www.notion.so/myworkspace/a8aec43384f447ed84390访问这个页面将重定向到你的网站页面>
如果你写了Notion ID它将分享到页面链接如<https://your_user_name.notion.site/myworkspace/a8aec43384f447ed84390不需要重定向网址>
## 同步图片
@@ -233,6 +297,7 @@ Notion ID是你想分享文件的页面ID。
[顿顿饭](https://dun.mianbaoduo.com/@easy)
# 感谢
[开发流程 | Obsidian 插件开发文档](https://luhaifeng666.github.io/obsidian-plugin-docs-zh/zh/getting-started/development-workflow.html)
[GitHub - devbean/obsidian-wordpress: An obsidian plugin for publishing docs to WordPress.](https://github.com/devbean/obsidian-wordpress)

442
README.md
View File

@@ -1,156 +1,275 @@
# Obsidian to NotionNext
[中文文档](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.
[![Test](https://github.com/jxpeng98/obsidian-to-NotionNext/actions/workflows/test.yml/badge.svg)](https://github.com/jxpeng98/obsidian-to-NotionNext/actions/workflows/test.yml)
[![Release](https://github.com/jxpeng98/obsidian-to-NotionNext/actions/workflows/release.yml/badge.svg)](https://github.com/jxpeng98/obsidian-to-NotionNext/actions/workflows/release.yml)
[![Obsidian Downloads](https://img.shields.io/badge/dynamic/json?logo=obsidian&color=%23483699&label=downloads&query=%24%5B%22share-to-notionnext%22%5D.downloads&url=https%3A%2F%2Fraw.githubusercontent.com%2Fobsidianmd%2Fobsidian-releases%2Fmaster%2Fcommunity-plugin-stats.json)](https://GitHub.com/jxpeng98/obsidian-to-NotionNext/releases/)
[![GitHub release (with filter)](https://img.shields.io/github/package-json/v/jxpeng98/obsidian-to-NotionNext)](https://github.com/jxpeng98/obsidian-to-NotionNext/releases/)
[//]: # ([![Github all releases]&#40;https://img.shields.io/github/downloads/jxpeng98/obsidian-to-NotionNext/total.svg&#41;]&#40;https://GitHub.com/jxpeng98/obsidian-to-NotionNext/releases/&#41;)
[//]: # ([中文文档]&#40;README-zh.md&#41;)
**Now, support All Notion databases, including NotionNext and General Notion databases, and customise the database list.**
**现在支持所有Notion数据库包括NotionNext和一般Notion数据库以及自定义数据库列表。**
## Precautions
### For customised database users before version 2.3.0
**⚠️⚠️⚠️: The existing customised database should be recreated if you want to update to version 2.3.0. The new version has a new database structure, and the old database structure is not compatible with the new version to build the index properly.**
### 在2.3.0版本之前的自定义数据库用户
**⚠️⚠️⚠️: 如果你想要更新到2.3.0版本,你需要重新创建自定义数据库。新版本有一个新的数据库结构,旧的数据库结构无法构建索引。**
## TODO List
- [x] Modify the Edit function for the custom properties. 改进自定义属性的编辑功能
- [x] Support sync with long notes (From `v2.4.0`). 从`v2.4.0`开始支持长笔记同步
- [x] Support custom properties for Notion General database. 支持自定义属性
- [x] Support preview for database details in plugin settings. 支持预览数据库详情
- [x] Support edit for database details in plugin settings. 支持编辑数据库详情
- [ ] Support group upload with one click 支持一键多数据库上传
## How to use
If you want to use this plugin, you need to follow the following steps to set up the plugin. The steps can be divided into two parts: setting up the Notion API and setting up the plugin in Obsidian.
### 1. Setting up the Notion API
1). Go to [Notion API](https://www.notion.com/my-integrations) to create a new integration, and **copy the token**.
![create-api](https://minioapi.pjx.ac.cn/img1/2024/07/28526e416571f8b1fe70d90bd9975b81.gif)
2). Create a database in your Notion workspace.
Open Notion, click top-left `Create` a new page` -> You can give a title for the page and press "/" Select 'Database - Full page'.
![create-database](https://minioapi.pjx.ac.cn/img1/2024/07/f1bd993ed667bcc980a45194eb647c34.gif)
Rename the database properties to `title` and `tags`.
**⚠️⚠️`title` is the title column! `tags` is lower case⚠**
![rename-title-tags](https://minioapi.pjx.ac.cn/img1/2024/07/20c7b81761195706dde6e7dae69edbc4.gif)
3). Add the Notion API integration.
Click the right-top '...' -> connections -> connect to -> find the integration you created and connect it.
![add-integration](https://minioapi.pjx.ac.cn/img1/2024/07/bf59397146d96727c96dac06515c6c22.gif)
4). Publish the database to the public.
Click the 'Share' -> 'Publish' -> 'Publish' -> **copy the database id from the URL**.
![share-publish](https://minioapi.pjx.ac.cn/img1/2024/07/5e5b9585cfa4a9086ef6704c3b48eacf.gif)
### 2. Setting up the plugin in Obsidian
1). Go to Obsidian settings -> community plugins -> search 'NotionNext' -> install it.
![plugin](https://minioapi.pjx.ac.cn/img1/2024/07/1bf82618e943ec5eb68d89d4556bc3fc.png)
2). Open the plugin settings and create a new database.
![add-database](https://minioapi.pjx.ac.cn/img1/2024/07/115f47b4180d04fb362b931f8092b5fb.png)
### 3. Create a database
**You can create three types of databases:**
#### 1⃣ General database
General database is the default database, which only has the `title` and `tags` columns. **⚠️⚠️`title` and `tags` are lowercase⚠**
![general](https://minioapi.pjx.ac.cn/img1/2024/07/b2d0d0259afae7a542bd9f62256cd2c2.png)
The general is as follows:
- Full Name: the full name that you would like to give to the database.
- Abbreviate Name: the abbreviation that you would like to give to the database.
- Notion tags sync: whether you want to sync the tags column to Notion.
- Customise title property: whether you want to change the `title` to another name (e.g., `name`).
- Notion API token: the token you copied from the Notion API page.
- Notion Database ID: the database ID you copied from the Notion database URL. (e.g., only copy `7158cd3f70ce4f60afd9da5bdf74fd0c` between `site/` and `?`. `https://jxpeng.notion.site/7158cd3f70ce4f60afd9da5bdf74fd0c?v=380317ac5373sadasdas3c13e618f&pvs=4**`)
After setting, it should look like this:
![general-setting](https://minioapi.pjx.ac.cn/img1/2024/07/3fc630171f6e8ae1011271b2b37d5d48.png)
Once you click `save`, the new database will be created in the plugin settings and display in the detabase list.
![database-list](https://minioapi.pjx.ac.cn/img1/2024/07/3dff23b1c13a31f7696dc794fb78eba5.png)
#### 2⃣ NotionNext database
If you use [NotionNext](https://github.com/tangly1024/NotionNext) to set up your website, you can use the NotionNext database. (**All the properties are in lowercase, and you do not need to change the properties**)
![notionnext](https://minioapi.pjx.ac.cn/img1/2024/07/b164160ccfaf4fb23aa7ac1ce467e235.png)
Like the general database, you need to fill in the following information:
- Full Name: the full name that you would like to give to the database.
- Abbreviate Name: the abbreviation that you would like to give to the database.
- Notion API token: the token you copied from the Notion API page.
- Notion Database ID: the database ID you copied from the Notion database URL.
#### 3⃣ Custom database
The custom database is purely customised, and you can customise the properties you want to sync. (**All the properties are in lowercase, and you do not need to change the properties**)
![customise](https://minioapi.pjx.ac.cn/img1/2024/07/612b1377ec94d452abec7dc3fbe6cefb.png)
The basic information is identical to the general database and NotionNext database. You need to fill in the following information:
- Full Name: the full name that you would like to give to the database.
- Abbreviate Name: the abbreviation that you would like to give to the database.
- Notion API token: the token you copied from the Notion API page.
- Notion Database ID: the database ID you copied from the Notion database URL.
After adding the information, You can click `Add New Property` to add the properties you want to sync. There is no limit to the number of properties you can add.
![customise-add-property](https://minioapi.pjx.ac.cn/img1/2024/07/17cfbfa93d92404ff70d920302f15981.png)
**⚠️⚠️⚠️**: **`Title column` is the main property in the Notion database, which has the open button to open the underlying page.** You can select the type of all other properties. **⚠️⚠️⚠️**
![title-column](https://minioapi.pjx.ac.cn/img1/2024/07/0521b0aa6cedbe67bca4966bf79041ea.png)
According to the previously shown database, you can use `title` as the `Title column`, and `Tags` as the property 1.
![title-and-tags](https://minioapi.pjx.ac.cn/img1/2024/07/87529214008147d3aa1bbe5868849f31.png)
**Until now, you have set up the plugin in Obsidian. You can start syncing your notes to Notion.**
### 4. Start syncing
Create a new note in Obsidian, and fill in the front matter with the properties you want to sync.
#### 1⃣ note for general database
For example, we can create a test note with the following front matter:
``` markdown
---
title: test
tags: [test, web]
---
This is a test file.
```
After creating the note, you can click the `Share to NotionNext` button on the left sidebar to sync the note to Notion.
![general-sync](https://minioapi.pjx.ac.cn/img1/2024/07/6a690dc4b04d6b74134e9d3d76636c1b.gif)
#### 2⃣ note for NotionNext database
We use the following front matter for the NotionNext database:
```markdown
---
title: test-notionnext
titleicon: 📎
date: 2023-07-23
coverurl: https://img.jxpeng.dev/2023/08/843e27a210847f05a0f7cfb121fec100.jpg
type: Post
slug: test
status: Draft
category: test
summary: this is a summary for test post
icon: fa-solid fa-camera
password: "1234"
tags:
- test
- web
NotionID-pengjiaxin: 8ba573de-8fdf-4681-b063-c39d26e7860e
---
this is a test file
```
![notionnext-sync](https://minioapi.pjx.ac.cn/img1/2024/07/5539ec534e7a855ad87aa7949e5e836f.gif)
#### 3⃣ note for custom database
##### sync title and tags
Use the previously created general database and rename `title` to `Name` and `tags` to `Tags` in Notion.
![rename-notion](https://minioapi.pjx.ac.cn/img1/2024/07/e4d1c988be22353537998db897c7a471.gif)
The front matter for the custom database is as follows:
```markdown
---
Name: test-custom
Tags:
- test
- web
---
This is a test file.
```
![sync-custom](https://minioapi.pjx.ac.cn/img1/2024/07/973f17955cd921cca6b5007f0990eea4.gif)
##### sync more properties
Add more property to the custom database. For example, we can add a `summary` and `date` property to the custom database.
1). Click `Edit` in the plugin settings.
![edit](https://minioapi.pjx.ac.cn/img1/2024/07/f50639d8118b1f106d7bb29be5d3527b.png)
2). Click `Add` and add the `summary` and `date` property.
![add-property](https://minioapi.pjx.ac.cn/img1/2024/07/49716657adf0255a37fa214abf4716dc.gif)
3). After adding the properties, you can see the properties in the plugin settings.
![four properties](https://minioapi.pjx.ac.cn/img1/2024/07/63a0a0e09d537b1cf55cfdc49de4db1a.png)
4). Add the `summary` and `date` in Notion
![add-in-notion](https://minioapi.pjx.ac.cn/img1/2024/07/d193fe72ce3676bf643b0d1eb92dd4b7.gif)
5). Create a new note with the `summary` and `date` property.
```markdown
---
Name: test-custom-more
Tags:
- test
- web
summary: this is a summary for test custom
date: 2022-05-03
---
This is a test file.
```
6). Sync the note to Notion.
![sync-custom](https://minioapi.pjx.ac.cn/img1/2024/07/0e4cfbd61acdaf813fd81ee6d046e067.gif)
---
## Acknowledgment
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 Tag 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.
**Now, support both NotionNext and General databases.**
## Update
### 1.0.1
- Fix the custom name element display bug in the settings.
### 1.0.0 (Big Update)
- From this version, you can **modify the first column name (title column, default: 'title')** as you want. (**Note: You need to have the 'tags' column in your Notion General database, and add `tags:` in your markdown frontmatter. If not, you will receive `network error 400`. But you can leave the `tags:` blank.**)
![](https://minioapi.pjx.ac.cn/img1/2023/11/4a298b9be3990e9d2201bf2f50ca5a0a.png)
Like this:
![](https://minioapi.pjx.ac.cn/img1/2023/11/4cd8d79cd9dd9dde299e39c666cb3473.gif)
- Add a switch button to control whether display the setting tabs in the plugin settings for both NotionNext and Notion General databases.
![](https://minioapi.pjx.ac.cn/img1/2023/11/becb60fc44783842da4b3cf4c322f363.gif)
### 0.2.6
- Add a switch button to control whether to show the upload command in the command palette.
![](https://minioapi.pjx.ac.cn/img1/2023/11/147c6a4eaa34da41a6f102558ed77106.png)
If you turn off the button for the General database, you won't see the option for the General database in the upload command list.
If you turn off the button for the NotionNext database, you won't see the option for the NotionNext database in the upload command list.
![](https://minioapi.pjx.ac.cn/img1/2023/11/70b8e4fc2148688ccbd6cfc53ce339a2.png)
### 0.2.3
- Fix the bug, now you can update normally.
### 0.2.2
- Support both NotionNext and General Notion database.
- You can have one NotionNext and one General Notion database.
- General Notion database can only have `title` and `tags` columns, and `tags` columns should be the multi-selected property. **the name of the columns is case sensitive. You should use small letter.**
![](https://minioapi.pjx.ac.cn/img1/2023/11/712a12081d855aa60f82a7b46913ab7e.gif)
![](https://minioapi.pjx.ac.cn/img1/2023/11/9de76cecceef74c78884ddfc1c221659.gif)
### 0.2.1
- Restructure the code
### 0.2.0
- From this version, the interactive logic has been rewritten. When you click the ribbon icon, it will display the sync command for all presetting NotionNext databases. You can choose the database you want to sync to. **However, only NotionNext database is supported for now.**
### 0.1.10
- Fix the Chinese support in the settings.
### 0.1.8
- Rebuild the uploadCommand function, and add one button to select the different databases. **However, only NotionNext database is supported for now.**
### 0.1.7
- [x] Removed the `convert tag` option. Now, you can directly add tags in the YAML front matter. If you don't want to add tags, you can delete the tags in the YAML front matter or leave the tags blank.
## How to Use
### Precautions
For now, this plugin is exclusively for [NotionNext](https://github.com/tangly1024/NotionNext). If you're not using this template, you'll keep receiving `error 400`.
For those without a NotionNext requirement, please use the original [Obsidian-to-notion](https://github.com/EasyChris/obsidian-to-notion).
### Pre-Installation Steps
Before installing the plugin, you must have set up the following:
1. Your NotionNext database.
2. According to the original author's readme.md, set up the Notion API, and it should already be associated with your NotionNext repository.
3. NotionNext Database ID
4. Your NotionNext database should have the following contents:
- type
- title
- slug
- category
- tags
- date
- status
- summary
- password
- icon
From 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
#### Install via Community Plugins
Open `Obsidian settings -> Community Plugins -> Browse -> NotionNext -> Share to NotionNext`
#### Mannually Install
1. Close Obsidian.
2. Download the plugin file from Release and unzip it into your Obsidian plugin directory.
3. Re-open Obsidian, go to settings, and enable the plugin.
4. In the settings, find Obsidian to NotionNext, and enter your NotionNext Database ID and API token.
### How to Use
### Using the Plugin
In the repository, I have uploaded a template which you can directly copy into your template folder. After that, use Obsidian's template feature to create a new note with one click.
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
---
# 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: 📎 # emoji icon, default is 📜, 默认是📜
date: 2023-07-23 # default is today 默认是今天。 Format is YYYY-MM-DD 格式是YYYY-MM-DD
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
```
Usage of the template is as follows:
![](https://minioapi.pjx.ac.cn/img1/2023/09/354b950e5777b48832c2475e4a31f2cc.gif)
**Plugin preview is shown below**
![](https://minioapi.pjx.ac.cn/img1/2023/09/d6199619b68fab218fca8ae0cebece78.gif)
---
**Original README.md**
<details> <summary> Original README.md </summary>
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
[![](https://github.com/Easychris/obsidian-to-notion/actions/workflows/CI.yml/badge.svg)](https://github.com/Easychris/obsidian-to-notion/actions/workflows/CI.yml)
[![Release Obsidian plugin](https://github.com/Easychris/obsidian-to-notion/actions/workflows/release.yml/badge.svg)](https://github.com/Easychris/obsidian-to-notion/actions/workflows/release.yml)
[![GitHub license](https://img.shields.io/github/license/EasyChris/obsidian-to-notion)](https://raw.githubusercontent.com/EasyChris/obsidian-to-notion/master/LICENSE)
[![Github all releases](https://img.shields.io/github/downloads/Easychris/obsidian-to-notion/total.svg)](https://GitHub.com/Easychris/obsidian-to-notion/releases/)
[![GitLab latest release](https://badgen.net/github/release/Easychris/obsidian-to-notion/)](https://github.com/Easychris/obsidian-to-notion/releases)
Share of obsidian to Notion [中文文档](README-zh.md)
Sharing files from Obsidian to Notion with a single click, and Obsidian will automatically add the Notion share link
@@ -160,7 +279,9 @@ You are welcome to offer it a star if it can benefit you.
![](./doc/1.gif)
# TODO
### [TODO Board](https://github.com/users/EasyChris/projects/3/views/1)
- [x] support for custom page banner
- [x] update the exsit page
- [x] support for mult language
@@ -169,41 +290,47 @@ You are welcome to offer it a star if it can benefit you.
- [x] support tags thank for [@jannikbuscha](https://github.com/jannikbuscha)
- [ ] transfer the bi-link format like [[]] into the format that Notion supports.
# How to use
## Install the plugin
### Marketplace download
Open obsidian setting -> Add plugin -> Search -> notion
![](https://afox-1256168983.cos.ap-shanghai.myqcloud.com/20220628214145.png)
### BRAT
Enter `BRAT` into the plugin market center to find it.
Add `EasyChris/obsidian-to-notion` to the list of BRAT plugins that have been installed.
Return to the plugin center and turn it on.
### Manual installation
```
cd YOUR_OBSIDIAN_FOLDER/.obsidian/plugins/
git clone https://github.com/EasyChris/obsidian-to-notion.git
```
## Apply Notion API
Official reference documentation: [https://developers.notion.com/docs](https://developers.notion.com/docs)
### Step 1: Create integration.
### Step 1: Create integration
Go to [https://www.notion.com/my-integrations](https://www.notion.com/my-integrations)
Once created, copy `secrets toekn`
![](https://files.readme.io/2ec137d-093ad49-create-integration.gif)
#### Note:
#### Note
database first custom name must be "Name", otherwise sync to notion will be failed
![](https://afox-1256168983.cos.ap-shanghai.myqcloud.com/20220618102029.png)
### Step 2: Share a database with your integration
Create a new page (with public permissions)
Create a new database in the page -> you need `full page database`
![](./doc/3.gif)
@@ -220,25 +347,25 @@ https://www.notion.so/myworkspace/a8aec43384f447ed84390e8e42c2e089?v=...
```
## Open the plugin configuration
Fill the configuration with the `NOTION_API_KEY` and `DATABASE_ID` you got
![](./doc/2.png)
## Upload file content to notion
Click the uploadCommand notion button
![](./doc/4.png)
A share link will be automatically generated after successful uploadCommand
![](./doc/5.png)
## Banner URL [option]
Banner url must be a image url like: https://i.imgur.com/xxx.jpg
Banner url must be a image url like: <https://i.imgur.com/xxx.jpg>
If you don't want to use banner, leave it blank
## Convert Tags [option]
Transfer the Obsidian tags to the Notion table.
It requires the column with the name 'Tags'.
![](./doc/7.png)
@@ -247,11 +374,11 @@ Add tags to your notion page
![](./doc/10.png)
* open plugin convert tags
- open plugin convert tags
![](./doc/8.png)
* add tags in the head
- add tags in the head
```markdown
---
@@ -262,7 +389,6 @@ this is test tags
```
```markdown
---
tags:
@@ -277,24 +403,20 @@ this is test tags
Thanks for [@jannikbuscha](https://github.com/jannikbuscha) contribution
## Notion ID [option]
Notion ID is the your notion site ID that you want to share the file to.
if you don't write it, notion will share to the default link like:
https://www.notion.so/myworkspace/a8aec43384f447ed84390
<https://www.notion.so/myworkspace/a8aec43384f447ed84390>
that visit this page need to redirect to your site url
if you write the Notion ID, it will share to the page link like:
https://your_user_name.notion.site/myworkspace/a8aec43384f447ed84390.
<https://your_user_name.notion.site/myworkspace/a8aec43384f447ed84390>.
The visiter don't need to redirect url.
## Sync image to Notion
To sync images to your oss or cos bucket, use the [Obsidian Image Auto Upload Plugin](https://github.com/renmu123/obsidian-image-auto-upload-plugin).
# Development
```
@@ -309,9 +431,6 @@ yarn dev
node update-version.js
./release.sh
```
```
# Thanks
@@ -329,3 +448,6 @@ node update-version.js
# License
GNU GPLv3
</details>

View File

@@ -1,58 +1,61 @@
import esbuild from "esbuild";
import process from "process";
import builtins from 'builtin-modules'
import builtins from "builtin-modules";
const banner =
`/*
const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;
const prod = (process.argv[2] === 'production');
const prod = process.argv[2] === "production";
const ctx = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ['src/main.ts'],
entryPoints: ["src/main.ts"],
bundle: true,
external: [
'obsidian',
'electron',
'@codemirror/autocomplete',
'@codemirror/closebrackets',
'@codemirror/collab',
'@codemirror/commands',
'@codemirror/comment',
'@codemirror/fold',
'@codemirror/gutter',
'@codemirror/highlight',
'@codemirror/history',
'@codemirror/language',
'@codemirror/lint',
'@codemirror/matchbrackets',
'@codemirror/panel',
'@codemirror/rangeset',
'@codemirror/rectangular-selection',
'@codemirror/search',
'@codemirror/state',
'@codemirror/stream-parser',
'@codemirror/text',
'@codemirror/tooltip',
'@codemirror/view',
...builtins
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/closebrackets",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/comment",
"@codemirror/fold",
"@codemirror/gutter",
"@codemirror/highlight",
"@codemirror/history",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/matchbrackets",
"@codemirror/panel",
"@codemirror/rangeset",
"@codemirror/rectangular-selection",
"@codemirror/search",
"@codemirror/state",
"@codemirror/stream-parser",
"@codemirror/text",
"@codemirror/tooltip",
"@codemirror/view",
...builtins,
],
format: 'cjs',
target: 'es2016',
platform: "node",
format: "cjs",
target: "es2016",
logLevel: "info",
sourcemap: prod ? false : 'inline',
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: 'main.js',
outfile: "main.js",
});
if (!prod) {
await ctx.watch();
} else {
await ctx.rebuild()
ctx.dispose().then(r => console.log('Build completed'), e => console.error(e));
await ctx.rebuild();
ctx.dispose().then(
(r) => console.log("Build completed"),
(e) => console.error(e),
);
}

View File

@@ -1,7 +1,7 @@
{
"id": "share-to-notionnext",
"name": "Share to NotionNext",
"version": "1.0.1",
"version": "2.4.0",
"minAppVersion": "0.0.1",
"description": "Shares obsidian md file to notion with notion api for NotionNext web deploy, originally created by EasyChris/obsidian-to-notion.",
"author": "EasyChris, jxpeng98",

View File

@@ -12,7 +12,7 @@ date: 2023-07-23 # default is today 默认是今天。 Format is YYYY-MM-DD
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
status: 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 默认是空,可直接删除

View File

@@ -1,6 +1,6 @@
{
"name": "share-to-notionnext",
"version": "1.0.1",
"version": "2.4.0",
"type": "module",
"description": "Shares obsidian md file to notion with notion api for NotionNext web deploy, originally created by EasyChris/obsidian-to-notion.",
"main": "main.js",
@@ -15,17 +15,18 @@
"devDependencies": {
"@types/node": "^20.5.7",
"@types/yaml-front-matter": "^4.1.3",
"@typescript-eslint/eslint-plugin": "^6.11.0",
"@typescript-eslint/parser": "^6.11.0",
"builtin-modules": "^3.2.0",
"esbuild": "0.19.2",
"obsidian": "latest",
"@typescript-eslint/eslint-plugin": "^6.16.0",
"@typescript-eslint/parser": "^6.16.0",
"builtin-modules": "^3.3.0",
"esbuild": "0.19.5",
"obsidian": "^1.6.6",
"tslib": "2.6.2",
"typescript": "5.2.2"
},
"dependencies": {
"@tryfabric/martian": "^1.2.4",
"@jxpeng98/martian": "^1.2.7",
"https-proxy-agent": "^7.0.2",
"node-fetch": "^3.3.2",
"process": "^0.11.10",
"remark-math": "^6.0.0",
"yaml": "^2.3.4",

View File

@@ -1,8 +1,9 @@
import { i18nConfig } from "src/lang/I18n";
import { Editor, MarkdownView } from "obsidian";
import { Editor, MarkdownView, setTooltip } from "obsidian";
import { FuzzySuggester, DatabaseList } from "./FuzzySuggester";
import { uploadCommandGeneral, uploadCommandNext } from "../upload/uploadCommand";
import { uploadCommandCustom, uploadCommandGeneral, uploadCommandNext } from "../upload/uploadCommand";
import ObsidianSyncNotionPlugin from "src/main";
import { DatabaseDetails } from "../ui/settingTabs";
interface Command {
@@ -21,26 +22,11 @@ export default class RibbonCommands {
constructor(plugin: ObsidianSyncNotionPlugin) {
this.plugin = plugin;
// Check if NextButton is true, then include the corresponding command
if (this.plugin.settings.NextButton) {
this.Ncommand.push({
id: "share-to-notionnext",
name: i18nConfig.CommandName, // Use the translated text from i18nConfig
editorCallback: async (editor: Editor, view: MarkdownView) => {
await uploadCommandNext(this.plugin, this.plugin.settings, this.plugin.app);
}
});
}
// iterate through the database detail
// Check if GeneralButton is true, then include the corresponding command
if (this.plugin.settings.GeneralButton) {
this.Ncommand.push({
id: "share-to-notion",
name: i18nConfig.CommandNameGeneral, // Use the translated text from i18nConfig
editorCallback: async (editor: Editor, view: MarkdownView) => {
await uploadCommandGeneral(this.plugin, this.plugin.settings, this.plugin.app);
}
});
for (let key in this.plugin.settings.databaseDetails) {
let dbDetails = this.plugin.settings.databaseDetails[key];
this.addCommandForDatabase(dbDetails);
}
// Register all the commands
@@ -77,24 +63,9 @@ export default class RibbonCommands {
this.Ncommand = [];
if (this.plugin.settings.NextButton) {
this.Ncommand.push({
id: "share-to-notionnext",
name: i18nConfig.CommandName, // Use the translated text from i18nConfig
editorCallback: async (editor: Editor, view: MarkdownView) => {
await uploadCommandNext(this.plugin, this.plugin.settings, this.plugin.app);
}
});
}
if (this.plugin.settings.GeneralButton) {
this.Ncommand.push({
id: "share-to-notion",
name: i18nConfig.CommandNameGeneral, // Use the translated text from i18nConfig
editorCallback: async (editor: Editor, view: MarkdownView) => {
await uploadCommandGeneral(this.plugin, this.plugin.settings, this.plugin.app);
}
});
for (let key in this.plugin.settings.databaseDetails) {
let dbDetails = this.plugin.settings.databaseDetails[key];
this.addCommandForDatabase(dbDetails);
}
this.Ncommand.forEach(command => {
@@ -107,4 +78,30 @@ export default class RibbonCommands {
);
});
}
private addCommandForDatabase(dbDetails: DatabaseDetails) {
// Example logic - adjust based on your specific requirements
let commandId = `share-to-${dbDetails.abName}`;
let commandName = `Share to ${dbDetails.fullName} (${dbDetails.abName})`; // or use a translated name
let editorCallback: (editor: Editor, view: MarkdownView) => Promise<void>;
if (dbDetails.format === 'next') {
editorCallback = async (editor, view) => {
await uploadCommandNext(this.plugin, this.plugin.settings, dbDetails, this.plugin.app);
};
} else if (dbDetails.format === 'general') {
editorCallback = async (editor, view) => {
await uploadCommandGeneral(this.plugin, this.plugin.settings, dbDetails, this.plugin.app);
};
}
else if (dbDetails.format === 'custom') {
editorCallback = async (editor, view) => {
await uploadCommandCustom(this.plugin, this.plugin.settings, dbDetails, this.plugin.app);
};
}
this.Ncommand.push({ id: commandId, name: commandName, editorCallback });
}
}

View File

@@ -1,109 +1,17 @@
import {en} from "./locale/en";
import {zh} from "./locale/zh";
import {ja} from "./locale/ja";
export const I18n: {[key: string]:any} = {
en: {
ribbonIcon: "Share to NotionNext",
GeneralSetting: "General information Settings",
CommandID: "share-to-notionnext",
CommandName: "Share to NotionNext Database",
CommandIDGeneral: "share-to-notion",
CommandNameGeneral: "Share to Notion General Database",
NotionNextButton: "NotionNext Sync",
NotionNextButtonDesc: "Open this option, Sync to NotionNext command will be displayed in the command palette (default: ON)",
NotionNextSettingHeader: "NotionNext Database Settings",
NotionAPI: "Notion API Token",
NotionAPIDesc: "Generate from https://www.notion.so/my-integrations",
NotionAPIText: "Enter your Notion API Token",
DatabaseID: "Database ID",
DatabaseIDText: "Enter your Database ID",
BannerUrl: "Banner url (optional)",
BannerUrlDesc:
"Default is empty, if you want to show a banner, please enter the url (like: https://minioapi.pjx.ac.cn/img1/2023/11/b7b40a0724e93b7d7ab494bb3b8a2da8.png)",
BannerUrlText: "Enter your banner url",
NotionUser: "Notion ID (username, optional)",
NotionUserDesc:
"Collect from share link likes:https://username.notion.site. Your notion id is [username]",
NotionUserText: "Enter your notion ID",
NotionGeneralSettingHeader: "General Notion Database Settings",
NotionGeneralButton: "Notion General Sync",
NotionGeneralButtonDesc: "Open this option, Sync to Notion General Database command will be displayed in the command palette (default: ON)",
NotionCustomTitle: "Customise title property",
NotionCustomTitleDesc: "Modify the column name of the Notion database (default: OFF)",
NotionCustomTitleName: "Preferred title name",
NotionCustomTitleNameDesc: "Enter the preferred title name for the first column of the Notion database (default: title)",
NotionCustomTitleText: "Enter the name",
NotYetFinish:
"Not finished. This function will be available in the next version",
PlaceHolder: "Enter database Name",
"notion-logo": "Share to NotionNext",
"sync-success": "Sync to NotionNext success: \n",
"sync-fail": "Sync to NotionNext fail: \n",
"open-notion": "Please open the file that needs to be synchronized",
"config-secrets-notion-api":
"Please set up the notion API in the settings tab.",
"config-secrets-database-id":
"Please set up the database id in the settings tab.",
"set-tags-fail":
"Set tags fail,please check the frontmatter of the file or close the tag switch in the settings tab.",
NNonMissing:
"The 'NNon' property is missing in the settings. Please set it up.",
"set-api-id":
"Please set up the notion API and database ID in the settings tab.",
NotionCustomSettingHeader: "Notion Custom Database Settings",
NotionCustomButton: "Notion Customised command switch",
NotionCustomButtonDesc: "Open this option, Sync to Notion Customised Database command will be displayed in the command palette",
},
zh: {
ribbonIcon: "分享到 NotionNext",
GeneralSetting: "通用设置",
CommandID: "share-to-notionnext",
CommandName: "分享到 NotionNext",
CommandIDGeneral: "share-to-notion",
CommandNameGeneral: "分享到 Notion 普通数据库",
NotionNextButton: "NotionNext 同步",
NotionNextButtonDesc: "打开此选项NotionNext 同步将显示在命令面板中(默认:开)",
NotionNextSettingHeader: "NotionNext 数据库参数设置",
NotionAPI: "Notion API 令牌",
NotionAPIDesc: "从 https://www.notion.so/my-integrations 生成",
NotionAPIText: "输入你的 Notion API 令牌",
DatabaseID: "数据库 ID",
DatabaseIDText: "输入你的数据库 ID",
BannerUrl: "封面图片地址(可选)",
BannerUrlDesc:
"默认为空如果你想显示封面图片请输入图片地址例如https://minioapi.pjx.ac.cn/img1/2023/11/b7b40a0724e93b7d7ab494bb3b8a2da8.png",
BannerUrlText: "输入你的封面图片地址",
NotionUser: "Notion ID用户名可选",
NotionUserDesc:
"数据库分享链接类似https://username.notion.site/。你的 Notion ID 是 [username]",
NotionUserText: "输入你的 Notion ID",
NotionGeneralSettingHeader: "普通 Notion 数据库设置",
NotionGeneralButton: "普通数据库同步",
NotionGeneralButtonDesc: "打开此选项,同步到普通数据库命令将显示在命令面板中(默认:开)",
NotionCustomTitle: "修改 Notion 数据库表头开关",
NotionCustomTitleDesc: "自定义Notion 数据库第一列表头名(默认:关)",
NotionCustomTitleName: "想要修改的表头名",
NotionCustomTitleNameDesc: "输入你想要修改的notion数据库的表头名默认title",
NotionCustomTitleText: "输入表头名",
NotYetFinish: "未完成。此功能将在之后版本中提供",
PlaceHolder: "输入数据库名称",
"notion-logo": "分享到NotionNext",
"sync-success": "同步到NotionNext成功:\n",
"sync-fail": "同步到NotionNext失败: \n",
"open-file": "请打开需要同步的文件",
"config-secrets-notion-api": "请在插件设置中添加notion API",
"config-secrets-database-id": "请在插件设置中添加database id",
"set-tags-fail":
"设置标签失败,请检查文件的frontmatter,或者在插件设置中关闭设置tags开关",
NNonMissing: "未设置'NNon'属性请在插件设置中选择NotionNext数据库。",
"set-api-id": "请在插件设置中设置notion API和database ID",
NotionCustomSettingHeader: "Notion 自定义数据库设置",
NotionCustomButton: "Notion 自定义数据库同步命令开关",
NotionCustomButtonDesc: "打开此选项,同步到自定义数据库命令将显示在命令面板中",
},
en,
zh,
ja,
};
export const I18nConfig = (lang: any): any => {
export const I18nConfig = (lang: string): any => {
return I18n[lang];
};
export const i18nConfig = I18nConfig(
window.localStorage.getItem("language") || "en",
); // Export i18nConfig
const storedLanguage = window.localStorage.getItem("language");
export const i18nConfig = I18n[storedLanguage || window.navigator.language.split('-')[0] || "en"];

82
src/lang/locale/en.ts Normal file
View File

@@ -0,0 +1,82 @@
export const en = {
databaseFormat: "Database Format",
databaseFormatDesc: "Select the database format you want to sync to NotionNext or General",
databaseNext: "NotionNext",
databaseGeneral: "General",
databaseCustom: "Custom",
databaseFullName: "Database Full Name",
databaseFullNameDesc: "Please give a full name for your database",
databaseFullNameText: "Enter your database full name",
databaseAbbreviateName: "Database Abbreviate Name",
databaseAbbreviateNameDesc: "Please give a nick name for your database",
databaseAbbreviateNameText: "Enter your database nick name",
ribbonIcon: "Share to NotionNext",
GeneralSetting: "General information Settings",
CommandID: "share-to-notionnext",
CommandName: "Share to NotionNext Database",
CommandIDGeneral: "share-to-notion",
CommandNameGeneral: "Share to Notion General Database",
NotionNextButton: "NotionNext Sync",
NotionNextButtonDesc: "Open this option, Sync to NotionNext command will be displayed in the command palette (default: ON)",
NotionNextSettingHeader: "NotionNext Database Settings",
NotionAPI: "Notion API Token",
NotionAPIDesc: "Generate from https://www.notion.so/my-integrations",
NotionAPIText: "Enter your Notion API Token",
DatabaseID: "Database ID",
DatabaseIDDesc: "Collect from the top-right Share --> Publish",
DatabaseIDText: "Enter your Database ID",
BannerUrl: "Banner url (optional)",
BannerUrlDesc:
"Default is empty, if you want to show a banner, please enter the url (like: https://abc.com/b.png)",
BannerUrlText: "Enter your banner url",
NotionUser: "Notion ID (username, optional)",
NotionUserDesc:
"Collect from share link likes:https://username.notion.site. Your notion id is [username]",
NotionUserText: "Enter your notion ID",
NotionLinkDisplay: "Notion Link Display",
NotionLinkDisplayDesc: "Default is ON, if you want to hide the link in the front matter, please turn it off",
NotionGeneralSettingHeader: "General Notion Database Settings",
NotionGeneralButton: "Notion General Sync",
NotionGeneralButtonDesc: "Open this option, Sync to Notion General Database command will be displayed in the command palette (default: ON)",
NotionTagButton: "Notion Tags Sync",
NotionTagButtonDesc: "Sync Tags to Notion General Database (default: ON)",
NotionCustomTitle: "Customise title property",
NotionCustomTitleDesc: "Modify the column name of the Notion database (default: OFF)",
NotionCustomTitleName: "Preferred title name",
NotionCustomTitleNameDesc: "Enter the preferred title name for the first column of the Notion database (default: title)",
NotionCustomTitleText: "Enter the name",
NotionCustomValues: "Customise values property",
NotionCustomValuesDesc: "Modify the column name of the Notion databaseone per line",
NotionCustomValuesText: "Enter all properties that you want to sync",
NotYetFinish: "Not finished. This function will be available in the next version",
PlaceHolder: "Enter database Name",
"notion-logo": "Share to NotionNext",
"sync-preffix": "Sync to ",
"sync-success": "success",
"sync-fail": "failed",
"open-notion": "Please open the file that needs to be synchronized",
"config-secrets-notion-api":
"Please set up the notion API in the settings tab.",
"config-secrets-database-id":
"Please set up the database id in the settings tab.",
"set-tags-fail":
"Set tags fail,please check the frontmatter of the file or close the tag switch in the settings tab.",
NNonMissing:
"The 'NNon' property is missing in the settings. Please set it up.",
"set-api-id":
"Please set up the notion API and database ID in the settings tab.",
NotionCustomSettingHeader: "Notion Custom Database Settings",
NotionCustomButton: "Notion Customised command switch",
NotionCustomButtonDesc: "Open this option, Sync to Notion Customised Database command will be displayed in the command palette",
CustomPropertyName: "Property Name",
CustomPropertyFirstColumn: "Title Column",
CustomPropertyFirstColumnDesc: "The title of the page, must be the first property",
CustomProperty: "Property",
AddCustomProperty: "Add Custom Property",
AddNewProperty: "Add New Property",
AddNewPropertyDesc: "Add new property match with your notion database",
CopyErrorMessage: "Auto copy failed, please copy it manually",
BlockUploaded: "All blocks uploaded",
ExtraBlockUploaded: "Extra blocks uploaded",
CheckConsole: "Check the console for more information \n opt+cmd+i/ctrl+shift+i",
}

74
src/lang/locale/ja.ts Normal file
View File

@@ -0,0 +1,74 @@
export const ja = {
databaseFormat: "データベース形式",
databaseFormatDesc: "同期したいデータベース形式を選択してください",
databaseNext: "NotionNext",
databaseGeneral: "一般的なNotion",
databaseCustom: "カスタム",
databaseFullName: "データベースの全称",
databaseFullNameDesc: "データベースの全称を入力してください",
databaseFullNameText: "データベースの全称を入力",
databaseAbbreviateName: "データベースの略称",
databaseAbbreviateNameDesc: "データベースの略称を入力してください",
databaseAbbreviateNameText: "データベースの略称を入力",
ribbonIcon: "NotionNextで共有",
GeneralSetting: "一般設定",
CommandID: "share-to-notionnext",
CommandName: "NotionNextデータベースに共有",
CommandIDGeneral: "share-to-notion",
CommandNameGeneral: "一般的なNotionデータベースに共有",
NotionNextButton: "NotionNext同期",
NotionNextButtonDesc: "このオプションを開くと、NotionNext同期コマンドがコマンドパレットに表示されますデフォルトON",
NotionNextSettingHeader: "NotionNextデータベース設定",
NotionAPI: "Notion API トークン",
NotionAPIDesc: "https://www.notion.so/my-integrations から生成してください",
NotionAPIText: "Notion API トークンを入力",
DatabaseID: "データベースID",
DatabaseIDDesc: "右上の共有 --> 公開から取得してください",
DatabaseIDText: "データベースIDを入力",
BannerUrl: "バナーのURL任意",
BannerUrlDesc: "デフォルトは空白です。バナーを表示したい場合は、URLを入力してくださいhttps://abc.com/b.png",
BannerUrlText: "バナーのURLを入力",
NotionUser: "Notion IDユーザー名、任意",
NotionUserDesc: "共有リンクから取得https://username.notion.site。Notion IDは[username]です",
NotionUserText: "Notion IDを入力",
NotionLinkDisplay: "Notionリンク表示",
NotionLinkDisplayDesc: "デフォルトはONです。front matterにリンクを非表示にしたい場合は、オフにしてください",
NotionGeneralSettingHeader: "一般的なNotionデータベース設定",
NotionGeneralButton: "一般的なNotion同期",
NotionGeneralButtonDesc: "このオプションを開くと、一般的なNotionデータベース同期コマンドがコマンドパレットに表示されますデフォルトON",
NotionTagButton: "Notionタグ同期",
NotionTagButtonDesc: "タグを一般的なNotionデータベースに同期デフォルトON",
NotionCustomTitle: "タイトルのカスタマイズ",
NotionCustomTitleDesc: "Notionデータベースの列名を変更デフォルトOFF",
NotionCustomTitleName: "希望のタイトル名",
NotionCustomTitleNameDesc: "Notionデータベースの最初の列のための希望のタイトル名を入力デフォルトtitle",
NotionCustomTitleText: "名前を入力",
NotionCustomValues: "値のカスタマイズ",
NotionCustomValuesDesc: "Notionデータベースの列名を変更、1行に1つ",
NotionCustomValuesText: "同期したいすべてのプロパティを入力",
NotYetFinish: "未完了。この機能は次のバージョンで利用可能になります",
PlaceHolder: "データベース名を入力",
"notion-logo": "NotionNextで共有",
"sync-success": "NotionNextへの同期に成功\n",
"sync-fail": "NotionNextへの同期に失敗\n",
"open-notion": "同期が必要なファイルを開いてください",
"config-secrets-notion-api": "設定タブでNotion APIを設定してください",
"config-secrets-database-id": "設定タブでデータベースIDを設定してください",
"set-tags-fail": "タグの設定に失敗。ファイルのfrontmatterを確認するか、設定タブでタグのスイッチをオフにしてください",
NNonMissing: "設定に 'NNon' プロパティがありません。設定してください",
"set-api-id": "設定タブでNotion APIおよびデータベースIDを設定してください",
NotionCustomSettingHeader: "Notionカスタムデータベース設定",
NotionCustomButton: "Notionカスタマイズコマンドの切り替え",
NotionCustomButtonDesc: "このオプションを開くと、Notionカスタムデータベース同期コマンドがコマンドパレットに表示されます",
CustomPropertyName: "カスタムプロパティ名",
CustomPropertyFirstColumn: "最初の列のカスタムプロパティ名",
CustomPropertyFirstColumnDesc: "最初の列のカスタムプロパティ名を入力してください",
CustomProperty: "カスタムプロパティ",
AddCustomProperty: "カスタムプロパティを追加",
AddNewProperty: "新しいプロパティを追加",
AddNewPropertyDesc: "新しいプロパティを追加してください",
CopyErrorMessage: "自動コピーに失敗しました",
BlockUploaded: "ブロックがアップロードされました",
ExtraBlockUploaded: "追加ブロックがアップロードされました",
CheckConsole: "詳細情報を確認するには、コンソールを開いてください \n opt+cmd+i/ctrl+shift+i",
};

77
src/lang/locale/zh.ts Normal file
View File

@@ -0,0 +1,77 @@
export const zh = {
databaseFormat: "数据库格式",
databaseFormatDesc: "选择你想要同步的数据库格式Next 或者 普通",
databaseNext: "NotionNext",
databaseGeneral: "普通",
databaseCustom: "自定义",
databaseFullName: "数据库全称",
databaseFullNameDesc: "给你的数据库起一个全称",
databaseFullNameText: "输入你的数据库全称",
databaseAbbreviateName: "数据库简称",
databaseAbbreviateNameDesc: "给你的数据库起一个简称",
databaseAbbreviateNameText: "输入你的数据库简称",
ribbonIcon: "分享到 NotionNext",
GeneralSetting: "通用设置",
CommandID: "share-to-notionnext",
CommandName: "分享到 NotionNext",
CommandIDGeneral: "share-to-notion",
CommandNameGeneral: "分享到 Notion 普通数据库",
NotionNextButton: "NotionNext 同步",
NotionNextButtonDesc: "打开此选项NotionNext 同步将显示在命令面板中(默认:开)",
NotionNextSettingHeader: "NotionNext 数据库参数设置",
NotionAPI: "Notion API 令牌",
NotionAPIDesc: "从 https://www.notion.so/my-integrations 生成",
NotionAPIText: "输入你的 Notion API 令牌",
DatabaseID: "数据库 ID",
DatabaseIDDesc: "从右上角的分享 --> 发布中获取",
DatabaseIDText: "输入你的数据库 ID",
BannerUrl: "封面图片地址(可选)",
BannerUrlDesc:
"默认为空如果你想显示封面图片请输入图片地址例如https://abc.com/b.png",
BannerUrlText: "输入你的封面图片地址",
NotionUser: "Notion ID用户名可选",
NotionUserDesc:
"数据库分享链接类似https://username.notion.site/。你的 Notion ID 是 [username]",
NotionUserText: "输入你的 Notion ID",
NotionLinkDisplay: "Notion 链接显示",
NotionLinkDisplayDesc: "默认开启如果你不想在front matter中显示链接请关闭",
NotionGeneralSettingHeader: "普通 Notion 数据库设置",
NotionGeneralButton: "普通数据库同步",
NotionGeneralButtonDesc: "打开此选项,同步到普通数据库命令将显示在命令面板中(默认:开)",
NotionTagButton: "标签同步开关",
NotionTagButtonDesc: "将标签同步到普通数据库(默认:开)",
NotionCustomTitle: "修改 Notion 数据库表头开关",
NotionCustomTitleDesc: "自定义Notion 数据库第一列表头名(默认:关)",
NotionCustomTitleName: "想要修改的表头名",
NotionCustomTitleNameDesc: "输入你想要修改的notion数据库的表头名默认title",
NotionCustomTitleText: "输入表头名",
NotionCustomValues: "自定义Notion 数据库表头",
NotionCustomValuesDesc: "自定义Notion 数据库表头,每行一个",
NotionCustomValuesText: "输入你想要同步的所有属性",
NotYetFinish: "未完成。此功能将在之后版本中提供",
PlaceHolder: "输入数据库名称",
"notion-logo": "分享到NotionNext",
"sync-success": "同步到NotionNext成功:\n",
"sync-fail": "同步到NotionNext失败: \n",
"open-file": "请打开需要同步的文件",
"config-secrets-notion-api": "请在插件设置中添加notion API",
"config-secrets-database-id": "请在插件设置中添加database id",
"set-tags-fail":
"设置标签失败,请检查文件的frontmatter,或者在插件设置中关闭设置tags开关",
NNonMissing: "未设置'NNon'属性请在插件设置中选择NotionNext数据库。",
"set-api-id": "请在插件设置中设置notion API和database ID",
NotionCustomSettingHeader: "Notion 自定义数据库设置",
NotionCustomButton: "Notion 自定义数据库同步命令开关",
NotionCustomButtonDesc: "打开此选项,同步到自定义数据库命令将显示在命令面板中",
CustomPropertyName: "自定义属性名",
CustomPropertyFirstColumn: "第一列属性名",
CustomPropertyFirstColumnDesc: "第一列必须为标题属性名",
CustomProperty: "自定义属性",
AddCustomProperty: "添加自定义属性",
AddNewProperty: "添加新属性",
AddNewPropertyDesc: "添加一个和Notion数据库匹配的新属性",
CopyErrorMessage: "复制链接失败,请手动复制",
BlockUploaded: "所有内容已成功上传",
ExtraBlockUploaded: "额外内容已成功上传",
CheckConsole: "opt+cmd+i/ctrl+shift+i\n打开控制台查看更多信息",
}

View File

@@ -4,7 +4,7 @@ import { Upload2NotionGeneral } from "src/upload/upload_general/Upload2NotionGen
import { Upload2NotionNext } from "src/upload/upload_next/Upload2NotionNext";
import { i18nConfig } from "src/lang/I18n";
import ribbonCommands from "src/commands/NotionCommands";
import { ObsidianSettingTab, PluginSettings, DEFAULT_SETTINGS } from "src/ui/settingTabs";
import { ObsidianSettingTab, PluginSettings, DEFAULT_SETTINGS, DatabaseDetails } from "src/ui/settingTabs";
// Remember to rename these classes and interfaces!
@@ -55,6 +55,33 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
await this.saveData(this.settings);
}
async addDatabaseDetails(dbDetails: DatabaseDetails) {
this.settings.databaseDetails = {
...this.settings.databaseDetails,
[dbDetails.abName]: dbDetails,
};
await this.saveSettings();
}
async deleteDatabaseDetails(dbDetails: DatabaseDetails) {
delete this.settings.databaseDetails[dbDetails.abName];
await this.saveSettings();
}
async updateDatabaseDetails(dbDetails: DatabaseDetails) {
// delete the old database details
delete this.settings.databaseDetails[dbDetails.abName];
this.settings.databaseDetails = {
...this.settings.databaseDetails,
[dbDetails.abName]: dbDetails,
};
await this.saveSettings();
}
}

64
src/ui/DeleteModal.ts Normal file
View File

@@ -0,0 +1,64 @@
import { App, ButtonComponent, Modal, Setting } from "obsidian";
import { DatabaseDetails, ObsidianSettingTab } from "./settingTabs";
import ObsidianSyncNotionPlugin from "../main";
export class DeleteModal extends Modal {
data: Record<string, any> = {
deleted: false,
}
plugin: ObsidianSyncNotionPlugin;
settingTab: ObsidianSettingTab;
dbDetails: DatabaseDetails
constructor(app: App, plugin: ObsidianSyncNotionPlugin, settingTab: ObsidianSettingTab, dbDetails: DatabaseDetails) {
super(app);
this.plugin = plugin;
this.settingTab = settingTab;
this.dbDetails = dbDetails;
}
display() {
this.containerEl.addClass("delete-modal");
this.titleEl.setText('Delete Database');
let { contentEl } = this;
contentEl.empty();
const deleteDiv = contentEl.createDiv('delete-div');
deleteDiv.createEl('h4', { text: 'Are you sure you want to delete the following database?' });
deleteDiv.createEl('h2', { text: this.dbDetails.fullName + ' (' + this.dbDetails.abName + ', ' + this.dbDetails.format + ')' });
// add delete button
let footerEl = contentEl.createDiv('save-button');
let deleteButton = new Setting(footerEl)
deleteButton
.addButton((button: ButtonComponent): ButtonComponent => {
return button
.setTooltip("Delete")
.setIcon("trash")
.onClick(async () => {
this.data.deleted = true;
this.close();
})
});
deleteButton.addExtraButton((button) => {
return button
.setTooltip('Cancel')
.setIcon('cross')
.onClick(() => {
this.data.deleted = false;
this.close();
});
});
}
onOpen() {
this.display();
}
}

349
src/ui/EditModal.ts Normal file
View File

@@ -0,0 +1,349 @@
import { App, ButtonComponent, Setting } from "obsidian";
import { customProperty, SettingModal } from "./settingModal";
import ObsidianSyncNotionPlugin from "../main";
import { DatabaseDetails, ObsidianSettingTab } from "./settingTabs";
import { i18nConfig } from "../lang/I18n";
export class EditModal extends SettingModal {
propertyLines: Setting[] = []; // Store all property line settings
[key: string]: any; // Index signature
dataTemp: Record<string, any> = {
databaseFormatTemp: '',
// databaseFormatTempInd: false,
databaseFullNameTemp: '',
// databaseFullNameTempInd: false,
databaseAbbreviateNameTemp: '',
// databaseAbbreviateNameTempInd: false,
notionAPITemp: '',
// notionAPITempInd: false,
databaseIDTemp: '',
// databaseIDTempInd: false,
tagButtonTemp: false,
// tagButtonTempInd: false,
customTitleButtonTemp: false,
// customTitleButtonTempInd: false,
customTitleNameTemp: '',
customPropertiesTemp: [],
// customTitleNameTempInd: false,
// customValues: '',
savedTemp: false,
savedTempInd: false,
};
dataPrev: Record<string, any> = {
databaseFormatPrev: '',
// databaseFormatPrevInd: false,
databaseFullNamePrev: '',
// databaseFullNamePrevInd: false,
databaseAbbreviateNamePrev: '',
// databaseAbbreviateNamePrevInd: false,
notionAPIPrev: '',
// notionAPIPrevInd: false,
databaseIDPrev: '',
// databaseIDPrevInd: false,
tagButtonPrev: false,
// tagButtonPrevInd: false,
customTitleButtonPrev: false,
// customTitleButtonPrevInd: false,
customTitleNamePrev: '',
customPropertiesPrev: [],
// customTitleNamePrevInd: false,
// customValues: '',
savedPrev: false,
savedPrevInd: false,
};
plugin: ObsidianSyncNotionPlugin;
settingTab: ObsidianSettingTab;
dbDetails: DatabaseDetails;
constructor(app: App, plugin: ObsidianSyncNotionPlugin, settingTab: ObsidianSettingTab, dbDetails: DatabaseDetails) {
super(app, plugin, settingTab);
this.plugin = plugin;
this.settingTab = settingTab;
if (dbDetails) {
// Temp details
this.dataTemp.databaseFormatTemp = dbDetails.format;
this.dataTemp.databaseFullNameTemp = dbDetails.fullName;
this.dataTemp.databaseAbbreviateNameTemp = dbDetails.abName;
this.dataTemp.notionAPITemp = dbDetails.notionAPI;
this.dataTemp.databaseIDTemp = dbDetails.databaseID;
this.dataTemp.tagButtonTemp = dbDetails.tagButton;
this.dataTemp.customTitleButtonTemp = dbDetails.customTitleButton;
this.dataTemp.customTitleNameTemp = dbDetails.customTitleName;
this.dataTemp.customPropertiesTemp = dbDetails.customProperties.map(prop => ({ ...prop })); // Ensure deep copy
// this.dataTemp.customValues = dbDetails.customValues;
this.dataTemp.savedTemp = dbDetails.saved;
// Prev details
this.dataPrev.databaseFormatPrev = dbDetails.format;
this.dataPrev.databaseFullNamePrev = dbDetails.fullName;
this.dataPrev.databaseAbbreviateNamePrev = dbDetails.abName;
this.dataPrev.notionAPIPrev = dbDetails.notionAPI;
this.dataPrev.databaseIDPrev = dbDetails.databaseID;
this.dataPrev.tagButtonPrev = dbDetails.tagButton;
this.dataPrev.customTitleButtonPrev = dbDetails.customTitleButton;
this.dataPrev.customTitleNamePrev = dbDetails.customTitleName;
this.dataPrev.customPropertiesPrev = dbDetails.customProperties.map(prop => ({ ...prop })); // Ensure deep copy
// this.dataTemp.customValues = dbDetails.customValues;
this.dataPrev.savedPrev = dbDetails.saved;
}
}
display(): void {
this.containerEl.addClass("edit-modal");
this.titleEl.setText('Edit Database');
let { contentEl } = this;
contentEl.empty();
const editDiv = contentEl.createDiv('edit-div');
const nextTabs = contentEl.createDiv('next-tabs');
new Setting(editDiv)
.setName(i18nConfig.databaseFormat)
.setDesc(i18nConfig.databaseFormatDesc)
.addDropdown((component) => {
component
.addOption('none', '')
.addOption('general', i18nConfig.databaseGeneral)
.addOption('next', i18nConfig.databaseNext)
.addOption('custom', i18nConfig.databaseCustom)
.setValue(this.dataTemp.databaseFormatTemp)
.onChange(async (value) => {
this.dataTemp.databaseFormatTemp = value;
nextTabs.empty();
this.updateContentBasedOnSelection(value, nextTabs);
});
// Initialize content based on the current dropdown value
this.updateContentBasedOnSelection(this.dataTemp.databaseFormatTemp, nextTabs);
});
// add save button
let footerEl = contentEl.createDiv('save-button');
let saveButton = new Setting(footerEl)
saveButton.addButton((button: ButtonComponent) => {
return button
.setTooltip('Save')
.setIcon('checkmark')
.onClick(async () => {
this.dataTemp.savedTempInd = true;
this.dataTemp.savedTemp = true;
// console.log(this.dataTemp);
// console.log(this.dataPrev);
this.close();
});
}
);
saveButton.addExtraButton((button) => {
return button
.setTooltip('Cancel')
.setIcon('cross')
.onClick(() => {
// console.log(this.dataTemp);
// console.log(this.dataPrev);
this.close();
});
}
);
}
onOpen(): void {
this.display()
}
updateContentBasedOnSelection(value: string, nextTabs: HTMLElement): void {
// Clear existing content
nextTabs.empty();
// Generate content based on the selected value
if (value === 'general') {
nextTabs.createEl('h3', { text: i18nConfig.NotionGeneralSettingHeader });
// add full name
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.dataTemp.databaseFullNameTemp, 'dataTemp', 'databaseFullNameTemp')
// add abbreviate name
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.dataTemp.databaseAbbreviateNameTemp, 'dataTemp', 'databaseAbbreviateNameTemp')
// tag button
this.createSettingEl(nextTabs, i18nConfig.NotionTagButton, i18nConfig.NotionTagButtonDesc, 'toggle', i18nConfig.NotionCustomTitleText, this.dataTemp.tagButtonTemp, 'dataTemp', 'tagButtonTemp')
// add custom title button
new Setting(nextTabs)
.setName(i18nConfig.NotionCustomTitle)
.setDesc(i18nConfig.NotionCustomTitleDesc)
.addToggle((toggle) =>
toggle
.setValue(this.dataTemp.customTitleButtonTemp)
.onChange(async (value) => {
this.dataTemp.customTitleButtonTemp = value;
this.updateSettingEl(CustomNameEl, value)
})
);
// add custom title name
const CustomNameEl = this.createStyleDiv('custom-name', (this.dataTemp.customTitleButtonTemp), nextTabs);
this.createSettingEl(CustomNameEl, i18nConfig.NotionCustomTitleName, i18nConfig.NotionCustomTitleNameDesc, 'text', i18nConfig.NotionCustomTitleText, this.dataTemp.customTitleNameTemp, 'dataTemp', 'customTitleNameTemp')
// add api key
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.dataTemp.notionAPITemp, 'dataTemp', 'notionAPITemp')
// add database id
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.dataTemp.databaseIDTemp, 'dataTemp', 'databaseIDTemp')
} else if (value === 'next') {
nextTabs.createEl('h3', { text: i18nConfig.NotionNextSettingHeader });
// add full name
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.dataTemp.databaseFullNameTemp, 'dataTemp', 'databaseFullNameTemp')
// add abbreviate name
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.dataTemp.databaseAbbreviateNameTemp, 'dataTemp', 'databaseAbbreviateNameTemp')
// add api key
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.dataTemp.notionAPITemp, 'dataTemp', 'notionAPITemp')
// add database id
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.dataTemp.databaseIDTemp, 'dataTemp', 'databaseIDTemp')
} else if (value === 'custom') {
nextTabs.createEl('h3', { text: i18nConfig.NotionCustomSettingHeader });
// add full name
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.dataTemp.databaseFullNameTemp, 'dataTemp', 'databaseFullNameTemp')
// add abbreviate name
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.dataTemp.databaseAbbreviateNameTemp, 'dataTemp', 'databaseAbbreviateNameTemp')
// add api key
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.dataTemp.notionAPITemp, 'dataTemp', 'notionAPITemp')
// add database id
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.dataTemp.databaseIDTemp, 'dataTemp', 'databaseIDTemp')
// add custom properties
this.initializePropertyLines(nextTabs, this.dataTemp.customPropertiesTemp);
}
}
initializePropertyLines(containerEl: HTMLElement, properties: customProperty[]): void {
if (!containerEl) {
console.error('Failed to initialize property lines: containerEl is null');
return;
}
new Setting(containerEl)
.setName("Add New Property")
.setDesc("Click to add a new property")
.addButton(button => {
return button
.setButtonText('Add')
.setTooltip('Add one more property')
.onClick(() => {
this.createPropertyLine(containerEl, properties);
});
});
properties.forEach(property => {
this.updatePropertyLine(containerEl, property, properties);
});
}
updatePropertyLine(containerEl: HTMLElement, property: customProperty, properties: customProperty[]) {
let isExistingProperty = property !== null;
const propertyIndex = isExistingProperty ? property.index : properties.length;
const propertyLine = new Setting(containerEl)
.setName(propertyIndex === 0 ? i18nConfig.CustomPropertyFirstColumn : `${i18nConfig.CustomProperty} ${propertyIndex}`)
.setDesc(propertyIndex === 0 ? i18nConfig.CustomPropertyFirstColumnDesc : "");
propertyLine.addText(text => {
text.setPlaceholder(i18nConfig.CustomPropertyName)
.setValue(isExistingProperty ? property.customName : "")
.onChange(value => {
const actualIndex = properties.findIndex(p => p.index === propertyIndex);
if (actualIndex !== -1) {
properties[actualIndex].customName = value;
} else {
properties.push({ customName: value, customType: '', index: propertyIndex });
isExistingProperty = true;
}
});
});
propertyLine.addDropdown((dropdown) => {
const options: Record<string, string> = {
'rich_text': 'Text',
'number': 'Number',
'select': 'Select',
'multi_select': 'Multi-Select',
'date': 'Date',
'files': 'Files & Media',
'checkbox': 'Checkbox',
'url': 'URL',
'email': 'Email',
'phone_number': 'Phone Number'
};
if (propertyIndex === 0) {
dropdown.addOption('title', 'Title');
} else {
Object.keys(options).forEach(key => {
dropdown.addOption(key, options[key]);
});
}
dropdown.setValue(isExistingProperty ? property.customType : "")
.onChange(value => {
const actualIndex = properties.findIndex(p => p.index === propertyIndex);
if (actualIndex !== -1) {
properties[actualIndex].customType = value;
} else if (!isExistingProperty) {
properties.push({ customName: '', customType: value, index: propertyIndex });
isExistingProperty = true; // Update the flag to prevent re-adding
}
});
});
if (propertyIndex > 0) {
propertyLine.addButton(button => {
return button
.setTooltip("Delete")
.setIcon("trash")
.onClick(() => {
console.log('Deleting property', properties[propertyIndex]);
this.deleteProperty(propertyIndex, properties);
});
});
}
this.propertyLines.push(propertyLine);
this.updatePropertyLines();
}
createStyleDiv(className: string, commandValue: boolean = false, parentEl: HTMLElement): HTMLDivElement {
return super.createStyleDiv(className, commandValue, parentEl);
}
updateSettingEl(element: HTMLElement, commandValue: boolean) {
super.updateSettingEl(element, commandValue);
}
createSettingEl(contentEl: HTMLElement, name: string, desc: string, type: string, placeholder: string, holderValue: any, dataRecord: string, settingsKey: string): Setting {
return super.createSettingEl(contentEl, name, desc, type, placeholder, holderValue, dataRecord, settingsKey);
}
}

178
src/ui/PreviewModal.ts Normal file
View File

@@ -0,0 +1,178 @@
import { App, ExtraButtonComponent, Modal, Notice, Setting } from "obsidian";
import ObsidianSyncNotionPlugin from "../main";
import { DatabaseDetails, ObsidianSettingTab } from "./settingTabs";
import { customProperty } from "./settingModal";
import { i18nConfig } from "../lang/I18n";
export class PreviewModal extends Modal {
plugin: ObsidianSyncNotionPlugin;
settingTab: ObsidianSettingTab;
dbDetails: DatabaseDetails;
constructor(app: App, plugin: ObsidianSyncNotionPlugin, settingTab: ObsidianSettingTab, dbDetails: DatabaseDetails) {
super(app);
this.plugin = plugin;
this.settingTab = settingTab;
this.dbDetails = dbDetails;
}
display(): void {
this.containerEl.addClass('preview-modal')
this.titleEl.setText('Preview')
let { contentEl } = this;
const previewEl = contentEl.createDiv('preview-content')
const dbFormatEl = new Setting(previewEl)
dbFormatEl
.setName('Database Format')
.addText(text => text
.setValue(this.dbDetails.format)
.setDisabled(true));
const dbFullEl = new Setting(previewEl)
dbFullEl
.setName('Database Full Name')
.addText(text => text
.setValue(this.dbDetails.fullName)
.setDisabled(true));
const dbAbbrEl = new Setting(previewEl)
dbAbbrEl
.setName('Database Abbreviate Name')
.addText(text => text
.setValue(this.dbDetails.abName)
.setDisabled(true));
// .controlEl.createEl('p', { text: this.dbDetails.abName })
// Setting for toggle and copy buttons
new Setting(previewEl)
.setName('Notion API Key')
.addExtraButton((button: ExtraButtonComponent) => {
let isApiKeyVisible = false;
return button
.setTooltip('Toggle API Key Visibility')
.setIcon('eye')
.onClick(() => {
isApiKeyVisible = !isApiKeyVisible;
button.setIcon(isApiKeyVisible ? 'eye-off' : 'eye');
apiKeySetting.settingEl.style.display = isApiKeyVisible ? '' : 'none'; // Toggle visibility
});
})
// Setting for displaying the API key
const apiKeySetting = new Setting(previewEl)
apiKeySetting.infoEl.createEl('p', { text: this.dbDetails.notionAPI })
apiKeySetting
.addExtraButton((button: ExtraButtonComponent) => {
return button
.setTooltip('Copy API Key')
.setIcon('clipboard')
.onClick(() => {
navigator.clipboard.writeText(this.dbDetails.notionAPI)
new Notice('API Key copied to clipboard');
});
});
apiKeySetting.settingEl.style.display = 'none'; // Hide initially
new Setting(previewEl)
.setName('Database ID')
.addExtraButton((button: ExtraButtonComponent) => {
let isDbIdVisible = false;
return button
.setTooltip('Toggle Database ID Visibility')
.setIcon('eye')
.onClick(() => {
isDbIdVisible = !isDbIdVisible;
button.setIcon(isDbIdVisible ? 'eye-off' : 'eye');
dbIdSetting.settingEl.style.display = isDbIdVisible ? '' : 'none'; // Toggle visibility
});
})
const dbIdSetting = new Setting(previewEl)
dbIdSetting.infoEl.createEl('p', { text: this.dbDetails.databaseID })
dbIdSetting
.addExtraButton((button: ExtraButtonComponent) => {
return button
.setTooltip('Copy Database ID')
.setIcon('clipboard')
.onClick(() => {
navigator.clipboard.writeText(this.dbDetails.databaseID)
new Notice('Database ID copied to clipboard');
});
});
dbIdSetting.settingEl.style.display = 'none'; // Hide initially
// Preview the custom properties
if (this.dbDetails.format === 'custom') {
const customPrv = previewEl.createDiv("custom-tabs");
this.previewPropertyLine(previewEl, this.dbDetails.customProperties);
}
}
onOpen() {
this.display()
}
previewPropertyLine(containerEl: HTMLElement, properties: customProperty[]): void {
properties.forEach((property, index) => {
const propertyLine = new Setting(containerEl)
.setName(index === 0 ? i18nConfig.CustomPropertyFirstColumn : `${i18nConfig.CustomProperty} ${index}`)
.setDesc(index === 0 ? i18nConfig.CustomPropertyFirstColumnDesc : "");
propertyLine.addText(text => {
text.setPlaceholder(i18nConfig.CustomPropertyName)
.setValue(property.customName)
.setDisabled(true);
});
propertyLine.addDropdown((dropdown) => {
const options: Record<string, string> = {
'title': 'Title',
'rich_text': 'Text',
'number': 'Number',
'select': 'Select',
'multi_select': 'Multi-Select',
'date': 'Date',
'files': 'Files & Media',
'checkbox': 'Checkbox',
'url': 'URL',
'email': 'Email',
'phone_number': 'Phone Number',
// Additional options can be added here
};
// Populate dropdown with options
Object.keys(options).forEach(key => {
dropdown.addOption(key, options[key]);
});
dropdown.setValue(property.customType)
.setDisabled(true); // Disable dropdown to prevent changes
});
});
}
}

View File

377
src/ui/settingModal.ts Normal file
View File

@@ -0,0 +1,377 @@
import {
Modal,
Setting,
ButtonComponent, App
} from 'obsidian';
import { i18nConfig } from "../lang/I18n";
import ObsidianSyncNotionPlugin from "../main";
import { DatabaseDetails, ObsidianSettingTab } from "./settingTabs";
export interface customProperty {
customName: string;
customType: string;
index: number;
}
export class SettingModal extends Modal {
propertyLines: Setting[] = []; // Store all property line settings
properties: customProperty[] = []; // Array to store property values and types
[key: string]: any; // Index signature
data: Record<string, any> = {
databaseFormat: 'none',
databaseFullName: '',
databaseAbbreviateName: '',
notionAPI: '',
databaseID: '',
tagButton: true,
customTitleButton: false,
customTitleName: '',
customProperties: [],
// customValues: '',
saved: false,
};
plugin: ObsidianSyncNotionPlugin;
settingTab: ObsidianSettingTab;
constructor(app: App, plugin: ObsidianSyncNotionPlugin, settingTab: ObsidianSettingTab, dbDetails?: DatabaseDetails) {
super(app);
this.plugin = plugin;
this.settingTab = settingTab;
this.properties = [];
if (dbDetails) {
this.data.databaseFormat = dbDetails.format;
this.data.databaseFullName = dbDetails.fullName;
this.data.databaseAbbreviateName = dbDetails.abName;
this.data.notionAPI = dbDetails.notionAPI;
this.data.databaseID = dbDetails.databaseID;
this.data.tagButton = dbDetails.tagButton;
this.data.customTitleButton = dbDetails.customTitleButton;
this.data.customTitleName = dbDetails.customTitleName;
this.data.customProperties = dbDetails.customProperties;
// this.data.customValues = dbDetails.customValues;
this.data.saved = dbDetails.saved;
}
}
display(): void {
this.containerEl.addClass("settings-modal");
this.titleEl.setText('Add new database');
// create the dropdown button to select the database format
let { contentEl } = this;
contentEl.empty();
const settingDiv = contentEl.createDiv('setting-div');
const nextTabs = contentEl.createDiv('next-tabs');
new Setting(settingDiv)
.setName(i18nConfig.databaseFormat)
.setDesc(i18nConfig.databaseFormatDesc)
.addDropdown((component) => {
component
.addOption('none', '')
.addOption('general', i18nConfig.databaseGeneral)
.addOption('next', i18nConfig.databaseNext)
.addOption('custom', i18nConfig.databaseCustom)
.setValue(this.data.databaseFormat)
.onChange(async (value) => {
this.data.databaseFormat = value;
nextTabs.empty();
this.updateContentBasedOnSelection(value, nextTabs);
});
// Initialize content based on the current dropdown value
(this.data.saved) ? this.updateContentBasedOnSelection(this.data.databaseFormat, nextTabs) : this.updateContentBasedOnSelection(this.plugin.settings.databaseFormat, nextTabs);
});
// add save button
let footerEl = contentEl.createDiv('save-button');
let saveButton = new Setting(footerEl)
saveButton.addButton((button: ButtonComponent) => {
return button
.setTooltip('Save')
.setIcon('checkmark')
.onClick(async () => {
this.data.saved = true;
this.data.customProperties = this.properties;
this.close();
});
}
);
saveButton.addExtraButton((button) => {
return button
.setTooltip('Cancel')
.setIcon('cross')
.onClick(() => {
this.data.saved = false;
// this.data.customProperties = this.properties;
this.close();
});
}
);
}
updateContentBasedOnSelection(value: string, nextTabs: HTMLElement): void {
// Clear existing content
nextTabs.empty();
// Generate content based on the selected value
if (value === 'general') {
nextTabs.createEl('h3', { text: i18nConfig.NotionGeneralSettingHeader });
// add full name
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.data.databaseFullName, 'data', 'databaseFullName')
// add abbreviate name
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.data.databaseAbbreviateName, 'data', 'databaseAbbreviateName')
// tag button
this.createSettingEl(nextTabs, i18nConfig.NotionTagButton, i18nConfig.NotionTagButtonDesc, 'toggle', i18nConfig.NotionCustomTitleText, this.data.tagButton, 'data', 'tagButton')
// add custom title button
new Setting(nextTabs)
.setName(i18nConfig.NotionCustomTitle)
.setDesc(i18nConfig.NotionCustomTitleDesc)
.addToggle((toggle) =>
toggle
.setValue(this.data.customTitleButton)
.onChange(async (value) => {
this.data.customTitleButton = value;
this.updateSettingEl(CustomNameEl, value)
})
);
// add custom title name
const CustomNameEl = this.createStyleDiv('custom-name', (this.data.customTitleButton), nextTabs);
this.createSettingEl(CustomNameEl, i18nConfig.NotionCustomTitleName, i18nConfig.NotionCustomTitleNameDesc, 'text', i18nConfig.NotionCustomTitleText, this.data.customTitleName, 'data', 'customTitleName')
// add api key
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.data.notionAPI, 'data', 'notionAPI')
// add database id
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.data.databaseID, 'data', 'databaseID')
} else if (value === 'next') {
nextTabs.createEl('h3', { text: i18nConfig.NotionNextSettingHeader });
// add full name
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.data.databaseFullName, 'data', 'databaseFullName')
// add abbreviate name
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.data.databaseAbbreviateName, 'data', 'databaseAbbreviateName')
// add api key
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.data.notionAPI, 'data', 'notionAPI')
// add database id
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.data.databaseID, 'data', 'databaseID')
} else if (value === 'custom') {
nextTabs.createEl('h3', { text: i18nConfig.NotionCustomSettingHeader });
// add full name
this.createSettingEl(nextTabs, i18nConfig.databaseFullName, i18nConfig.databaseFullNameDesc, 'text', i18nConfig.databaseFullNameText, this.data.databaseFullName, 'data', 'databaseFullName')
// add abbreviate name
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.data.databaseAbbreviateName, 'data', 'databaseAbbreviateName')
// add api key
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.data.notionAPI, 'data', 'notionAPI')
// add database id
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.DatabaseIDDesc, 'password', i18nConfig.DatabaseIDText, this.data.databaseID, 'data', 'databaseID')
// add new property button
new Setting(nextTabs)
.setName(i18nConfig.NotionCustomValues)
.setDesc(i18nConfig.NotionCustomValuesDesc)
.addButton((button: ButtonComponent) => {
return button
.setTooltip('Add one more property')
.setButtonText('Add New Property')
.onClick(async () => {
const customTabs = nextTabs.createDiv("custom-tabs");
this.createPropertyLine(customTabs, this.properties);
});
}
);
}
}
onOpen() {
// add console log to check if the modal is opened
this.display()
}
createPropertyLine(containerEl: HTMLElement, properties: customProperty[]): void {
const propertyIndex = properties.length;
properties.push({ customName: "", customType: "", index: propertyIndex });
const propertyLine = new Setting(containerEl)
.setName(propertyIndex === 0 ? i18nConfig.CustomPropertyFirstColumn : `${i18nConfig.CustomProperty} ${propertyIndex}`)
.setDesc(propertyIndex === 0 ? i18nConfig.CustomPropertyFirstColumnDesc : "");
propertyLine.addText(text => {
text.setPlaceholder(i18nConfig.CustomPropertyName)
.setValue("")
.onChange(value => {
let actualIndex = properties.findIndex(p => p.index === propertyIndex);
if (actualIndex !== -1) {
properties[actualIndex].customName = value;
}
});
});
propertyLine.addDropdown((dropdown) => {
const options: Record<string, string> = {
'rich_text': 'Text',
'number': 'Number',
'select': 'Select',
'multi_select': 'Multi-Select',
'date': 'Date',
'files': 'Files & Media',
'checkbox': 'Checkbox',
'url': 'URL',
'email': 'Email',
'phone_number': 'Phone Number',
// 'formula': 'Formula',
// 'relation': 'Relation',
// 'rollup': 'Rollup',
// 'created_time': 'Created time',
// 'created_by': 'Created by',
// 'last_edited_time': 'Last Edited Time',
// 'last_edited_by': 'Last Edited By',
};
const currentProperty = properties[propertyIndex];
if (propertyIndex === 0) {
dropdown.addOption("title", "Title");
} else {
Object.keys(options).forEach(key => {
dropdown.addOption(key, options[key]);
});
}
dropdown.setValue("")
.onChange(value => {
if (currentProperty) {
currentProperty.customType = value;
// Retrieve the index of currentProperty from the properties array
const updatedIndex = properties.findIndex(p => p === currentProperty);
console.log(`Updated value at index ${updatedIndex}: ${value}`);
} else {
console.log("Property not found, may have been deleted.");
}
});
});
if (propertyIndex > 0) {
propertyLine.addButton(button => {
return button
.setTooltip("Delete")
.setIcon("trash")
.onClick(() => {
this.deleteProperty(propertyIndex, properties);
});
});
}
this.propertyLines.push(propertyLine);
this.updatePropertyLines(); // Ensure property lines are updated after creation
}
deleteProperty(propertyIndex: number, properties: customProperty[]): void {
let actualIndex = properties.findIndex(p => p.index === propertyIndex);
if (actualIndex !== -1) {
properties.splice(actualIndex, 1);
if (this.propertyLines[actualIndex]) {
this.propertyLines[actualIndex].settingEl.remove();
this.propertyLines.splice(actualIndex, 1);
}
// Update indices in the properties array
properties.forEach((prop, idx) => {
prop.index = idx;
});
this.updatePropertyLines();
}
}
updatePropertyLines() {
this.propertyLines.forEach((line, idx) => {
line.setName(idx === 0 ? i18nConfig.CustomPropertyFirstColumn : `${i18nConfig.CustomProperty} ${idx}`);
});
}
// create a function to create a div with a style for pop over elements
public createStyleDiv(className: string, commandValue: boolean = false, parentEl: HTMLElement) {
return parentEl.createDiv(className, (div) => {
this.updateSettingEl(div, commandValue);
});
}
// update the setting display style in the setting tab
public updateSettingEl(element: HTMLElement, commandValue: boolean) {
element.style.borderTop = commandValue ? "1px solid var(--background-modifier-border)" : "none";
element.style.paddingTop = commandValue ? "0.75em" : "0";
element.style.display = commandValue ? "block" : "none";
element.style.alignItems = "center";
}
// function to add one setting element in the setting tab.
public createSettingEl(contentEl: HTMLElement, name: string, desc: string, type: string, placeholder: string, holderValue: any, dataRecord: string, settingsKey: string) {
if (type === 'password') {
return new Setting(contentEl)
.setName(name)
.setDesc(desc)
.addText((text) => {
text.inputEl.type = type;
return text
.setPlaceholder(placeholder)
.setValue(holderValue)
.onChange(async (value) => {
this[dataRecord][settingsKey] = value; // Update the settings dictionary await this.plugin.saveSettings();
})
});
} else if (type === 'toggle') {
return new Setting(contentEl)
.setName(name)
.setDesc(desc)
.addToggle((toggle) =>
toggle
.setValue(holderValue)
.onChange(async (value) => {
this[dataRecord][settingsKey] = value; // Update the settings dictionary await this.plugin.saveSettings();
})
);
} else if (type === 'text') {
return new Setting(contentEl)
.setName(name)
.setDesc(desc)
.addText((text) =>
text
.setPlaceholder(placeholder)
.setValue(holderValue)
.onChange(async (value) => {
this[dataRecord][settingsKey] = value; // Update the settings dictionary await this.plugin.saveSettings();
})
);
}
}
}

View File

@@ -1,6 +1,10 @@
import { App, PluginSettingTab, Setting } from "obsidian";
import { App, ButtonComponent, PluginSettingTab, Setting } from "obsidian";
import { i18nConfig } from "../lang/I18n";
import ObsidianSyncNotionPlugin from "../main";
import { SettingModal } from "./settingModal";
import { PreviewModal } from "./PreviewModal";
import { EditModal } from "./EditModal";
import { DeleteModal } from "./DeleteModal";
export interface PluginSettings {
NextButton: boolean;
@@ -8,16 +12,34 @@ export interface PluginSettings {
databaseIDNext: string;
bannerUrl: string;
notionUser: string;
NotionLinkDisplay: boolean;
proxy: string;
GeneralButton: boolean;
CustomTitleButton: boolean;
CustomTitleName: string;
tagButton: boolean;
customTitleButton: boolean;
customTitleName: string;
notionAPIGeneral: string;
databaseIDGeneral: string;
CustomButton: boolean;
CustomValues: string;
notionAPICustom: string;
databaseIDCustom: string;
[key: string]: any;
databaseDetails: Record<string, DatabaseDetails>
}
export interface DatabaseDetails {
format: string;
fullName: string;
abName: string;
notionAPI: string;
databaseID: string;
tagButton: boolean;
customTitleButton: boolean;
customTitleName: string;
customProperties: { customName: string, customType: string, index: number }[];
// customValues: string;
saved: boolean;
}
export const DEFAULT_SETTINGS: PluginSettings = {
@@ -26,20 +48,25 @@ export const DEFAULT_SETTINGS: PluginSettings = {
databaseIDNext: "",
bannerUrl: "",
notionUser: "",
NotionLinkDisplay: true,
proxy: "",
GeneralButton: true,
CustomTitleButton: false,
CustomTitleName: "",
tagButton: true,
customTitleButton: false,
customTitleName: "",
notionAPIGeneral: "",
databaseIDGeneral: "",
CustomButton: false,
CustomValues: "",
notionAPICustom: "",
databaseIDCustom: "",
databaseDetails: {},
};
export class ObsidianSettingTab extends PluginSettingTab {
plugin: ObsidianSyncNotionPlugin;
databaseEl: HTMLDivElement;
constructor(app: App, plugin: ObsidianSyncNotionPlugin) {
super(app, plugin);
@@ -58,131 +85,66 @@ export class ObsidianSettingTab extends PluginSettingTab {
this.createSettingEl(containerEl, i18nConfig.NotionUser, i18nConfig.NotionUserDesc, 'text', i18nConfig.NotionUserText, this.plugin.settings.notionUser, 'notionUser')
containerEl.createEl('h2', { text: i18nConfig.NotionNextSettingHeader })
this.createSettingEl(containerEl, i18nConfig.NotionLinkDisplay, i18nConfig.NotionLinkDisplayDesc, 'toggle', i18nConfig.NotionLinkDisplay, this.plugin.settings.NotionLinkDisplay, 'NotionLinkDisplay')
// add new button
new Setting(containerEl)
.setName(i18nConfig.NotionNextButton)
.setDesc(i18nConfig.NotionNextButtonDesc)
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.NextButton)
.onChange(async (value) => {
this.plugin.settings.NextButton = value;
.setName("Add New Database")
.setDesc("Add New Database")
.addButton((button: ButtonComponent): ButtonComponent => {
return button
.setTooltip("Add New Database")
.setIcon("plus")
.onClick(async () => {
let modal = new SettingModal(this.app, this.plugin, this);
this.updateSettingEl(notionAPINextEl, value)
this.updateSettingEl(databaseIDNextEl, value)
await this.plugin.saveSettings();
await this.plugin.commands.updateCommand();
})
);
const notionAPINextEl = this.createStyleDiv('api-next', this.plugin.settings.NextButton)
this.createSettingEl(notionAPINextEl, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.plugin.settings.notionAPINext, 'notionAPINext')
const databaseIDNextEl = this.createStyleDiv('databaseID-next', this.plugin.settings.NextButton)
this.createSettingEl(databaseIDNextEl, i18nConfig.DatabaseID, i18nConfig.NotionAPIDesc, 'password', i18nConfig.DatabaseIDText, this.plugin.settings.databaseIDNext, 'databaseIDNext')
// General Database Settings
containerEl.createEl('h2', { text: i18nConfig.NotionGeneralSettingHeader });
// new Setting(containerEl)
// .setName(i18nConfig.NotYetFinish)
new Setting(containerEl)
.setName(i18nConfig.NotionGeneralButton)
.setDesc(i18nConfig.NotionGeneralButtonDesc)
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.GeneralButton)
.onChange(async (value) => {
this.plugin.settings.GeneralButton = value;
this.updateSettingEl(CustomTitleEl, value)
// name should follow the result of the title button
if (value) {
this.updateSettingEl(CustomNameEl, this.plugin.settings.CustomTitleButton)
} else {
this.updateSettingEl(CustomNameEl, value)
modal.onClose = () => {
if (modal.data.saved) {
const dbDetails = {
format: modal.data.databaseFormat,
fullName: modal.data.databaseFullName,
abName: modal.data.databaseAbbreviateName,
notionAPI: modal.data.notionAPI,
databaseID: modal.data.databaseID,
tagButton: modal.data.tagButton,
customTitleButton: modal.data.customTitleButton,
customTitleName: modal.data.customTitleName,
customProperties: modal.data.customProperties,
// customValues: modal.data.customValues,
saved: modal.data.saved,
}
this.updateSettingEl(notionAPIGeneralEl, value)
this.updateSettingEl(databaseIDGeneralEl, value)
this.plugin.addDatabaseDetails(dbDetails);
this.plugin.commands.updateCommand();
await this.plugin.saveSettings();
await this.plugin.commands.updateCommand();
this.display()
}
}
})
);
modal.open();
});
});
const CustomTitleEl = this.createStyleDiv('custom-title', this.plugin.settings.GeneralButton);
new Setting(CustomTitleEl)
.setName(i18nConfig.NotionCustomTitle)
.setDesc(i18nConfig.NotionCustomTitleDesc)
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.CustomTitleButton)
.onChange(async (value) => {
this.plugin.settings.CustomTitleButton = value;
// new section to display all created database
containerEl.createEl('h2', { text: "Database List" });
this.updateSettingEl(CustomNameEl, value)
this.databaseEl = containerEl.createDiv('database-list');
// list all created database
this.showDatabase();
await this.plugin.saveSettings();
await this.plugin.commands.updateCommand();
})
);
const CustomNameEl = this.createStyleDiv('custom-name', this.plugin.settings.CustomTitleButton);
this.createSettingEl(CustomNameEl, i18nConfig.NotionCustomTitleName, i18nConfig.NotionCustomTitleNameDesc, 'text', i18nConfig.NotionCustomTitleText, this.plugin.settings.CustomTitleName, 'CustomTitleName')
// new Setting(containerEl)
// .setName("Convert tags(optional)")
// .setDesc("Transfer the Obsidian tags to the Notion table. It requires the column with the name 'Tags'")
// .addToggle((toggle) =>
// toggle
// .setValue(this.plugin.settings.allowTags)
// .onChange(async (value) => {
// this.plugin.settings.allowTags = value;
// await this.plugin.saveSettings();
// })
// );
const notionAPIGeneralEl = this.createStyleDiv('api-general', this.plugin.settings.GeneralButton);
this.createSettingEl(notionAPIGeneralEl, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.plugin.settings.notionAPIGeneral, 'notionAPIGeneral')
const databaseIDGeneralEl = this.createStyleDiv('databaseID-general', this.plugin.settings.GeneralButton);
this.createSettingEl(databaseIDGeneralEl, i18nConfig.DatabaseID, i18nConfig.NotionAPIDesc, 'password', i18nConfig.DatabaseIDText, this.plugin.settings.databaseIDGeneral, 'databaseIDGeneral')
// Custom Database Settings
// containerEl.createEl('h2', {text: i18nConfig.NotionCustomSettingHeader});
//
// new Setting(containerEl)
// .setName(i18nConfig.NotionCustomButton)
// .setDesc(i18nConfig.NotionCustomButtonDesc)
// .addToggle((toggle) =>
// toggle
// .setValue(this.plugin.settings.CustomButton)
// .onChange(async (value) => {
// this.plugin.settings.CustomButton = value;
// await this.plugin.saveSettings();
// })
// );
}
// create a function to create a div with a style for pop over elements
private createStyleDiv(className: string, commandValue: boolean = false) {
return this.containerEl.createDiv(className, (div) => {
this.updateSettingEl(div, commandValue);
});
}
// public createStyleDiv(className: string, commandValue: boolean = false) {
// return this.containerEl.createDiv(className, (div) => {
// this.updateSettingEl(div, commandValue);
// });
// }
// update the setting display style in the setting tab
private updateSettingEl(element: HTMLElement, commandValue: boolean) {
public updateSettingEl(element: HTMLElement, commandValue: boolean) {
element.style.borderTop = commandValue ? "1px solid var(--background-modifier-border)" : "none";
element.style.paddingTop = commandValue ? "0.75em" : "0";
element.style.display = commandValue ? "block" : "none";
@@ -190,7 +152,7 @@ export class ObsidianSettingTab extends PluginSettingTab {
}
// function to add one setting element in the setting tab.
private createSettingEl(containerEl: HTMLElement, name: string, desc: string, type: string, placeholder: string, holderValue: any, settingsKey: string) {
public createSettingEl(containerEl: HTMLElement, name: string, desc: string, type: string, placeholder: string, holderValue: any, settingsKey: string) {
if (type === 'password') {
return new Setting(containerEl)
.setName(name)
@@ -235,4 +197,111 @@ export class ObsidianSettingTab extends PluginSettingTab {
}
}
// function to show all the database details
showDatabase() {
this.databaseEl.empty();
for (let key in this.plugin.settings.databaseDetails) {
let dbDetails = this.plugin.settings.databaseDetails[key];
const databaseDiv = this.databaseEl.createDiv('database-div');
let settingEl = new Setting(databaseDiv)
.setName(`${dbDetails.fullName} (${dbDetails.abName})`)
.setDesc(dbDetails.format)
// add a button for preview data
settingEl
.addButton((button: ButtonComponent): ButtonComponent => {
return button
.setTooltip("Preview Database")
.setIcon("eye")
.onClick(async () => {
let modal = new PreviewModal(this.app, this.plugin, this, dbDetails);
modal.open();
});
});
// add a button for edit data
settingEl
.addButton((button: ButtonComponent): ButtonComponent => {
return button
.setTooltip("Edit Database")
.setIcon("pencil")
.onClick(async () => {
let modal = new EditModal(this.app, this.plugin, this, dbDetails);
modal.onClose = () => {
if (modal.dataTemp.savedTempInd) {
const dbDetailsNew: DatabaseDetails = {
format: modal.dataTemp.databaseFormatTemp,
fullName: modal.dataTemp.databaseFullNameTemp,
abName: modal.dataTemp.databaseAbbreviateNameTemp,
notionAPI: modal.dataTemp.notionAPITemp,
databaseID: modal.dataTemp.databaseIDTemp,
tagButton: modal.dataTemp.tagButtonTemp,
customTitleButton: modal.dataTemp.customTitleButtonTemp,
customTitleName: modal.dataTemp.customTitleNameTemp,
customProperties: modal.dataTemp.customPropertiesTemp,
// customValues: modal.data.customValues,
saved: modal.dataTemp.savedTemp,
}
const dbDetailsPrev: DatabaseDetails = {
format: modal.dataPrev.databaseFormatPrev,
fullName: modal.dataPrev.databaseFullNamePrev,
abName: modal.dataPrev.databaseAbbreviateNamePrev,
notionAPI: modal.dataPrev.notionAPIPrev,
databaseID: modal.dataPrev.databaseIDPrev,
tagButton: modal.dataPrev.tagButtonPrev,
customTitleButton: modal.dataPrev.customTitleButtonPrev,
customTitleName: modal.dataPrev.customTitleNamePrev,
customProperties: modal.dataPrev.customPropertiesPrev,
// customValues: modal.data.customValues,
saved: modal.dataPrev.savedPrev,
}
this.plugin.deleteDatabaseDetails(dbDetailsPrev);
this.plugin.updateDatabaseDetails(dbDetailsNew);
this.plugin.commands.updateCommand();
this.display()
}
}
modal.open();
});
});
settingEl
.addButton((button: ButtonComponent): ButtonComponent => {
return button
.setTooltip("Delete Database")
.setIcon("trash")
.onClick(async () => {
let modal = new DeleteModal(this.app, this.plugin, this, dbDetails);
modal.onClose = () => {
if (modal.data.deleted) {
this.plugin.deleteDatabaseDetails(dbDetails);
console.log(dbDetails.fullName + " deleted");
this.plugin.commands.updateCommand();
this.display()
}
}
modal.open();
});
});
}
}
}

View File

@@ -1,6 +1,7 @@
import { App, Notice, TFile } from "obsidian";
import ObsidianSyncNotionPlugin from "../main";
import { PluginSettings } from "../ui/settingTabs";
import { DatabaseDetails } from "../ui/settingTabs";
import { i18nConfig } from "src/lang/I18n";
export async function updateYamlInfo(
yamlContent: string,
@@ -8,10 +9,14 @@ export async function updateYamlInfo(
res: any,
app: App,
plugin: ObsidianSyncNotionPlugin,
dbDetails: DatabaseDetails,
) {
let { url, id } = res.json
let { url, id } = res;
// replace www to notionID
const { notionUser } = plugin.settings;
const { notionUser, NotionLinkDisplay } = plugin.settings;
const { abName } = dbDetails
const notionIDKey = `NotionID-${abName}`;
const linkKey = `link-${abName}`;
if (notionUser !== "") {
// replace url str "www" to notionID
@@ -19,33 +24,21 @@ export async function updateYamlInfo(
}
await app.fileManager.processFrontMatter(nowFile, yamlContent => {
if (yamlContent['notionID']) {
delete yamlContent['notionID']
if (yamlContent[notionIDKey]) {
delete yamlContent[notionIDKey]
}
if (yamlContent['link']) {
delete yamlContent['link']
if (yamlContent[linkKey]) {
delete yamlContent[linkKey]
}
// add new notionID and link
yamlContent.notionID = id;
yamlContent.link = url;
yamlContent[notionIDKey] = id;
(NotionLinkDisplay) ? yamlContent[linkKey] = url : null;
});
try {
await navigator.clipboard.writeText(url)
} catch (error) {
new Notice(`复制链接失败,请手动复制${error}`)
console.log(error)
new Notice(`${i18nConfig.CopyErrorMessage}`);
}
// 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

@@ -2,42 +2,54 @@ import { i18nConfig } from "../lang/I18n";
import {App, Notice} from "obsidian";
import {Upload2NotionNext} from "./upload_next/Upload2NotionNext";
import {Upload2NotionGeneral} from "./upload_general/Upload2NotionGeneral";
import { PluginSettings } from "../ui/settingTabs";
import {Upload2NotionCustom} from "./upoload_custom/Upload2NotionCustom";
import {DatabaseDetails, PluginSettings} from "../ui/settingTabs";
import ObsidianSyncNotionPlugin from "../main";
import {getNowFileMarkdownContentNext} from "./upload_next/getMarkdownNext";
import {getNowFileMarkdownContentGeneral} from "./upload_general/getMarkdownGeneral";
import {getNowFileMarkdownContentCustom} from "./upoload_custom/getMarkdownCustom";
export async function uploadCommandNext(
plugin: ObsidianSyncNotionPlugin,
settings: PluginSettings,
dbDetails: DatabaseDetails,
app: App,
) {
const { notionAPINext, databaseIDNext } = settings;
// Check if NNon exists
// if (NNon === undefined) {
// const NNonmessage = i18nConfig.NNonMissing;
// new Notice(NNonmessage);
// return;
// }
const {notionAPI, databaseID} = dbDetails;
// Check if the user has set up the Notion API and database ID
if (notionAPINext === "" || databaseIDNext === "") {
if (notionAPI === "" || databaseID === "") {
const setAPIMessage = i18nConfig["set-api-id"];
new Notice(setAPIMessage);
return;
}
const { markDownData, nowFile, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime } = await getNowFileMarkdownContentNext(app, settings)
const {
markDownData,
nowFile,
emoji,
cover,
tags,
type,
slug,
stats,
category,
summary,
paword,
favicon,
datetime
} = await getNowFileMarkdownContentNext(app, settings)
if (markDownData) {
const {basename} = nowFile;
const upload = new Upload2NotionNext(plugin);
const upload = new Upload2NotionNext(plugin, dbDetails);
const res = await upload.syncMarkdownToNotionNext(basename, emoji, cover, tags, type, slug, stats, category, summary, paword, favicon, datetime, markDownData, nowFile, this.app);
if (res.status === 200) {
new Notice(`${i18nConfig["sync-success"]}${basename}`);
const {response} = res;
if (response.status === 200) {
new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green";
} else {
new Notice(`${i18nConfig["sync-fail"]} ${basename}`, 5000);
}
@@ -46,17 +58,17 @@ export async function uploadCommandNext(
}
export async function uploadCommandGeneral(
plugin: ObsidianSyncNotionPlugin,
settings: PluginSettings,
dbDetails: DatabaseDetails,
app: App,
) {
const { notionAPIGeneral, databaseIDGeneral } = settings;
const {notionAPI, databaseID} = dbDetails;
// Check if the user has set up the Notion API and database ID
if (notionAPIGeneral === "" || databaseIDGeneral === "") {
if (notionAPI === "" || databaseID === "") {
const setAPIMessage = i18nConfig["set-api-id"];
new Notice(setAPIMessage);
return;
@@ -67,11 +79,47 @@ export async function uploadCommandGeneral(
if (markDownData) {
const {basename} = nowFile;
const upload = new Upload2NotionGeneral(plugin);
const upload = new Upload2NotionGeneral(plugin, dbDetails);
const res = await upload.syncMarkdownToNotionGeneral(basename, cover, tags, markDownData, nowFile, this.app);
if (res.status === 200) {
new Notice(`${i18nConfig["sync-success"]}${basename}`);
const {response} = res;
if (response.status === 200) {
new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green";
} else {
new Notice(`${i18nConfig["sync-fail"]} ${basename}`, 5000);
}
}
}
export async function uploadCommandCustom(
plugin: ObsidianSyncNotionPlugin,
settings: PluginSettings,
dbDetails: DatabaseDetails,
app: App,
) {
const {notionAPI, databaseID} = settings;
// Check if the user has set up the Notion API and database ID
if (notionAPI === "" || databaseID === "") {
const setAPIMessage = i18nConfig["set-api-id"];
new Notice(setAPIMessage);
return;
}
const {markDownData, nowFile, cover, customValues} = await getNowFileMarkdownContentCustom(app, dbDetails)
if (markDownData) {
const {basename} = nowFile;
const upload = new Upload2NotionCustom(plugin, dbDetails);
const res = await upload.syncMarkdownToNotionCustom(cover, customValues, markDownData, nowFile, this.app);
const {response} = res;
if (response.status === 200) {
new Notice(`${i18nConfig["sync-preffix"]} ${basename} ${i18nConfig["sync-success"]}`).noticeEl.style.color = "green";
} else {
new Notice(`${i18nConfig["sync-fail"]} ${basename}`, 5000);
}

View File

@@ -1,26 +1,31 @@
import { App, Notice, requestUrl, TFile } from "obsidian";
import { Client } from '@notionhq/client';
import {markdownToBlocks,} from "@tryfabric/martian";
import { markdownToBlocks, } from "@jxpeng98/martian";
import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main";
import { DatabaseDetails } from "../../ui/settingTabs";
export class UploadBaseGeneral {
plugin: MyPlugin;
notion: Client;
agent: any;
dbDetails: DatabaseDetails
constructor(plugin: MyPlugin) {
constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) {
this.plugin = plugin;
this.dbDetails = dbDetails
}
async deletePage(notionID: string) {
const { notionAPI } = this.dbDetails
return requestUrl({
url: `https://api.notion.com/v1/blocks/${notionID}`,
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.plugin.settings.notionAPINext,
'Authorization': 'Bearer ' + notionAPI,
'Notion-Version': '2022-06-28',
},
body: ''
@@ -28,11 +33,12 @@ export class UploadBaseGeneral {
}
async getDataBase(databaseID: string) {
const { notionAPI } = this.dbDetails
const response = await requestUrl({
url: `https://api.notion.com/v1/databases/${databaseID}`,
method: 'GET',
headers: {
'Authorization': 'Bearer ' + this.plugin.settings.notionAPINext,
'Authorization': 'Bearer ' + notionAPI,
'Notion-Version': '2022-06-28',
}
}

View File

@@ -1,18 +1,22 @@
import { App, Notice, requestUrl, TFile } from "obsidian";
import { Client } from '@notionhq/client';
import { markdownToBlocks, } from "@tryfabric/martian";
import {App, Notice, TFile} from "obsidian";
import {Client} from "@notionhq/client";
import {markdownToBlocks} from "@jxpeng98/martian";
import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main";
import { PluginSettings } from "../../ui/settingTabs";
import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs";
import {UploadBaseGeneral} from "./BaseUpload2NotionGeneral";
import {updateYamlInfo} from "../updateYaml";
import fetch from 'node-fetch';
import {i18nConfig} from "../../lang/I18n";
export class Upload2NotionGeneral extends UploadBaseGeneral {
settings: PluginSettings;
dbDetails: DatabaseDetails;
constructor(plugin: MyPlugin) {
super(plugin);
constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) {
super(plugin, dbDetails);
this.dbDetails = dbDetails;
}
// 因为需要解析notion的block进行对比非常的麻烦
@@ -24,19 +28,19 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
tags: string[],
childArr: any,
) {
await this.deletePage(notionID)
await this.deletePage(notionID);
const databasecover = await this.getDataBase(this.plugin.settings.databaseIDGeneral)
const {databaseID} = this.dbDetails;
const databaseCover = await this.getDataBase(
databaseID,
);
if (cover == null) {
cover = databasecover
cover = databaseCover;
}
return await this.createPage(
title,
cover,
tags,
childArr)
return await this.createPage(title, cover, tags, childArr);
}
async createPage(
@@ -45,61 +49,140 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
tags: string[],
childArr: any,
) {
const {
databaseID,
customTitleButton,
customTitleName,
tagButton,
notionAPI
} = this.dbDetails;
// check the length of the childArr and split it into chunks of 100
const childArrLength = childArr.length;
let extraArr: any[] = [];
let firstArr: any;
let pushCount = 0;
console.log(`Page includes ${childArrLength} blocks`)
if (childArrLength > 100) {
for (let i = 0; i < childArr.length; i += 100) {
if (i == 0) {
firstArr = childArr.slice(0, 100);
} else {
const chunk = childArr.slice(i, i + 100);
extraArr.push(chunk);
pushCount++;
}
}
} else {
firstArr = childArr;
}
const bodyString: any = {
parent: {
database_id: this.plugin.settings.databaseIDGeneral,
database_id: databaseID,
},
properties: {
[this.plugin.settings.CustomTitleButton ? this.plugin.settings.CustomTitleName : 'title']: {
[customTitleButton
? customTitleName
: "title"]: {
title: [
{
text: {
content: title
content: title,
},
},
],
},
...(tagButton
? {
tags: {
multi_select: tags && true ? tags.map(tag => {
return { "name": tag }
}) : [],
multi_select: tags && true ? tags.map((tag) => ({name: tag})) : [],
},
},
children: childArr,
}
: {}),
},
children: firstArr,
};
if (cover) {
bodyString.cover = {
type: "external",
external: {
url: cover
}
}
url: cover,
},
};
}
if (!bodyString.cover && this.plugin.settings.bannerUrl) {
bodyString.cover = {
type: "external",
external: {
url: this.plugin.settings.bannerUrl
url: this.plugin.settings.bannerUrl,
},
};
}
console.log(bodyString)
const response = await fetch("https://api.notion.com/v1/pages", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
});
const data: any = await response.json();
if (!response.ok) {
new Notice(`Error ${data.status}: ${data.code} \n ${i18nConfig["CheckConsole"]}`, 5000);
} else {
console.log(`Page created: ${data.url}`);
console.log(`Page ID: ${data.id}`);
}
// upload the rest of the blocks
if (pushCount > 0) {
for (let i = 0; i < pushCount; i++) {
const extraBlocks = {
children: extraArr[i],
};
console.log(extraBlocks)
const extraResponse = await fetch(`https://api.notion.com/v1/blocks/${data.id}/children`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(extraBlocks),
});
const extraData: any = await extraResponse.json();
if (!extraResponse.ok) {
new Notice(`Error ${extraData.status}: ${extraData.code} \n ${i18nConfig["CheckConsole"]}`, 5000);
console.log(`Error message: \n ${extraData.message}`);
} else {
console.log(`${i18nConfig["ExtraBlockUploaded"]} to page: ${data.id}`);
if (i === pushCount - 1) {
console.log(`${i18nConfig["BlockUploaded"]} to page: ${data.id}`);
new Notice(`${i18nConfig["BlockUploaded"]} page: ${data.id}`, 5000);
}
}
}
}
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.notionAPIGeneral,
'Notion-Version': '2022-06-28',
},
body: JSON.stringify(bodyString),
})
} catch (error) {
new Notice(`network error ${error}`)
return {
response, // for status code
data // for id and url
}
}
@@ -111,12 +194,21 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
nowFile: TFile,
app: App,
): Promise<any> {
let res: any
const options = {
notionLimits: {
truncate: false,
}
}
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
const __content = yamlContent.__content;
const file2Block = markdownToBlocks(__content, options);
const frontmasster =
app.metadataCache.getFileCache(nowFile)?.frontmatter;
const {abName} = this.dbDetails
const notionIDKey = `NotionID-${abName}`;
const notionID = frontmasster ? frontmasster[notionIDKey] : null;
if (notionID) {
res = await this.updatePage(
@@ -124,21 +216,20 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
title,
cover,
tags,
file2Block
file2Block,
);
} else {
res = await this.createPage(
title,
cover,
tags,
file2Block
);
res = await this.createPage(title, cover, tags, file2Block);
}
if (res.status === 200) {
await updateYamlInfo(markdown, nowFile, res, app, this.plugin)
} else {
new Notice(`${res.text}`)
let {response, data} = res;
// console.log(response)
if (response && response.status === 200) {
await updateYamlInfo(markdown, nowFile, data, app, this.plugin, this.dbDetails);
}
return res
return res;
}
}

View File

@@ -1,26 +1,30 @@
import { App, Notice, requestUrl, TFile } from "obsidian";
import { Client } from '@notionhq/client';
import {markdownToBlocks,} from "@tryfabric/martian";
import { markdownToBlocks, } from "@jxpeng98/martian";
import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main";
import { DatabaseDetails } from "../../ui/settingTabs";
export class UploadBaseNext {
plugin: MyPlugin;
notion: Client;
agent: any;
dbDetails: DatabaseDetails
constructor(plugin: MyPlugin) {
constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) {
this.plugin = plugin;
this.dbDetails = dbDetails
}
async deletePage(notionID: string) {
const { notionAPI } = this.dbDetails
return requestUrl({
url: `https://api.notion.com/v1/blocks/${notionID}`,
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.plugin.settings.notionAPINext,
'Authorization': 'Bearer ' + notionAPI,
'Notion-Version': '2022-06-28',
},
body: ''
@@ -28,11 +32,13 @@ export class UploadBaseNext {
}
async getDataBase(databaseID: string) {
const { notionAPI } = this.dbDetails
const response = await requestUrl({
url: `https://api.notion.com/v1/databases/${databaseID}`,
method: 'GET',
headers: {
'Authorization': 'Bearer ' + this.plugin.settings.notionAPINext,
'Authorization': 'Bearer ' + notionAPI,
'Notion-Version': '2022-06-28',
}
}

View File

@@ -1,18 +1,23 @@
import {UploadBaseNext} from "./BaseUpload2NotionNext";
import { App, Notice, requestUrl, TFile } from "obsidian";
import { Client } from '@notionhq/client';
import { markdownToBlocks, } from "@tryfabric/martian";
import {App, Notice, TFile} from "obsidian";
import {Client} from "@notionhq/client";
import {markdownToBlocks} from "@jxpeng98/martian";
import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main";
import {PluginSettings} from "../../ui/settingTabs";
import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs";
import {updateYamlInfo} from "../updateYaml";
import {LIMITS, paragraph} from "@jxpeng98/martian/src/notion";
import fetch from "node-fetch";
import {i18nConfig} from "../../lang/I18n";
export class Upload2NotionNext extends UploadBaseNext {
settings: PluginSettings;
dbDetails: DatabaseDetails;
constructor(plugin: MyPlugin) {
super(plugin);
constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) {
super(plugin, dbDetails);
this.dbDetails = dbDetails;
}
// 因为需要解析notion的block进行对比非常的麻烦
@@ -31,14 +36,16 @@ export class Upload2NotionNext extends UploadBaseNext {
paword: string,
favicon: string,
datetime: string,
childArr: any
childArr: any,
) {
await this.deletePage(notionID)
await this.deletePage(notionID);
const databasecover = await this.getDataBase(this.plugin.settings.databaseIDNext)
const {databaseID} = this.dbDetails;
const databaseCover = await this.getDataBase(databaseID);
if (cover == null) {
cover = databasecover
cover = databaseCover;
}
return await this.createPage(
@@ -54,7 +61,8 @@ export class Upload2NotionNext extends UploadBaseNext {
paword,
favicon,
datetime,
childArr)
childArr,
);
}
async createPage(
@@ -70,122 +78,220 @@ export class Upload2NotionNext extends UploadBaseNext {
pawrod: string,
favicon: string,
datetime: string,
childArr: any
childArr: any,
) {
const bodyString: any = {
const {databaseID, notionAPI} = this.dbDetails;
// check the length of the childArr
// if it is too long, split it into multiple pages
const childArrLength = childArr.length;
let extraArr: any[] = [];
let firstArr: any;
let pushCount = 0;
console.log(`Page includes ${childArrLength} blocks`)
if (childArrLength > 100) {
for (let i = 0; i < childArr.length; i += 100) {
if (i == 0) {
firstArr = childArr.slice(0, 100);
} else {
const chunk = childArr.slice(i, i + 100);
extraArr.push(chunk);
pushCount++;
}
}
} else {
firstArr = childArr;
}
const pageProperties: any = {
parent: {
database_id: this.plugin.settings.databaseIDNext
},
icon: {
emoji: emoji || '📜'
database_id: databaseID,
},
properties: {
title: {
title: [
{
text: {
content: title
content: title,
},
},
],
},
tags: {
multi_select: tags && true ? tags.map(tag => {
return { "name": tag }
}) : [],
},
type: {
select: {
name: type || 'Post'
}
name: type || "Post",
},
slug: {
rich_text: [
{
text: {
content: slug || ''
}
}
]
},
status: {
select: {
name: stats || 'Draft'
}
name: stats || "Draft",
},
},
category: {
select: {
name: category || 'Obsidian'
}
name: category || "Obsidian",
},
summary: {
rich_text: [
{
text: {
content: summary || ''
}
}
]
},
password: {
rich_text: [
{
text: {
content: pawrod || ''
}
}
]
content: pawrod || "",
},
},
],
},
icon: {
rich_text: [
{
text: {
content: favicon || ''
}
}
]
content: favicon || "",
},
},
],
},
date: {
date: {
start: datetime || new Date().toISOString()
}
}
start: datetime || new Date().toISOString(),
},
children: childArr,
},
},
};
// add tags
if (tags) {
pageProperties.properties.tags = {
multi_select: tags.map((tag) => {
return {name: tag};
}),
};
}
// add title icon
if (emoji) {
pageProperties.icon = {
emoji: emoji,
};
}
// add slug
if (slug) {
pageProperties.properties.slug = {
rich_text: [
{
text: {
content: slug,
},
},
],
};
}
// check if summary is available
if (summary) {
pageProperties.properties.summary = {
rich_text: [
{
text: {
content: summary,
},
},
],
};
}
if (cover) {
bodyString.cover = {
pageProperties.cover = {
type: "external",
external: {
url: cover
}
}
url: cover,
},
};
}
if (!bodyString.cover && this.plugin.settings.bannerUrl) {
bodyString.cover = {
if (!pageProperties.cover && this.plugin.settings.bannerUrl) {
pageProperties.cover = {
type: "external",
external: {
url: this.plugin.settings.bannerUrl
}
}
url: this.plugin.settings.bannerUrl,
},
};
}
try {
return await requestUrl({
url: `https://api.notion.com/v1/pages`,
method: 'POST',
const bodyString: any = {
...pageProperties,
children: firstArr,
};
console.log(bodyString);
const response = await fetch("https://api.notion.com/v1/pages", {
method: "POST",
headers: {
'Content-Type': 'application/json',
// 'User-Agent': 'obsidian.md',
'Authorization': 'Bearer ' + this.plugin.settings.notionAPINext,
'Notion-Version': '2022-06-28',
"Content-Type": "application/json",
Authorization: "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
})
} catch (error) {
new Notice(`network error ${error}`)
});
const data: any = await response.json();
// can use response.ok or response.status === 200
if (!response.ok) {
new Notice(`Error ${data.status}: ${data.code} \n ${i18nConfig["CheckConsole"]}`, 5000);
console.log(`Error message: \n ${data.message}`);
} else {
console.log(`Page created: ${data.url}`);
console.log(`Page ID: ${data.id}`);
}
// if the childArr is over 100, patch the rest of the blocks append to the page
if (pushCount > 0) {
for (let i = 0; i < pushCount; i++) {
const extraBlocks = {
children: extraArr[i]
}
console.log(extraBlocks)
const extraResponse = await fetch(
`https://api.notion.com/v1/blocks/${data.id}/children`,
{
method: "PATCH",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(extraBlocks),
});
const extraData: any = await extraResponse.json();
if (!extraResponse.ok) {
new Notice(
`Extra Block: Error ${extraData.status}: ${extraData.code} \n ${i18nConfig["CheckConsole"]}`,
5000,
);
console.log(`Error message: \n ${extraData.message}`);
} else {
console.log(`${i18nConfig["ExtraBlockUploaded"]} to page: ${data.id}`);
if (i === pushCount - 1) {
console.log(`${i18nConfig["BlockUploaded"]} to page: ${data.id}`);
new Notice(`${i18nConfig["BlockUploaded"]} page: ${data.id}`, 5000);
}
}
}
}
return {
response, // for status code
data, // for id and url
};
}
async syncMarkdownToNotionNext(
@@ -205,12 +311,39 @@ export class Upload2NotionNext extends UploadBaseNext {
nowFile: TFile,
app: App,
): Promise<any> {
let res: any
const options = {
notionLimits: {
truncate: false,
},
};
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
const __content = yamlContent.__content;
const file2Block = markdownToBlocks(__content, options);
const frontmatter =
app.metadataCache.getFileCache(nowFile)?.frontmatter;
const {abName} = this.dbDetails;
const notionIDKey = `NotionID-${abName}`;
const notionID = frontmatter ? frontmatter[notionIDKey] : null;
// increase the limits
// Motivated by https://github.com/tryfabric/martian/issues/51
file2Block.forEach((block, index) => {
if (
block.type === "paragraph" &&
block.paragraph.rich_text.length > LIMITS.RICH_TEXT_ARRAYS
) {
const newParagraphBlocks: any[] = [];
const chunk: any = [];
const richTextChunks = chunk(block.paragraph.rich_text, 100);
richTextChunks.forEach((chunk: any) => {
newParagraphBlocks.push(paragraph(chunk));
});
file2Block.splice(index, 1, ...newParagraphBlocks);
}
});
if (notionID) {
res = await this.updatePage(
@@ -227,7 +360,7 @@ export class Upload2NotionNext extends UploadBaseNext {
paword,
favicon,
datetime,
file2Block
file2Block,
);
} else {
res = await this.createPage(
@@ -243,15 +376,23 @@ export class Upload2NotionNext extends UploadBaseNext {
paword,
favicon,
datetime,
file2Block
file2Block,
);
}
if (res.status === 200) {
await updateYamlInfo(markdown, nowFile, res, app, this.plugin)
} else {
new Notice(`${res.text}`)
}
return res
}
let {response, data} = res;
if (response && response.status === 200) {
await updateYamlInfo(
markdown,
nowFile,
data,
app,
this.plugin,
this.dbDetails,
);
}
return res;
}
}

View File

@@ -13,6 +13,7 @@ export async function getNowFileMarkdownContentNext(
let type = '';
let slug = '';
let stats = '';
let status = '';
let category = '';
let summary = '';
let paword = '';
@@ -26,7 +27,7 @@ export async function getNowFileMarkdownContentNext(
tags = FileCache.frontmatter.tags;
type = FileCache.frontmatter.type;
slug = FileCache.frontmatter.slug;
stats = FileCache.frontmatter.stats;
stats = FileCache.frontmatter.stats || FileCache.frontmatter.status;
category = FileCache.frontmatter.category;
summary = FileCache.frontmatter.summary;
paword = FileCache.frontmatter.password;

View File

@@ -0,0 +1,53 @@
import { App, Notice, requestUrl, TFile } from "obsidian";
import { Client } from '@notionhq/client';
import { markdownToBlocks, } from "@jxpeng98/martian";
import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main";
import { DatabaseDetails } from "../../ui/settingTabs";
export class UploadBaseCustom {
plugin: MyPlugin;
notion: Client;
agent: any;
dbDetails: DatabaseDetails
constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) {
this.plugin = plugin;
this.dbDetails = dbDetails
}
async deletePage(notionID: string) {
const { notionAPI } = this.dbDetails
return requestUrl({
url: `https://api.notion.com/v1/blocks/${notionID}`,
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + notionAPI,
'Notion-Version': '2022-06-28',
},
body: ''
});
}
async getDataBase(databaseID: string) {
const { notionAPI } = this.dbDetails
const response = await requestUrl({
url: `https://api.notion.com/v1/databases/${databaseID}`,
method: 'GET',
headers: {
'Authorization': 'Bearer ' + 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
}
}
}

View File

@@ -0,0 +1,314 @@
import {App, Notice, TFile} from "obsidian";
import {markdownToBlocks} from "@jxpeng98/martian";
import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main";
import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs";
import {updateYamlInfo} from "../updateYaml";
import {UploadBaseCustom} from "./BaseUpload2NotionCustom";
import fetch from 'node-fetch';
import {i18nConfig} from "../../lang/I18n";
export class Upload2NotionCustom extends UploadBaseCustom {
settings: PluginSettings;
dbDetails: DatabaseDetails;
constructor(plugin: MyPlugin, dbDetails: DatabaseDetails) {
super(plugin, dbDetails);
this.dbDetails = dbDetails;
}
// 因为需要解析notion的block进行对比非常的麻烦
// 暂时就直接删除新建一个page
async updatePage(
notionID: string,
cover: string,
customValues: Record<string, string>,
childArr: any,
) {
await this.deletePage(notionID);
const {databaseID} = this.dbDetails;
const databaseCover = await this.getDataBase(
databaseID
);
if (cover == null) {
cover = databaseCover;
}
return await this.createPage(cover, customValues, childArr);
}
async createPage(
cover: string,
customValues: Record<string, string>,
childArr: any,
) {
const {
databaseID,
customProperties,
notionAPI
} = this.dbDetails;
// check the length of the childArr and split it into chunks of 100
const childArrLength = childArr.length;
let extraArr: any[] = [];
let firstArr: any;
let pushCount = 0;
console.log(`Page includes ${childArrLength} blocks`)
if (childArrLength > 100) {
for (let i = 0; i < childArr.length; i += 100) {
if (i == 0) {
firstArr = childArr.slice(0, 100);
} else {
const chunk = childArr.slice(i, i + 100);
extraArr.push(chunk);
pushCount++;
}
}
} else {
firstArr = childArr;
}
const bodyString: any = this.buildBodyString(customProperties, customValues, firstArr);
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,
},
};
}
console.log(bodyString)
const response = await fetch("https://api.notion.com/v1/pages", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
});
const data: any = await response.json();
if (!response.ok) {
new Notice(`Error ${data.status}: ${data.code} \n ${i18nConfig["CheckConsole"]}`, 5000);
console.log(`Error message: \n ${data.message}`);
} else {
console.log(`Page created: ${data.url}`);
console.log(`Page ID: ${data.id}`);
}
// upload the rest of the blocks
if (pushCount > 0) {
for (let i = 0; i < pushCount; i++) {
const extraBlocks = {
children: extraArr[i],
};
console.log(extraBlocks)
const extraResponse = await fetch(`https://api.notion.com/v1/blocks/${data.id}/children`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(extraBlocks),
});
const extraData: any = await extraResponse.json();
if (!extraResponse.ok) {
new Notice(`Error ${extraData.status}: ${extraData.code} \n ${i18nConfig["CheckConsole"]}`, 5000);
console.log(`Error message: \n ${extraData.message}`);
} else {
console.log(`${i18nConfig["ExtraBlockUploaded"]} to page: ${data.id}`);
if (i === pushCount - 1) {
console.log(`${i18nConfig["BlockUploaded"]} to page: ${data.id}`);
new Notice(`${i18nConfig["BlockUploaded"]} page: ${data.id}`, 5000);
}
}
}
}
return {
response, // for status code
data // for id and url
}
}
async syncMarkdownToNotionCustom(
cover: string,
customValues: Record<string, string>,
markdown: string,
nowFile: TFile,
app: App,
): Promise<any> {
const options = {
strictImageUrls: true,
notionLimits: {
truncate: false,
}
}
let res: any;
const yamlContent: any = yamlFrontMatter.loadFront(markdown);
const __content = yamlContent.__content;
const file2Block = markdownToBlocks(__content, options);
const frontMatter =
app.metadataCache.getFileCache(nowFile)?.frontmatter;
const {abName} = this.dbDetails
const notionIDKey = `NotionID-${abName}`;
const notionID = frontMatter ? frontMatter[notionIDKey] : null;
if (notionID) {
res = await this.updatePage(
notionID,
cover,
customValues,
file2Block,
);
} else {
res = await this.createPage(cover, customValues, file2Block);
}
let {response, data} = res;
// console.log(response)
if (response && response.status === 200) {
await updateYamlInfo(markdown, nowFile, data, app, this.plugin, this.dbDetails);
}
return res;
}
private buildPropertyObject(customName: string, customType: string, customValues: Record<string, any>) {
const value = customValues[customName] || '';
switch (customType) {
case "title":
return {
title: [
{
text: {
content: value,
},
},
],
};
case "rich_text":
return {
rich_text: [
{
text: {
content: value || '',
},
},
],
};
case "date":
return {
date: {
start: value || new Date().toISOString(),
},
};
case "number":
return {
number: Number(value),
};
case "phone_number":
return {
phone_number: value,
};
case "email":
return {
email: value,
};
case "url":
return {
url: value,
};
case "files":
return {
files: Array.isArray(value) ? value.map(url => ({
name: url,
type: "external",
external: {
url: url,
},
})) : [
{
name: value,
type: "external",
external: {
url: value,
},
},
],
};
case "checkbox":
return {
checkbox: Boolean(value) || false,
};
case "select":
return {
select: {
name: value,
},
};
case "multi_select":
return {
multi_select: Array.isArray(value) ? value.map(item => ({name: item})) : [{name: value}],
};
}
}
private buildBodyString(
customProperties: { customName: string; customType: string }[],
customValues: Record<string, string>,
childArr: any,
) {
const properties: { [key: string]: any } = {};
// Only include custom properties that have values
customProperties.forEach(({customName, customType}) => {
if (customValues[customName] !== undefined) {
properties[customName] = this.buildPropertyObject(customName, customType, customValues);
}
}
);
// console.log(properties)
return {
parent: {
database_id: this.dbDetails.databaseID,
},
properties,
children: childArr,
};
}
}

View File

@@ -0,0 +1,57 @@
import { App, Notice } from "obsidian";
import { i18nConfig } from "../../lang/I18n";
import { DatabaseDetails } from "../../ui/settingTabs";
export async function getNowFileMarkdownContentCustom(
app: App,
dbDetails: DatabaseDetails,
) {
const nowFile = app.workspace.getActiveFile();
if (!nowFile) {
new Notice(i18nConfig["open-file"]);
return;
}
let cover = '';
let customValues: Record<string, any> = {};
const FileCache = app.metadataCache.getFileCache(nowFile);
try {
cover = FileCache.frontmatter.coverurl;
// Get custom property names from dbDetails excluding the title type property
const customPropertyNames = dbDetails.customProperties
.filter(property => property.customType !== 'title') // Exclude 'title' type property
.map(property => property.customName);
// Extract custom values from the front matter based on the names
customPropertyNames.forEach(propertyName => {
if (FileCache.frontmatter && FileCache.frontmatter[propertyName] !== undefined) {
customValues[propertyName] = FileCache.frontmatter[propertyName];
}
});
// Check if any of the customProperties has a customType of 'title'
const titleProperty = dbDetails.customProperties.find(property => property.customType === 'title');
// If a 'title' type property exists, use the file's basename as its value
if (titleProperty) {
customValues[titleProperty.customName] = nowFile.basename; // Use 'basename' for the file name without extension
}
} catch (error) {
new Notice(i18nConfig["set-tags-fail"]);
}
if (nowFile) {
const markDownData = await nowFile.vault.read(nowFile);
return {
markDownData,
nowFile,
cover,
customValues,
};
} else {
new Notice(i18nConfig["open-file"]);
return;
}
}

View File

@@ -3,6 +3,8 @@
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"module": "ESNext",
"target": "ES6",
"allowJs": true,
@@ -17,8 +19,8 @@
]
},
"include": [
"**/*.ts"
,
"src/Upload2Notion.ts"
"**/*.ts",
"src/Upload2Notion.ts",
"src/*.ts"
]
}

View File

@@ -1,11 +1,10 @@
// update package.json version
import fs from 'fs';
import fs from "fs";
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
const packageJson = JSON.parse(fs.readFileSync("./package.json", "utf8"));
console.log(packageJson.version);
// get node args
const args = process.argv.slice(2);
@@ -13,14 +12,13 @@ packageJson.version = args[0];
console.log(packageJson.version);
// write package.json
fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2));
fs.writeFileSync("./package.json", JSON.stringify(packageJson, null, 2));
// read manifest.json
const manifestJson = JSON.parse(fs.readFileSync('./manifest.json', 'utf8'));
const manifestJson = JSON.parse(fs.readFileSync("./manifest.json", "utf8"));
manifestJson.version = args[0];
// write manifest.json
fs.writeFileSync('./manifest.json', JSON.stringify(manifestJson, null, 2));
fs.writeFileSync("./manifest.json", JSON.stringify(manifestJson, null, 2));

617
yarn.lock
View File

@@ -2,115 +2,115 @@
# yarn lockfile v1
"@esbuild/android-arm64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz#bc35990f412a749e948b792825eef7df0ce0e073"
integrity sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==
"@esbuild/android-arm64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.5.tgz#276c5f99604054d3dbb733577e09adae944baa90"
integrity sha512-5d1OkoJxnYQfmC+Zd8NBFjkhyCNYwM4n9ODrycTFY6Jk1IGiZ+tjVJDDSwDt77nK+tfpGP4T50iMtVi4dEGzhQ==
"@esbuild/android-arm@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.2.tgz#edd1c8f23ba353c197f5b0337123c58ff2a56999"
integrity sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==
"@esbuild/android-arm@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.5.tgz#4a3cbf14758166abaae8ba9c01a80e68342a4eec"
integrity sha512-bhvbzWFF3CwMs5tbjf3ObfGqbl/17ict2/uwOSfr3wmxDE6VdS2GqY/FuzIPe0q0bdhj65zQsvqfArI9MY6+AA==
"@esbuild/android-x64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.2.tgz#2dcdd6e6f1f2d82ea1b746abd8da5b284960f35a"
integrity sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==
"@esbuild/android-x64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.5.tgz#21a3d11cd4613d2d3c5ccb9e746c254eb9265b0a"
integrity sha512-9t+28jHGL7uBdkBjL90QFxe7DVA+KGqWlHCF8ChTKyaKO//VLuoBricQCgwhOjA1/qOczsw843Fy4cbs4H3DVA==
"@esbuild/darwin-arm64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz#55b36bc06d76f5c243987c1f93a11a80d8fc3b26"
integrity sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==
"@esbuild/darwin-arm64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.5.tgz#714cb839f467d6a67b151ee8255886498e2b9bf6"
integrity sha512-mvXGcKqqIqyKoxq26qEDPHJuBYUA5KizJncKOAf9eJQez+L9O+KfvNFu6nl7SCZ/gFb2QPaRqqmG0doSWlgkqw==
"@esbuild/darwin-x64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz#982524af33a6424a3b5cb44bbd52559623ad719c"
integrity sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==
"@esbuild/darwin-x64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.5.tgz#2c553e97a6d2b4ae76a884e35e6cbab85a990bbf"
integrity sha512-Ly8cn6fGLNet19s0X4unjcniX24I0RqjPv+kurpXabZYSXGM4Pwpmf85WHJN3lAgB8GSth7s5A0r856S+4DyiA==
"@esbuild/freebsd-arm64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz#8e478a0856645265fe79eac4b31b52193011ee06"
integrity sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==
"@esbuild/freebsd-arm64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.5.tgz#d554f556718adb31917a0da24277bf84b6ee87f3"
integrity sha512-GGDNnPWTmWE+DMchq1W8Sd0mUkL+APvJg3b11klSGUDvRXh70JqLAO56tubmq1s2cgpVCSKYywEiKBfju8JztQ==
"@esbuild/freebsd-x64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz#01b96604f2540db023c73809bb8ae6cd1692d6f3"
integrity sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==
"@esbuild/freebsd-x64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.5.tgz#288f7358a3bb15d99e73c65c9adaa3dabb497432"
integrity sha512-1CCwDHnSSoA0HNwdfoNY0jLfJpd7ygaLAp5EHFos3VWJCRX9DMwWODf96s9TSse39Br7oOTLryRVmBoFwXbuuQ==
"@esbuild/linux-arm64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz#7e5d2c7864c5c83ec789b59c77cd9c20d2594916"
integrity sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==
"@esbuild/linux-arm64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.5.tgz#95933ae86325c93cb6b5e8333d22120ecfdc901b"
integrity sha512-o3vYippBmSrjjQUCEEiTZ2l+4yC0pVJD/Dl57WfPwwlvFkrxoSO7rmBZFii6kQB3Wrn/6GwJUPLU5t52eq2meA==
"@esbuild/linux-arm@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz#c32ae97bc0246664a1cfbdb4a98e7b006d7db8ae"
integrity sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==
"@esbuild/linux-arm@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.5.tgz#0acef93aa3e0579e46d33b666627bddb06636664"
integrity sha512-lrWXLY/vJBzCPC51QN0HM71uWgIEpGSjSZZADQhq7DKhPcI6NH1IdzjfHkDQws2oNpJKpR13kv7/pFHBbDQDwQ==
"@esbuild/linux-ia32@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz#3fc4f0fa026057fe885e4a180b3956e704f1ceaa"
integrity sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==
"@esbuild/linux-ia32@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.5.tgz#b6e5c9e80b42131cbd6b1ddaa48c92835f1ed67f"
integrity sha512-MkjHXS03AXAkNp1KKkhSKPOCYztRtK+KXDNkBa6P78F8Bw0ynknCSClO/ztGszILZtyO/lVKpa7MolbBZ6oJtQ==
"@esbuild/linux-loong64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz#633bcaea443f3505fb0ed109ab840c99ad3451a4"
integrity sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==
"@esbuild/linux-loong64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.5.tgz#e5f0cf95a180158b01ff5f417da796a1c09dfbea"
integrity sha512-42GwZMm5oYOD/JHqHska3Jg0r+XFb/fdZRX+WjADm3nLWLcIsN27YKtqxzQmGNJgu0AyXg4HtcSK9HuOk3v1Dw==
"@esbuild/linux-mips64el@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz#e0bff2898c46f52be7d4dbbcca8b887890805823"
integrity sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==
"@esbuild/linux-mips64el@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.5.tgz#ae36fb86c7d5f641f3a0c8472e83dcb6ea36a408"
integrity sha512-kcjndCSMitUuPJobWCnwQ9lLjiLZUR3QLQmlgaBfMX23UEa7ZOrtufnRds+6WZtIS9HdTXqND4yH8NLoVVIkcg==
"@esbuild/linux-ppc64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz#d75798da391f54a9674f8c143b9a52d1dbfbfdde"
integrity sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==
"@esbuild/linux-ppc64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.5.tgz#7960cb1666f0340ddd9eef7b26dcea3835d472d0"
integrity sha512-yJAxJfHVm0ZbsiljbtFFP1BQKLc8kUF6+17tjQ78QjqjAQDnhULWiTA6u0FCDmYT1oOKS9PzZ2z0aBI+Mcyj7Q==
"@esbuild/linux-riscv64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz#012409bd489ed1bb9b775541d4a46c5ded8e6dd8"
integrity sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==
"@esbuild/linux-riscv64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.5.tgz#32207df26af60a3a9feea1783fc21b9817bade19"
integrity sha512-5u8cIR/t3gaD6ad3wNt1MNRstAZO+aNyBxu2We8X31bA8XUNyamTVQwLDA1SLoPCUehNCymhBhK3Qim1433Zag==
"@esbuild/linux-s390x@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz#ece3ed75c5a150de8a5c110f02e97d315761626b"
integrity sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==
"@esbuild/linux-s390x@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.5.tgz#b38d5681db89a3723862dfa792812397b1510a7d"
integrity sha512-Z6JrMyEw/EmZBD/OFEFpb+gao9xJ59ATsoTNlj39jVBbXqoZm4Xntu6wVmGPB/OATi1uk/DB+yeDPv2E8PqZGw==
"@esbuild/linux-x64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz#dea187019741602d57aaf189a80abba261fbd2aa"
integrity sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==
"@esbuild/linux-x64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.5.tgz#46feba2ad041a241379d150f415b472fe3885075"
integrity sha512-psagl+2RlK1z8zWZOmVdImisMtrUxvwereIdyJTmtmHahJTKb64pAcqoPlx6CewPdvGvUKe2Jw+0Z/0qhSbG1A==
"@esbuild/netbsd-x64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz#bbfd7cf9ab236a23ee3a41b26f0628c57623d92a"
integrity sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==
"@esbuild/netbsd-x64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.5.tgz#3b5c1fb068f26bfc681d31f682adf1bea4ef0702"
integrity sha512-kL2l+xScnAy/E/3119OggX8SrWyBEcqAh8aOY1gr4gPvw76la2GlD4Ymf832UCVbmuWeTf2adkZDK+h0Z/fB4g==
"@esbuild/openbsd-x64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz#fa5c4c6ee52a360618f00053652e2902e1d7b4a7"
integrity sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==
"@esbuild/openbsd-x64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.5.tgz#ca6830316ca68056c5c88a875f103ad3235e00db"
integrity sha512-sPOfhtzFufQfTBgRnE1DIJjzsXukKSvZxloZbkJDG383q0awVAq600pc1nfqBcl0ice/WN9p4qLc39WhBShRTA==
"@esbuild/sunos-x64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz#52a2ac8ac6284c02d25df22bb4cfde26fbddd68d"
integrity sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==
"@esbuild/sunos-x64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.5.tgz#9efc4eb9539a7be7d5a05ada52ee43cda0d8e2dd"
integrity sha512-dGZkBXaafuKLpDSjKcB0ax0FL36YXCvJNnztjKV+6CO82tTYVDSH2lifitJ29jxRMoUhgkg9a+VA/B03WK5lcg==
"@esbuild/win32-arm64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz#719ed5870855de8537aef8149694a97d03486804"
integrity sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==
"@esbuild/win32-arm64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.5.tgz#29f8184afa7a02a956ebda4ed638099f4b8ff198"
integrity sha512-dWVjD9y03ilhdRQ6Xig1NWNgfLtf2o/STKTS+eZuF90fI2BhbwD6WlaiCGKptlqXlURVB5AUOxUj09LuwKGDTg==
"@esbuild/win32-ia32@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz#24832223880b0f581962c8660f8fb8797a1e046a"
integrity sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==
"@esbuild/win32-ia32@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.5.tgz#f3de07afb292ecad651ae4bb8727789de2d95b05"
integrity sha512-4liggWIA4oDgUxqpZwrDhmEfAH4d0iljanDOK7AnVU89T6CzHon/ony8C5LeOdfgx60x5cnQJFZwEydVlYx4iw==
"@esbuild/win32-x64@0.19.2":
version "0.19.2"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz#1205014625790c7ff0e471644a878a65d1e34ab0"
integrity sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==
"@esbuild/win32-x64@0.19.5":
version "0.19.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.5.tgz#faad84c41ba12e3a0acb52571df9bff37bee75f6"
integrity sha512-czTrygUsB/jlM8qEW5MD8bgYU2Xg14lo6kBDXW6HdxKjh8M5PzETGiSHaz9MtbXBYDloHNUAUW2tMiKW4KM9Mw==
"@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
@@ -120,9 +120,20 @@
eslint-visitor-keys "^3.3.0"
"@eslint-community/regexpp@^4.5.1":
version "4.10.0"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63"
integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==
version "4.11.0"
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae"
integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==
"@jxpeng98/martian@^1.2.7":
version "1.2.7"
resolved "https://registry.yarnpkg.com/@jxpeng98/martian/-/martian-1.2.7.tgz#43c8e5403064eb0bae1afe44d0c369ecdd1b4888"
integrity sha512-SeGqHRUeDkCP9SEJt5sddXit+VVoGQhx/U1ePiqcww6dbK0gnv2CiDzfgtRkBFW8zoCBzkq50m/UBS0xz/VGig==
dependencies:
"@notionhq/client" "^1.0.4"
remark-gfm "^1.0.0"
remark-math "^4.0.0"
remark-parse "^9.0.0"
unified "^9.2.1"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
@@ -153,17 +164,6 @@
"@types/node-fetch" "^2.5.10"
node-fetch "^2.6.1"
"@tryfabric/martian@^1.2.4":
version "1.2.4"
resolved "https://registry.yarnpkg.com/@tryfabric/martian/-/martian-1.2.4.tgz#3fdfe7129f1c28bccddc34854cf353b0f37c0c02"
integrity sha512-g7SP7beaxrjxLnW//vskra07a1jsJowqp07KMouxh4gCwaF+ItHbRZN8O+1dhJivBi3VdasT71BPyk+8wzEreQ==
dependencies:
"@notionhq/client" "^1.0.4"
remark-gfm "^1.0.0"
remark-math "^4.0.0"
remark-parse "^9.0.0"
unified "^9.2.1"
"@types/codemirror@5.60.8":
version "5.60.8"
resolved "https://registry.yarnpkg.com/@types/codemirror/-/codemirror-5.60.8.tgz#b647d04b470e8e1836dd84b2879988fc55c9de68"
@@ -184,9 +184,9 @@
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
"@types/hast@^3.0.0":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.3.tgz#7f75e6b43bc3f90316046a287d9ad3888309f7e1"
integrity sha512-2fYGlaDy/qyLlhidX42wAH0KBi2TCjKMH8CHmBXgRlJ3Y+OXTiqsPQ6IWarZKwF1JoUcAJdPogv1d4b0COTpmQ==
version "3.0.4"
resolved "https://registry.yarnpkg.com/@types/hast/-/hast-3.0.4.tgz#1d6b39993b82cea6ad783945b0508c25903e15aa"
integrity sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==
dependencies:
"@types/unist" "*"
@@ -201,9 +201,9 @@
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
"@types/katex@^0.16.0":
version "0.16.6"
resolved "https://registry.yarnpkg.com/@types/katex/-/katex-0.16.6.tgz#6dd82f78a1a1d2eceeff6bb97ba98d7c062454cb"
integrity sha512-rZYO1HInM99rAFYNwGqbYPxHZHxu2IwZYKj4bJ4oh6edVrm1UId8mmbHIZLBtG253qU6y3piag0XYe/joNnwzQ==
version "0.16.7"
resolved "https://registry.yarnpkg.com/@types/katex/-/katex-0.16.7.tgz#03ab680ab4fa4fbc6cb46ecf987ecad5d8019868"
integrity sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==
"@types/mdast@^3.0.0":
version "3.0.15"
@@ -213,9 +213,9 @@
"@types/unist" "^2"
"@types/mdast@^4.0.0":
version "4.0.3"
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.3.tgz#1e011ff013566e919a4232d1701ad30d70cab333"
integrity sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==
version "4.0.4"
resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-4.0.4.tgz#7ccf72edd2f1aa7dd3437e180c64373585804dd6"
integrity sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==
dependencies:
"@types/unist" "*"
@@ -225,29 +225,29 @@
integrity sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==
"@types/node-fetch@^2.5.10":
version "2.6.9"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.9.tgz#15f529d247f1ede1824f7e7acdaa192d5f28071e"
integrity sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA==
version "2.6.11"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.11.tgz#9b39b78665dae0e82a08f02f4967d62c66f95d24"
integrity sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==
dependencies:
"@types/node" "*"
form-data "^4.0.0"
"@types/node@*", "@types/node@^20.5.7":
version "20.9.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.9.0.tgz#bfcdc230583aeb891cf51e73cfdaacdd8deae298"
integrity sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==
version "20.14.12"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.14.12.tgz#129d7c3a822cb49fc7ff661235f19cfefd422b49"
integrity sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==
dependencies:
undici-types "~5.26.4"
"@types/semver@^7.5.0":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.5.tgz#deed5ab7019756c9c90ea86139106b0346223f35"
integrity sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==
version "7.5.8"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e"
integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==
"@types/tern@*":
version "0.23.7"
resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.7.tgz#f3c27fb7d6db5e86d7f069e7d5124bfa677a5b2d"
integrity sha512-0YS9XCZ0LAhlP11HV9SqncUYyz9Ggsgc7Om/AmchKvoeFyj0qPaJmX6rJ93mJVExizWDzUMb49gAtVpI1uHd8Q==
version "0.23.9"
resolved "https://registry.yarnpkg.com/@types/tern/-/tern-0.23.9.tgz#6f6093a4a9af3e6bb8dde528e024924d196b367c"
integrity sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==
dependencies:
"@types/estree" "*"
@@ -269,16 +269,16 @@
"@types/js-yaml" "*"
"@types/node" "*"
"@typescript-eslint/eslint-plugin@^6.11.0":
version "6.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz#52aae65174ff526576351f9ccd41cea01001463f"
integrity sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==
"@typescript-eslint/eslint-plugin@^6.16.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz#30830c1ca81fd5f3c2714e524c4303e0194f9cd3"
integrity sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==
dependencies:
"@eslint-community/regexpp" "^4.5.1"
"@typescript-eslint/scope-manager" "6.11.0"
"@typescript-eslint/type-utils" "6.11.0"
"@typescript-eslint/utils" "6.11.0"
"@typescript-eslint/visitor-keys" "6.11.0"
"@typescript-eslint/scope-manager" "6.21.0"
"@typescript-eslint/type-utils" "6.21.0"
"@typescript-eslint/utils" "6.21.0"
"@typescript-eslint/visitor-keys" "6.21.0"
debug "^4.3.4"
graphemer "^1.4.0"
ignore "^5.2.4"
@@ -286,78 +286,79 @@
semver "^7.5.4"
ts-api-utils "^1.0.1"
"@typescript-eslint/parser@^6.11.0":
version "6.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.11.0.tgz#9640d9595d905f3be4f278bf515130e6129b202e"
integrity sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==
"@typescript-eslint/parser@^6.16.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.21.0.tgz#af8fcf66feee2edc86bc5d1cf45e33b0630bf35b"
integrity sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==
dependencies:
"@typescript-eslint/scope-manager" "6.11.0"
"@typescript-eslint/types" "6.11.0"
"@typescript-eslint/typescript-estree" "6.11.0"
"@typescript-eslint/visitor-keys" "6.11.0"
"@typescript-eslint/scope-manager" "6.21.0"
"@typescript-eslint/types" "6.21.0"
"@typescript-eslint/typescript-estree" "6.21.0"
"@typescript-eslint/visitor-keys" "6.21.0"
debug "^4.3.4"
"@typescript-eslint/scope-manager@6.11.0":
version "6.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz#621f603537c89f4d105733d949aa4d55eee5cea8"
integrity sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==
"@typescript-eslint/scope-manager@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz#ea8a9bfc8f1504a6ac5d59a6df308d3a0630a2b1"
integrity sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==
dependencies:
"@typescript-eslint/types" "6.11.0"
"@typescript-eslint/visitor-keys" "6.11.0"
"@typescript-eslint/types" "6.21.0"
"@typescript-eslint/visitor-keys" "6.21.0"
"@typescript-eslint/type-utils@6.11.0":
version "6.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.11.0.tgz#d0b8b1ab6c26b974dbf91de1ebc5b11fea24e0d1"
integrity sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==
"@typescript-eslint/type-utils@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz#6473281cfed4dacabe8004e8521cee0bd9d4c01e"
integrity sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==
dependencies:
"@typescript-eslint/typescript-estree" "6.11.0"
"@typescript-eslint/utils" "6.11.0"
"@typescript-eslint/typescript-estree" "6.21.0"
"@typescript-eslint/utils" "6.21.0"
debug "^4.3.4"
ts-api-utils "^1.0.1"
"@typescript-eslint/types@6.11.0":
version "6.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.11.0.tgz#8ad3aa000cbf4bdc4dcceed96e9b577f15e0bf53"
integrity sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==
"@typescript-eslint/types@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.21.0.tgz#205724c5123a8fef7ecd195075fa6e85bac3436d"
integrity sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==
"@typescript-eslint/typescript-estree@6.11.0":
version "6.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.11.0.tgz#7b52c12a623bf7f8ec7f8a79901b9f98eb5c7990"
integrity sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==
"@typescript-eslint/typescript-estree@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz#c47ae7901db3b8bddc3ecd73daff2d0895688c46"
integrity sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==
dependencies:
"@typescript-eslint/types" "6.11.0"
"@typescript-eslint/visitor-keys" "6.11.0"
"@typescript-eslint/types" "6.21.0"
"@typescript-eslint/visitor-keys" "6.21.0"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
minimatch "9.0.3"
semver "^7.5.4"
ts-api-utils "^1.0.1"
"@typescript-eslint/utils@6.11.0":
version "6.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.11.0.tgz#11374f59ef4cea50857b1303477c08aafa2ca604"
integrity sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==
"@typescript-eslint/utils@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.21.0.tgz#4714e7a6b39e773c1c8e97ec587f520840cd8134"
integrity sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==
dependencies:
"@eslint-community/eslint-utils" "^4.4.0"
"@types/json-schema" "^7.0.12"
"@types/semver" "^7.5.0"
"@typescript-eslint/scope-manager" "6.11.0"
"@typescript-eslint/types" "6.11.0"
"@typescript-eslint/typescript-estree" "6.11.0"
"@typescript-eslint/scope-manager" "6.21.0"
"@typescript-eslint/types" "6.21.0"
"@typescript-eslint/typescript-estree" "6.21.0"
semver "^7.5.4"
"@typescript-eslint/visitor-keys@6.11.0":
version "6.11.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.11.0.tgz#d991538788923f92ec40d44389e7075b359f3458"
integrity sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==
"@typescript-eslint/visitor-keys@6.21.0":
version "6.21.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz#87a99d077aa507e20e238b11d56cc26ade45fe47"
integrity sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==
dependencies:
"@typescript-eslint/types" "6.11.0"
"@typescript-eslint/types" "6.21.0"
eslint-visitor-keys "^3.4.1"
agent-base@^7.0.2:
version "7.1.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434"
integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==
version "7.1.1"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317"
integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==
dependencies:
debug "^4.3.4"
@@ -388,14 +389,26 @@ bail@^2.0.0:
resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d"
integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==
braces@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
builtin-modules@^3.2.0:
brace-expansion@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
dependencies:
balanced-match "^1.0.0"
braces@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies:
fill-range "^7.1.1"
builtin-modules@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
@@ -447,10 +460,15 @@ commander@^8.3.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
data-uri-to-buffer@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e"
integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==
debug@4, debug@^4.0.0, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
version "4.3.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
dependencies:
ms "2.1.2"
@@ -485,33 +503,33 @@ dir-glob@^3.0.1:
dependencies:
path-type "^4.0.0"
esbuild@0.19.2:
version "0.19.2"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.2.tgz#b1541828a89dfb6f840d38538767c6130dca2aac"
integrity sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==
esbuild@0.19.5:
version "0.19.5"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.5.tgz#53a0e19dfbf61ba6c827d51a80813cf071239a8c"
integrity sha512-bUxalY7b1g8vNhQKdB24QDmHeY4V4tw/s6Ak5z+jJX9laP5MoQseTOMemAr0gxssjNcH0MCViG8ONI2kksvfFQ==
optionalDependencies:
"@esbuild/android-arm" "0.19.2"
"@esbuild/android-arm64" "0.19.2"
"@esbuild/android-x64" "0.19.2"
"@esbuild/darwin-arm64" "0.19.2"
"@esbuild/darwin-x64" "0.19.2"
"@esbuild/freebsd-arm64" "0.19.2"
"@esbuild/freebsd-x64" "0.19.2"
"@esbuild/linux-arm" "0.19.2"
"@esbuild/linux-arm64" "0.19.2"
"@esbuild/linux-ia32" "0.19.2"
"@esbuild/linux-loong64" "0.19.2"
"@esbuild/linux-mips64el" "0.19.2"
"@esbuild/linux-ppc64" "0.19.2"
"@esbuild/linux-riscv64" "0.19.2"
"@esbuild/linux-s390x" "0.19.2"
"@esbuild/linux-x64" "0.19.2"
"@esbuild/netbsd-x64" "0.19.2"
"@esbuild/openbsd-x64" "0.19.2"
"@esbuild/sunos-x64" "0.19.2"
"@esbuild/win32-arm64" "0.19.2"
"@esbuild/win32-ia32" "0.19.2"
"@esbuild/win32-x64" "0.19.2"
"@esbuild/android-arm" "0.19.5"
"@esbuild/android-arm64" "0.19.5"
"@esbuild/android-x64" "0.19.5"
"@esbuild/darwin-arm64" "0.19.5"
"@esbuild/darwin-x64" "0.19.5"
"@esbuild/freebsd-arm64" "0.19.5"
"@esbuild/freebsd-x64" "0.19.5"
"@esbuild/linux-arm" "0.19.5"
"@esbuild/linux-arm64" "0.19.5"
"@esbuild/linux-ia32" "0.19.5"
"@esbuild/linux-loong64" "0.19.5"
"@esbuild/linux-mips64el" "0.19.5"
"@esbuild/linux-ppc64" "0.19.5"
"@esbuild/linux-riscv64" "0.19.5"
"@esbuild/linux-s390x" "0.19.5"
"@esbuild/linux-x64" "0.19.5"
"@esbuild/netbsd-x64" "0.19.5"
"@esbuild/openbsd-x64" "0.19.5"
"@esbuild/sunos-x64" "0.19.5"
"@esbuild/win32-arm64" "0.19.5"
"@esbuild/win32-ia32" "0.19.5"
"@esbuild/win32-x64" "0.19.5"
escape-string-regexp@^4.0.0:
version "4.0.0"
@@ -545,16 +563,24 @@ fast-glob@^3.2.9:
micromatch "^4.0.4"
fastq@^1.6.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==
version "1.17.1"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47"
integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==
dependencies:
reusify "^1.0.4"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
version "3.2.0"
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==
dependencies:
node-domexception "^1.0.0"
web-streams-polyfill "^3.0.3"
fill-range@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies:
to-regex-range "^5.0.1"
@@ -567,6 +593,13 @@ form-data@^4.0.0:
combined-stream "^1.0.8"
mime-types "^2.1.12"
formdata-polyfill@^4.0.10:
version "4.0.10"
resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423"
integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==
dependencies:
fetch-blob "^3.1.2"
glob-parent@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
@@ -592,17 +625,17 @@ graphemer@^1.4.0:
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
https-proxy-agent@^7.0.2:
version "7.0.2"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b"
integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==
version "7.0.5"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2"
integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==
dependencies:
agent-base "^7.0.2"
debug "4"
ignore@^5.2.0, ignore@^5.2.4:
version "5.3.0"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78"
integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==
version "5.3.1"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
is-alphabetical@^1.0.0:
version "1.0.4"
@@ -675,9 +708,9 @@ katex@^0.12.0:
commander "^2.19.0"
katex@^0.16.0:
version "0.16.9"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.9.tgz#bc62d8f7abfea6e181250f85a56e4ef292dcb1fa"
integrity sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==
version "0.16.11"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.11.tgz#4bc84d5584f996abece5f01c6ad11304276a33f5"
integrity sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==
dependencies:
commander "^8.3.0"
@@ -691,13 +724,6 @@ longest-streak@^3.0.0:
resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4"
integrity sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
markdown-table@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b"
@@ -726,9 +752,9 @@ mdast-util-from-markdown@^0.8.0:
unist-util-stringify-position "^2.0.0"
mdast-util-from-markdown@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz#52f14815ec291ed061f2922fd14d6689c810cb88"
integrity sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==
version "2.0.1"
resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz#32a6e8f512b416e1f51eb817fc64bd867ebcd9cc"
integrity sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==
dependencies:
"@types/mdast" "^4.0.0"
"@types/unist" "^3.0.0"
@@ -808,9 +834,9 @@ mdast-util-math@^3.0.0:
unist-util-remove-position "^5.0.0"
mdast-util-phrasing@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.0.0.tgz#468cbbb277375523de807248b8ad969feb02a5c7"
integrity sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==
version "4.1.0"
resolved "https://registry.yarnpkg.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz#7cc0a8dec30eaf04b7b1a9661a92adb3382aa6e3"
integrity sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==
dependencies:
"@types/mdast" "^4.0.0"
unist-util-is "^6.0.0"
@@ -859,9 +885,9 @@ merge2@^1.3.0, merge2@^1.4.1:
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
micromark-core-commonmark@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz#50740201f0ee78c12a675bf3e68ffebc0bf931a3"
integrity sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==
version "2.0.1"
resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz#9a45510557d068605c6e9a80f282b2bb8581e43d"
integrity sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==
dependencies:
decode-named-character-reference "^1.0.0"
devlop "^1.0.0"
@@ -934,9 +960,9 @@ micromark-extension-math@^0.1.0:
micromark "~2.11.0"
micromark-extension-math@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/micromark-extension-math/-/micromark-extension-math-3.0.0.tgz#c7a47d6ce990812243ad3946a30bb60e4c2a8c76"
integrity sha512-iJ2Q28vBoEovLN5o3GO12CpqorQRYDPT+p4zW50tGwTfJB+iv/VnB6Ini+gqa24K97DwptMBBIvVX6Bjk49oyQ==
version "3.1.0"
resolved "https://registry.yarnpkg.com/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz#c42ee3b1dd5a9a03584e83dd8f08e3de510212c1"
integrity sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==
dependencies:
"@types/katex" "^0.16.0"
devlop "^1.0.0"
@@ -994,9 +1020,9 @@ micromark-factory-whitespace@^2.0.0:
micromark-util-types "^2.0.0"
micromark-util-character@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.0.1.tgz#52b824c2e2633b6fb33399d2ec78ee2a90d6b298"
integrity sha512-3wgnrmEAJ4T+mGXAUfMvMAbxU9RDG43XmGce4j6CwPtVxB3vfwXSZ6KhFwDzZ3mZHhmPimMAXg71veiBGzeAZw==
version "2.1.0"
resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-2.1.0.tgz#31320ace16b4644316f6bf057531689c71e2aee1"
integrity sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==
dependencies:
micromark-util-symbol "^2.0.0"
micromark-util-types "^2.0.0"
@@ -1076,9 +1102,9 @@ micromark-util-sanitize-uri@^2.0.0:
micromark-util-symbol "^2.0.0"
micromark-util-subtokenize@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz#9f412442d77e0c5789ffdf42377fa8a2bcbdf581"
integrity sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==
version "2.0.1"
resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz#76129c49ac65da6e479c09d0ec4b5f29ec6eace5"
integrity sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==
dependencies:
devlop "^1.0.0"
micromark-util-chunked "^2.0.0"
@@ -1127,11 +1153,11 @@ micromark@^4.0.0:
micromark-util-types "^2.0.0"
micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
version "4.0.7"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.7.tgz#33e8190d9fe474a9895525f5618eee136d46c2e5"
integrity sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==
dependencies:
braces "^3.0.2"
braces "^3.0.3"
picomatch "^2.3.1"
mime-db@1.52.0:
@@ -1146,6 +1172,13 @@ mime-types@^2.1.12:
dependencies:
mime-db "1.52.0"
minimatch@9.0.3:
version "9.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==
dependencies:
brace-expansion "^2.0.1"
moment@2.29.4:
version "2.29.4"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
@@ -1161,6 +1194,11 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
node-domexception@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5"
integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==
node-fetch@^2.6.1:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
@@ -1168,10 +1206,19 @@ node-fetch@^2.6.1:
dependencies:
whatwg-url "^5.0.0"
obsidian@latest:
version "1.4.11"
resolved "https://registry.yarnpkg.com/obsidian/-/obsidian-1.4.11.tgz#5cba594c83a74ebad58b630c610265018abdadaa"
integrity sha512-BCVYTvaXxElJMl6MMbDdY/CGK+aq18SdtDY/7vH8v6BxCBQ6KF4kKxL0vG9UZ0o5qh139KpUoJHNm+6O5dllKA==
node-fetch@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b"
integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==
dependencies:
data-uri-to-buffer "^4.0.0"
fetch-blob "^3.1.4"
formdata-polyfill "^4.0.10"
obsidian@^1.6.6:
version "1.6.6"
resolved "https://registry.yarnpkg.com/obsidian/-/obsidian-1.6.6.tgz#d45c4021c291765e1b77ed4a1c645e562ff6e77f"
integrity sha512-GZHzeOiwmw/wBjB5JwrsxAZBLqxGQmqtEKSvJJvT0LtTcqeOFnV8jv0ZK5kO7hBb44WxJc+LdS7mZgLXbb+qXQ==
dependencies:
"@types/codemirror" "5.60.8"
moment "2.29.4"
@@ -1259,11 +1306,9 @@ run-parallel@^1.1.9:
queue-microtask "^1.2.2"
semver@^7.5.4:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies:
lru-cache "^6.0.0"
version "7.6.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
slash@^3.0.0:
version "3.0.0"
@@ -1293,14 +1338,14 @@ trough@^1.0.0:
integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==
trough@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876"
integrity sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==
version "2.2.0"
resolved "https://registry.yarnpkg.com/trough/-/trough-2.2.0.tgz#94a60bd6bd375c152c1df911a4b11d5b0256f50f"
integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==
ts-api-utils@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331"
integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==
version "1.3.0"
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
tslib@2.6.2:
version "2.6.2"
@@ -1318,9 +1363,9 @@ undici-types@~5.26.4:
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
unified@^11.0.0:
version "11.0.4"
resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.4.tgz#f4be0ac0fe4c88cb873687c07c64c49ed5969015"
integrity sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==
version "11.0.5"
resolved "https://registry.yarnpkg.com/unified/-/unified-11.0.5.tgz#f66677610a5c0a9ee90cab2b8d4d66037026d9e1"
integrity sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==
dependencies:
"@types/unist" "^3.0.0"
bail "^2.0.0"
@@ -1428,14 +1473,19 @@ vfile@^4.0.0:
vfile-message "^2.0.0"
vfile@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.1.tgz#1e8327f41eac91947d4fe9d237a2dd9209762536"
integrity sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==
version "6.0.2"
resolved "https://registry.yarnpkg.com/vfile/-/vfile-6.0.2.tgz#ef49548ea3d270097a67011921411130ceae7deb"
integrity sha512-zND7NlS8rJYb/sPqkb13ZvbbUoExdbi4w3SfRrMq6R3FvnLQmmfpajJNITuuYm6AZ5uao9vy4BAos3EXBPf2rg==
dependencies:
"@types/unist" "^3.0.0"
unist-util-stringify-position "^4.0.0"
vfile-message "^4.0.0"
web-streams-polyfill@^3.0.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b"
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
@@ -1449,11 +1499,6 @@ whatwg-url@^5.0.0:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
yaml-front-matter@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/yaml-front-matter/-/yaml-front-matter-4.1.1.tgz#66eaa9a998fc3dd723708c73aa8e06e79cac91c9"
@@ -1463,9 +1508,9 @@ yaml-front-matter@^4.1.1:
js-yaml "^3.14.1"
yaml@^2.3.4:
version "2.3.4"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.4.tgz#53fc1d514be80aabf386dc6001eb29bf3b7523b2"
integrity sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==
version "2.5.0"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.5.0.tgz#c6165a721cf8000e91c36490a41d7be25176cf5d"
integrity sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==
zwitch@^1.0.0:
version "1.0.5"