Compare commits

...

19 Commits
1.1.0 ... 2.0.0

Author SHA1 Message Date
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
28 changed files with 1387 additions and 882 deletions

View File

@@ -4,7 +4,6 @@ on:
push:
branches:
- "*"
- "!master"
env:
PLUGIN_NAME: share-to-notionnext # Change this to match the id of your plugin.

View File

@@ -5,6 +5,20 @@
所以我在[原作者](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
- 修复设置中自定义标题的显示问题。
- 增加了标签同步的控制按钮。以减少空标签集造成的上传失败。

View File

@@ -1,632 +0,0 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
<meta charset="utf-8">
<meta name="generator" content="quarto-1.3.450">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>readme</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
vertical-align: middle;
}
/* CSS for syntax highlighting */
pre > code.sourceCode { white-space: pre; position: relative; }
pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
pre > code.sourceCode > span:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode > span { color: inherit; text-decoration: inherit; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
pre > code.sourceCode { white-space: pre-wrap; }
pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
}
pre.numberSource code
{ counter-reset: source-line 0; }
pre.numberSource code > span
{ position: relative; left: -4em; counter-increment: source-line; }
pre.numberSource code > span > a:first-child::before
{ content: counter(source-line);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
}
pre.numberSource { margin-left: 3em; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
}
</style>
<script src="README_files/libs/clipboard/clipboard.min.js"></script>
<script src="README_files/libs/quarto-html/quarto.js"></script>
<script src="README_files/libs/quarto-html/popper.min.js"></script>
<script src="README_files/libs/quarto-html/tippy.umd.min.js"></script>
<script src="README_files/libs/quarto-html/anchor.min.js"></script>
<link href="README_files/libs/quarto-html/tippy.css" rel="stylesheet">
<link href="README_files/libs/quarto-html/quarto-syntax-highlighting.css" rel="stylesheet" id="quarto-text-highlighting-styles">
<script src="README_files/libs/bootstrap/bootstrap.min.js"></script>
<link href="README_files/libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
<link href="README_files/libs/bootstrap/bootstrap.min.css" rel="stylesheet" id="quarto-bootstrap" data-mode="light">
</head>
<body class="fullcontent">
<div id="quarto-content" class="page-columns page-rows-contents page-layout-article">
<main class="content" id="quarto-document-content">
<section id="obsidian-to-notionnext" class="level1">
<h1>Obsidian to NotionNext</h1>
<p><a href="https://github.com/jxpeng98/obsidian-to-NotionNext/actions/workflows/test.yml"><img src="https://github.com/jxpeng98/obsidian-to-NotionNext/actions/workflows/test.yml/badge.svg" class="img-fluid" alt="test.yml"></a> <a href="https://github.com/jxpeng98/obsidian-to-NotionNext/actions/workflows/release.yml"><img src="https://github.com/jxpeng98/obsidian-to-NotionNext/actions/workflows/release.yml/badge.svg" class="img-fluid" alt="Release Obsidian plugin"></a> <a href="https://GitHub.com/jxpeng98/obsidian-to-NotionNext/releases/"><img src="https://img.shields.io/github/downloads/jxpeng98/obsidian-to-NotionNext/total.svg" class="img-fluid" alt="Github all releases"></a> <a href="https://github.com/jxpeng98/obsidian-to-NotionNext/releases"><img src="https://badgen.net/github/release/jxpeng98/obsidian-to-NotionNext.png" class="img-fluid" alt="GitLab latest release"></a></p>
<p><a href="README-zh.md">中文文档</a></p>
<p>Thanks to the <a href="https://github.com/EasyChris/obsidian-to-notion">original author</a> 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 <a href="https://github.com/tangly1024/NotionNext">NotionNext</a> to set up their website, this presents some limitations. Every time I import, I need to make a lot of modifications.</p>
<p>Thus, based on the <a href="https://github.com/EasyChris/obsidian-to-notion">original authors work</a>, Ive added a feature to match the <a href="https://github.com/tangly1024/NotionNext">NotionNext</a> template. This way, you can edit directly in Obsidian and publish with a single click after organizing.</p>
<p><strong>Now, support both NotionNext and General databases.</strong></p>
<section id="update" class="level2">
<h2 class="anchored" data-anchor-id="update">Update</h2>
<section id="section" class="level3">
<h3 class="anchored" data-anchor-id="section">1.0.1</h3>
<ul>
<li>Fix the custom name element display bug in the settings.</li>
</ul>
</section>
<section id="big-update" class="level3">
<h3 class="anchored" data-anchor-id="big-update">1.0.0 (Big Update)</h3>
<ul>
<li>From this version, you can <strong>modify the first column name (title column, default: title)</strong> as you want. (<strong>Note: You need to have the tags column in your Notion General database, and add <code>tags:</code> in your markdown frontmatter. If not, you will receive <code>network error 400</code>. But you can leave the <code>tags:</code> blank.</strong>)</li>
</ul>
<p><img src="https://minioapi.pjx.ac.cn/img1/2023/11/4a298b9be3990e9d2201bf2f50ca5a0a.png" class="img-fluid"> Like this: <img src="https://minioapi.pjx.ac.cn/img1/2023/11/4cd8d79cd9dd9dde299e39c666cb3473.gif" class="img-fluid"></p>
<ul>
<li>Add a switch button to control whether display the setting tabs in the plugin settings for both NotionNext and Notion General databases.</li>
</ul>
<p><img src="https://minioapi.pjx.ac.cn/img1/2023/11/becb60fc44783842da4b3cf4c322f363.gif" class="img-fluid"></p>
</section>
<section id="section-1" class="level3">
<h3 class="anchored" data-anchor-id="section-1">0.2.6</h3>
<ul>
<li>Add a switch button to control whether to show the upload command in the command palette.</li>
</ul>
<p><img src="https://minioapi.pjx.ac.cn/img1/2023/11/147c6a4eaa34da41a6f102558ed77106.png" class="img-fluid"> If you turn off the button for the General database, you wont see the option for the General database in the upload command list.</p>
<p>If you turn off the button for the NotionNext database, you wont see the option for the NotionNext database in the upload command list.</p>
<p><img src="https://minioapi.pjx.ac.cn/img1/2023/11/70b8e4fc2148688ccbd6cfc53ce339a2.png" class="img-fluid"></p>
</section>
<section id="section-2" class="level3">
<h3 class="anchored" data-anchor-id="section-2">0.2.3</h3>
<ul>
<li>Fix the bug, now you can update normally.</li>
</ul>
</section>
<section id="section-3" class="level3">
<h3 class="anchored" data-anchor-id="section-3">0.2.2</h3>
<ul>
<li>Support both NotionNext and General Notion database.</li>
<li>You can have one NotionNext and one General Notion database.</li>
<li>General Notion database can only have <code>title</code> and <code>tags</code> columns, and <code>tags</code> columns should be the multi-selected property. <strong>the name of the columns is case sensitive. You should use small letter.</strong></li>
</ul>
<p><img src="https://minioapi.pjx.ac.cn/img1/2023/11/712a12081d855aa60f82a7b46913ab7e.gif" class="img-fluid"></p>
<p><img src="https://minioapi.pjx.ac.cn/img1/2023/11/9de76cecceef74c78884ddfc1c221659.gif" class="img-fluid"></p>
</section>
<section id="section-4" class="level3">
<h3 class="anchored" data-anchor-id="section-4">0.2.1</h3>
<ul>
<li>Restructure the code</li>
</ul>
</section>
<section id="section-5" class="level3">
<h3 class="anchored" data-anchor-id="section-5">0.2.0</h3>
<ul>
<li>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. <strong>However, only NotionNext database is supported for now.</strong></li>
</ul>
</section>
<section id="section-6" class="level3">
<h3 class="anchored" data-anchor-id="section-6">0.1.10</h3>
<ul>
<li>Fix the Chinese support in the settings.</li>
</ul>
</section>
<section id="section-7" class="level3">
<h3 class="anchored" data-anchor-id="section-7">0.1.8</h3>
<ul>
<li>Rebuild the uploadCommand function, and add one button to select the different databases. <strong>However, only NotionNext database is supported for now.</strong></li>
</ul>
</section>
<section id="section-8" class="level3">
<h3 class="anchored" data-anchor-id="section-8">0.1.7</h3>
<ul class="task-list">
<li><input type="checkbox" checked="">Removed the <code>convert tag</code> option. Now, you can directly add tags in the YAML front matter. If you dont want to add tags, you can delete the tags in the YAML front matter or leave the tags blank.</li>
</ul>
</section>
</section>
<section id="how-to-use" class="level2">
<h2 class="anchored" data-anchor-id="how-to-use">How to Use</h2>
<section id="precautions" class="level3">
<h3 class="anchored" data-anchor-id="precautions">Precautions</h3>
<p>For now, this plugin is exclusively for <a href="https://github.com/tangly1024/NotionNext">NotionNext</a>. If youre not using this template, youll keep receiving <code>error 400</code>.</p>
<p>For those without a NotionNext requirement, please use the original <a href="https://github.com/EasyChris/obsidian-to-notion">Obsidian-to-notion</a>.</p>
</section>
<section id="pre-installation-steps" class="level3">
<h3 class="anchored" data-anchor-id="pre-installation-steps">Pre-Installation Steps</h3>
<p>Before installing the plugin, you must have set up the following:</p>
<ol type="1">
<li>Your NotionNext database.</li>
<li>According to the original authors readme.md, set up the Notion API, and it should already be associated with your NotionNext repository.</li>
<li>NotionNext Database ID</li>
<li>Your NotionNext database should have the following contents:
<ul>
<li>type</li>
<li>title</li>
<li>slug</li>
<li>category</li>
<li>tags</li>
<li>date</li>
<li>status</li>
<li>summary</li>
<li>password</li>
<li>icon</li>
</ul></li>
</ol>
<p>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.</p>
<p><strong>If youve directly copied the NotionNext template, these contents should already exist. Ive made changes to this plugin based on the original authors work. All you need to ensure is that your database has the above content, and every letter is in lowercase!!!</strong></p>
<p><strong>⚠️⚠️⚠️: All headers are in lowercase!!! The order doesnt matter!</strong></p>
</section>
<section id="plugin-installation" class="level3">
<h3 class="anchored" data-anchor-id="plugin-installation">Plugin Installation</h3>
<section id="install-via-community-plugins" class="level4">
<h4 class="anchored" data-anchor-id="install-via-community-plugins">Install via Community Plugins</h4>
<p>Open <code>Obsidian settings -&gt; Community Plugins -&gt; Browse -&gt; NotionNext -&gt; Share to NotionNext</code></p>
</section>
<section id="mannually-install" class="level4">
<h4 class="anchored" data-anchor-id="mannually-install">Mannually Install</h4>
<ol type="1">
<li>Close Obsidian.</li>
<li>Download the plugin file from Release and unzip it into your Obsidian plugin directory.</li>
<li>Re-open Obsidian, go to settings, and enable the plugin.</li>
<li>In the settings, find Obsidian to NotionNext, and enter your NotionNext Database ID and API token.</li>
</ol>
</section>
</section>
<section id="how-to-use-1" class="level3">
<h3 class="anchored" data-anchor-id="how-to-use-1">How to Use</h3>
</section>
<section id="using-the-plugin" class="level3">
<h3 class="anchored" data-anchor-id="using-the-plugin">Using the Plugin</h3>
<p>In the repository, I have uploaded a template which you can directly copy into your template folder. After that, use Obsidians template feature to create a new note with one click.</p>
<p>If you dont want to use the template, you can also directly create a new file in Obsidian and then copy the content below. Then save it.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co">---</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="co"># default value has been set.</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="co"># for any unwanted value, you can delete it or set it to empty.</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co"># for example, if you donot want to set password, you can delete password: "1234" or set it to password: ""</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="co"># 我已经在插件中设置了默认值,如果有不需要的选项,可以直接删除。</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="co"># 例如你不需要密码选项你可以将password: "1234"删除,或者将它设置为空。</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="co"># !!!!!!!!!!!!</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="co"># 现在阶段一定不要修改表头的名字, please do not change the name of the header in YAML front matter</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="co"># !!!!!!!!!!!!</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="an">titleicon:</span><span class="co"> 📎 # emoji icon, default is 📜, 默认是📜</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="an">date:</span><span class="co"> 2023-07-23 # default is today 默认是今天。 Format is YYYY-MM-DD 格式是YYYY-MM-DD</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="an">coverurl:</span><span class="co"> https://img.jxpeng.dev/2023/08/843e27a210847f05a0f7cfb121fec100.jpg # default is empty 默认是空</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="an">type:</span><span class="co"> Post # Post or Page, default is Post 默认是Post</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="an">slug:</span><span class="co"> test # slug for url, default is empty 默认是空</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="an">stats:</span><span class="co"> Draft # Draft, Invisible, Published, default is Draft 默认是Draft</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a><span class="an">category:</span><span class="co"> test # default is 'Obsidian' 默认是'Obsidian'</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a><span class="an">summary:</span><span class="co"> this is a summary for test post # default is empty 默认是空</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="an">icon:</span><span class="co"> fa-solid fa-camera # you can ignore this, default is empty 默认是空,可直接删除</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a><span class="an">password:</span><span class="co"> "1234" # if you donot want to set password, you can delete this line, default is empty 默认是空,可直接删除</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a><span class="co"># 现在必须开启tags选项否则会报错</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a><span class="an">tags:</span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a><span class="co"> - test # tags for post</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a><span class="co"> - web # add more tags if you want</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a><span class="co">---</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a>Contents Below</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p>Usage of the template is as follows: <img src="https://minioapi.pjx.ac.cn/img1/2023/09/354b950e5777b48832c2475e4a31f2cc.gif" class="img-fluid"></p>
<p><strong>Plugin preview is shown below</strong> <img src="https://minioapi.pjx.ac.cn/img1/2023/09/d6199619b68fab218fca8ae0cebece78.gif" class="img-fluid"></p>
<hr>
<p><strong>Original README.md</strong></p>
<p>Many Thanks for the original authors work. Ive only made some changes to the original authors work. If you find this plugin useful, please give the <a href="https://github.com/EasyChris/obsidian-to-notion">original author</a> a star.</p>
</section>
</section>
</section>
<section id="obsidian-to-notion" class="level1">
<h1>Obsidian to Notion</h1>
<p><a href="https://github.com/Easychris/obsidian-to-notion/actions/workflows/CI.yml"><img src="https://github.com/Easychris/obsidian-to-notion/actions/workflows/CI.yml/badge.svg" class="img-fluid"></a> <a href="https://github.com/Easychris/obsidian-to-notion/actions/workflows/release.yml"><img src="https://github.com/Easychris/obsidian-to-notion/actions/workflows/release.yml/badge.svg" class="img-fluid" alt="Release Obsidian plugin"></a> <a href="https://raw.githubusercontent.com/EasyChris/obsidian-to-notion/master/LICENSE"><img src="https://img.shields.io/github/license/EasyChris/obsidian-to-notion.png" class="img-fluid" alt="GitHub license"></a> <a href="https://GitHub.com/Easychris/obsidian-to-notion/releases/"><img src="https://img.shields.io/github/downloads/Easychris/obsidian-to-notion/total.svg" class="img-fluid" alt="Github all releases"></a> <a href="https://github.com/Easychris/obsidian-to-notion/releases"><img src="https://badgen.net/github/release/Easychris/obsidian-to-notion/.png" class="img-fluid" alt="GitLab latest release"></a></p>
<p>Share of obsidian to Notion <a href="README-zh.md">中文文档</a></p>
<p>Sharing files from Obsidian to Notion with a single click, and Obsidian will automatically add the Notion share link</p>
<p>You are welcome to offer it a star if it can benefit you.</p>
<p><img src="./doc/1.gif" class="img-fluid"></p>
</section>
<section id="todo" class="level1">
<h1>TODO</h1>
<section id="todo-board" class="level3">
<h3 class="anchored" data-anchor-id="todo-board"><a href="https://github.com/users/EasyChris/projects/3/views/1">TODO Board</a></h3>
<ul class="task-list">
<li><input type="checkbox" checked="">support for custom page banner</li>
<li><input type="checkbox" checked="">update the exsit page</li>
<li><input type="checkbox" checked="">support for mult language</li>
<li><input type="checkbox" checked="">support for auto copy the share link to clipboard</li>
<li><input type="checkbox" checked="">support for mobile</li>
<li><input type="checkbox" checked="">support tags thank for <a href="https://github.com/jannikbuscha"><span class="citation" data-cites="jannikbuscha">@jannikbuscha</span></a></li>
<li><input type="checkbox">transfer the bi-link format like [[]] into the format that Notion supports.</li>
</ul>
</section>
</section>
<section id="how-to-use-2" class="level1">
<h1>How to use</h1>
<section id="install-the-plugin" class="level2">
<h2 class="anchored" data-anchor-id="install-the-plugin">Install the plugin</h2>
<section id="marketplace-download" class="level3">
<h3 class="anchored" data-anchor-id="marketplace-download">Marketplace download</h3>
<p>Open obsidian setting -&gt; Add plugin -&gt; Search -&gt; notion</p>
<p><img src="https://afox-1256168983.cos.ap-shanghai.myqcloud.com/20220628214145.png" class="img-fluid"></p>
</section>
<section id="brat" class="level3">
<h3 class="anchored" data-anchor-id="brat">BRAT</h3>
<p>Enter <code>BRAT</code> into the plugin market center to find it. Add <code>EasyChris/obsidian-to-notion</code> to the list of BRAT plugins that have been installed. Return to the plugin center and turn it on.</p>
</section>
<section id="manual-installation" class="level3">
<h3 class="anchored" data-anchor-id="manual-installation">Manual installation</h3>
<pre><code>cd YOUR_OBSIDIAN_FOLDER/.obsidian/plugins/
git clone https://github.com/EasyChris/obsidian-to-notion.git</code></pre>
</section>
</section>
<section id="apply-notion-api" class="level2">
<h2 class="anchored" data-anchor-id="apply-notion-api">Apply Notion API</h2>
<p>Official reference documentation: <a href="https://developers.notion.com/docs">https://developers.notion.com/docs</a></p>
<section id="step-1-create-integration" class="level3">
<h3 class="anchored" data-anchor-id="step-1-create-integration">Step 1: Create integration</h3>
<p>Go to <a href="https://www.notion.com/my-integrations">https://www.notion.com/my-integrations</a> Once created, copy <code>secrets toekn</code> <img src="https://files.readme.io/2ec137d-093ad49-create-integration.gif" class="img-fluid"></p>
<section id="note" class="level4">
<h4 class="anchored" data-anchor-id="note">Note</h4>
<p>database first custom name must be “Name”, otherwise sync to notion will be failed</p>
<p><img src="https://afox-1256168983.cos.ap-shanghai.myqcloud.com/20220618102029.png" class="img-fluid"></p>
</section>
</section>
<section id="step-2-share-a-database-with-your-integration" class="level3">
<h3 class="anchored" data-anchor-id="step-2-share-a-database-with-your-integration">Step 2: Share a database with your integration</h3>
<p>Create a new page (with public permissions) Create a new database in the page -&gt; you need <code>full page database</code> <img src="./doc/3.gif" class="img-fluid"></p>
<p>Add <code>integration</code> to your new database</p>
<p><img src="./doc/6.gif" class="img-fluid"></p>
</section>
<section id="step-3-copy-the-database-id" class="level3">
<h3 class="anchored" data-anchor-id="step-3-copy-the-database-id">Step 3: Copy the database ID</h3>
<pre><code>https://www.notion.so/myworkspace/a8aec43384f447ed84390e8e42c2e089?v=...
| --------- Database ID --------|
</code></pre>
</section>
</section>
<section id="open-the-plugin-configuration" class="level2">
<h2 class="anchored" data-anchor-id="open-the-plugin-configuration">Open the plugin configuration</h2>
<p>Fill the configuration with the <code>NOTION_API_KEY</code> and <code>DATABASE_ID</code> you got <img src="./doc/2.png" class="img-fluid"></p>
</section>
<section id="upload-file-content-to-notion" class="level2">
<h2 class="anchored" data-anchor-id="upload-file-content-to-notion">Upload file content to notion</h2>
<p>Click the uploadCommand notion button <img src="./doc/4.png" class="img-fluid"> A share link will be automatically generated after successful uploadCommand <img src="./doc/5.png" class="img-fluid"></p>
</section>
<section id="banner-url-option" class="level2">
<h2 class="anchored" data-anchor-id="banner-url-option">Banner URL [option]</h2>
<p>Banner url must be a image url like: <a href="https://i.imgur.com/xxx.jpg" class="uri">https://i.imgur.com/xxx.jpg</a> If you dont want to use banner, leave it blank</p>
</section>
<section id="convert-tags-option" class="level2">
<h2 class="anchored" data-anchor-id="convert-tags-option">Convert Tags [option]</h2>
<p>Transfer the Obsidian tags to the Notion table. It requires the column with the name Tags. <img src="./doc/7.png" class="img-fluid"></p>
<p>Add tags to your notion page</p>
<p><img src="./doc/10.png" class="img-fluid"></p>
<ul>
<li>open plugin convert tags</li>
</ul>
<p><img src="./doc/8.png" class="img-fluid"></p>
<ul>
<li>add tags in the head</li>
</ul>
<div class="sourceCode" id="cb4"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">---</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="an">tags:</span><span class="co"> [tag1,tag2]</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="co">---</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>this is test tags</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<div class="sourceCode" id="cb5"><pre class="sourceCode markdown code-with-copy"><code class="sourceCode markdown"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">---</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="an">tags:</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="co"> - tag4</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="co">---</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>this is test tags</span></code><button title="Copy to Clipboard" class="code-copy-button"><i class="bi"></i></button></pre></div>
<p><img src="./doc/9.png" class="img-fluid"></p>
<p>Thanks for <a href="https://github.com/jannikbuscha"><span class="citation" data-cites="jannikbuscha">@jannikbuscha</span></a> contribution</p>
</section>
<section id="notion-id-option" class="level2">
<h2 class="anchored" data-anchor-id="notion-id-option">Notion ID [option]</h2>
<p>Notion ID is the your notion site ID that you want to share the file to. if you dont write it, notion will share to the default link like: <a href="https://www.notion.so/myworkspace/a8aec43384f447ed84390" class="uri">https://www.notion.so/myworkspace/a8aec43384f447ed84390</a> 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: <a href="https://your_user_name.notion.site/myworkspace/a8aec43384f447ed84390" class="uri">https://your_user_name.notion.site/myworkspace/a8aec43384f447ed84390</a>. The visiter dont need to redirect url.</p>
</section>
<section id="sync-image-to-notion" class="level2">
<h2 class="anchored" data-anchor-id="sync-image-to-notion">Sync image to Notion</h2>
<p>To sync images to your oss or cos bucket, use the <a href="https://github.com/renmu123/obsidian-image-auto-upload-plugin">Obsidian Image Auto Upload Plugin</a>.</p>
</section>
</section>
<section id="development" class="level1">
<h1>Development</h1>
<pre><code>git clone https://github.com/EasyChris/obsidian-to-notion.git
yarn install
yarn dev</code></pre>
<section id="release" class="level2">
<h2 class="anchored" data-anchor-id="release">Release</h2>
<pre><code>node update-version.js
./release.sh</code></pre>
<p>```</p>
</section>
</section>
<section id="thanks" class="level1">
<h1>Thanks</h1>
<p><a href="https://luhaifeng666.github.io/obsidian-plugin-docs-zh/zh/getting-started/development-workflow.html">Development Process | Obsidian Plugin Development Documentation</a></p>
<p><a href="https://github.com/devbean/obsidian-wordpress">GitHub - devbean/obsidian-wordpress: An obsidian plugin for publishing docs to WordPress.</a></p>
<p><a href="https://github.com/obsidianmd/obsidian-api">GitHub - obsidianmd/obsidian-api</a></p>
<p><a href="https://github.dev/zhaohongxuan/obsidian-weread-plugin">GitHub - Easychris/obsidian-to-notion: Obsidian Weread Plugin is an plugin to sync Weread(微信读书) hightlights and annotations into your Obsidian Vault.</a></p>
<p><a href="https://github.com/Quorafind/Obsidian-Memos">GitHub - Quorafind/Obsidian-Memos: A quick capture plugin for Obsidian, all data from your notes.</a></p>
<p><a href="https://github.com/jannikbuscha">https://github.com/jannikbuscha/obsidian-to-notion</a></p>
</section>
<section id="license" class="level1">
<h1>License</h1>
<p>GNU GPLv3</p>
</section>
</main>
<!-- /main column -->
<script id="quarto-html-after-body" type="application/javascript">
window.document.addEventListener("DOMContentLoaded", function (event) {
const toggleBodyColorMode = (bsSheetEl) => {
const mode = bsSheetEl.getAttribute("data-mode");
const bodyEl = window.document.querySelector("body");
if (mode === "dark") {
bodyEl.classList.add("quarto-dark");
bodyEl.classList.remove("quarto-light");
} else {
bodyEl.classList.add("quarto-light");
bodyEl.classList.remove("quarto-dark");
}
}
const toggleBodyColorPrimary = () => {
const bsSheetEl = window.document.querySelector("link#quarto-bootstrap");
if (bsSheetEl) {
toggleBodyColorMode(bsSheetEl);
}
}
toggleBodyColorPrimary();
const icon = "";
const anchorJS = new window.AnchorJS();
anchorJS.options = {
placement: 'right',
icon: icon
};
anchorJS.add('.anchored');
const isCodeAnnotation = (el) => {
for (const clz of el.classList) {
if (clz.startsWith('code-annotation-')) {
return true;
}
}
return false;
}
const clipboard = new window.ClipboardJS('.code-copy-button', {
text: function(trigger) {
const codeEl = trigger.previousElementSibling.cloneNode(true);
for (const childEl of codeEl.children) {
if (isCodeAnnotation(childEl)) {
childEl.remove();
}
}
return codeEl.innerText;
}
});
clipboard.on('success', function(e) {
// button target
const button = e.trigger;
// don't keep focus
button.blur();
// flash "checked"
button.classList.add('code-copy-button-checked');
var currentTitle = button.getAttribute("title");
button.setAttribute("title", "Copied!");
let tooltip;
if (window.bootstrap) {
button.setAttribute("data-bs-toggle", "tooltip");
button.setAttribute("data-bs-placement", "left");
button.setAttribute("data-bs-title", "Copied!");
tooltip = new bootstrap.Tooltip(button,
{ trigger: "manual",
customClass: "code-copy-button-tooltip",
offset: [0, -8]});
tooltip.show();
}
setTimeout(function() {
if (tooltip) {
tooltip.hide();
button.removeAttribute("data-bs-title");
button.removeAttribute("data-bs-toggle");
button.removeAttribute("data-bs-placement");
}
button.setAttribute("title", currentTitle);
button.classList.remove('code-copy-button-checked');
}, 1000);
// clear code selection
e.clearSelection();
});
function tippyHover(el, contentFn) {
const config = {
allowHTML: true,
content: contentFn,
maxWidth: 500,
delay: 100,
arrow: false,
appendTo: function(el) {
return el.parentElement;
},
interactive: true,
interactiveBorder: 10,
theme: 'quarto',
placement: 'bottom-start'
};
window.tippy(el, config);
}
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
for (var i=0; i<noterefs.length; i++) {
const ref = noterefs[i];
tippyHover(ref, function() {
// use id or data attribute instead here
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
try { href = new URL(href).hash; } catch {}
const id = href.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
return note.innerHTML;
});
}
let selectedAnnoteEl;
const selectorForAnnotation = ( cell, annotation) => {
let cellAttr = 'data-code-cell="' + cell + '"';
let lineAttr = 'data-code-annotation="' + annotation + '"';
const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
return selector;
}
const selectCodeLines = (annoteEl) => {
const doc = window.document;
const targetCell = annoteEl.getAttribute("data-target-cell");
const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
const lines = annoteSpan.getAttribute("data-code-lines").split(",");
const lineIds = lines.map((line) => {
return targetCell + "-" + line;
})
let top = null;
let height = null;
let parent = null;
if (lineIds.length > 0) {
//compute the position of the single el (top and bottom and make a div)
const el = window.document.getElementById(lineIds[0]);
top = el.offsetTop;
height = el.offsetHeight;
parent = el.parentElement.parentElement;
if (lineIds.length > 1) {
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
height = bottom - top;
}
if (top !== null && height !== null && parent !== null) {
// cook up a div (if necessary) and position it
let div = window.document.getElementById("code-annotation-line-highlight");
if (div === null) {
div = window.document.createElement("div");
div.setAttribute("id", "code-annotation-line-highlight");
div.style.position = 'absolute';
parent.appendChild(div);
}
div.style.top = top - 2 + "px";
div.style.height = height + 4 + "px";
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
if (gutterDiv === null) {
gutterDiv = window.document.createElement("div");
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
gutterDiv.style.position = 'absolute';
const codeCell = window.document.getElementById(targetCell);
const gutter = codeCell.querySelector('.code-annotation-gutter');
gutter.appendChild(gutterDiv);
}
gutterDiv.style.top = top - 2 + "px";
gutterDiv.style.height = height + 4 + "px";
}
selectedAnnoteEl = annoteEl;
}
};
const unselectCodeLines = () => {
const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
elementsIds.forEach((elId) => {
const div = window.document.getElementById(elId);
if (div) {
div.remove();
}
});
selectedAnnoteEl = undefined;
};
// Attach click handler to the DT
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
for (const annoteDlNode of annoteDls) {
annoteDlNode.addEventListener('click', (event) => {
const clickedEl = event.target;
if (clickedEl !== selectedAnnoteEl) {
unselectCodeLines();
const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
if (activeEl) {
activeEl.classList.remove('code-annotation-active');
}
selectCodeLines(clickedEl);
clickedEl.classList.add('code-annotation-active');
} else {
// Unselect the line
unselectCodeLines();
clickedEl.classList.remove('code-annotation-active');
}
});
}
const findCites = (el) => {
const parentEl = el.parentElement;
if (parentEl) {
const cites = parentEl.dataset.cites;
if (cites) {
return {
el,
cites: cites.split(' ')
};
} else {
return findCites(el.parentElement)
}
} else {
return undefined;
}
};
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
for (var i=0; i<bibliorefs.length; i++) {
const ref = bibliorefs[i];
const citeInfo = findCites(ref);
if (citeInfo) {
tippyHover(citeInfo.el, function() {
var popup = window.document.createElement('div');
citeInfo.cites.forEach(function(cite) {
var citeDiv = window.document.createElement('div');
citeDiv.classList.add('hanging-indent');
citeDiv.classList.add('csl-entry');
var biblioDiv = window.document.getElementById('ref-' + cite);
if (biblioDiv) {
citeDiv.innerHTML = biblioDiv.innerHTML;
}
popup.appendChild(citeDiv);
});
return popup.innerHTML;
});
}
}
});
</script>
</div> <!-- /content -->
</body></html>

View File

@@ -13,7 +13,55 @@ Thus, based on the [original author's work](https://github.com/EasyChris/obsidia
**Now, support both NotionNext and General databases.**
**现在支持NotionNext和普通Notion数据库。**
## TODO List
- [ ] Support custom properties for Notion General database. 支持自定义属性
- [ ] Support preview for database details in plugin settings. 支持预览数据库详情
- [ ] Support edit for database details in plugin settings. 支持编辑数据库详情
## Update
### 2.0.0 (Big Update)
- redesign the plugin settings UI. From this version, the settings UI will be separated into two parts:
- one for general settings: bannerUrl and your notion username (ID)
- one for database list: You can add new database or delete the database.
- 重新设计了插件设置界面。从这个版本开始,设置界面将被分成两部分:
- 一部分是通用设置bannerUrl和你的notion用户名ID
- 一部分是数据库列表:你可以添加新的数据库或者删除数据库。
![](https://minioapi.pjx.ac.cn/img1/2023/12/f7e89241f45cfee6b902ec4b69dd6f63.png)
- You can add more databases in the plugin settings.
- 你可以在插件设置中添加更多的数据库。
![](https://minioapi.pjx.ac.cn/img1/2023/12/023bf46ebbc92c3991d2c443c575bc80.gif)
- You can sync one note to multiple databases.
- 你可以将一个笔记同步到多个数据库中。
![](https://minioapi.pjx.ac.cn/img1/2023/12/75f793bad756162e46bf41e54166eb32.png)
**Note: You need to add your previous database in the new template.**
**注意:你需要将之前的数据库添加到新的模板中。**
### 1.1.2
- Fix the typo that you cannot sync the markdown file `status` in the frontmatter to NotionNext. You can use `stats` or `status` to sync the status of the post to NotionNext. This update will not affect the function of syncing to General database.
- 修复了一个拼写错误,导致无法同步`status`到NotionNext。现在你可以使用`stats`或者`status`来同步文章的状态到NotionNext。这个更新不会影响到同步到General数据库的功能。
- **Both `stats` and `status` will work, but you can only use one of them.**
- **`stats``status`都可以使用,但是你只能使用其中一个。**
For example,
```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
- Fix the setting display bug in Japanese.
- Add Japanese translation.
### 1.1.0
- Fix the custom name setting tab display bug.

View File

@@ -1,7 +1,7 @@
{
"id": "share-to-notionnext",
"name": "Share to NotionNext",
"version": "1.1.0",
"version": "2.0.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

@@ -1,6 +1,6 @@
{
"name": "share-to-notionnext",
"version": "1.1.0",
"version": "2.0.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,9 +15,9 @@
"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",
"@typescript-eslint/eslint-plugin": "^6.16.0",
"@typescript-eslint/parser": "^6.16.0",
"builtin-modules": "^3.3.0",
"esbuild": "0.19.5",
"obsidian": "latest",
"tslib": "2.6.2",

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 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 uploadCommandGeneral(this.plugin, dbDetails, this.plugin.app);
// };
// }
this.Ncommand.push({ id: commandId, name: commandName, editorCallback });
}
}

View File

@@ -1,15 +1,17 @@
import {en} from "./en";
import {zh} from "./zh";
import {en} from "./locale/en";
import {zh} from "./locale/zh";
import {ja} from "./locale/ja";
export const I18n: {[key: string]:any} = {
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"];

View File

@@ -1,4 +1,15 @@
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",
@@ -31,6 +42,9 @@ export const en = {
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",

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

@@ -0,0 +1,60 @@
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",
DatabaseIDText: "データベースIDを入力",
BannerUrl: "バナーのURL任意",
BannerUrlDesc: "デフォルトは空白です。バナーを表示したい場合は、URLを入力してくださいhttps://minioapi.pjx.ac.cn/img1/2023/11/b7b40a0724e93b7d7ab494bb3b8a2da8.png",
BannerUrlText: "バナーのURLを入力",
NotionUser: "Notion IDユーザー名、任意",
NotionUserDesc: "共有リンクから取得https://username.notion.site。Notion IDは[username]です",
NotionUserText: "Notion IDを入力",
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カスタムデータベース同期コマンドがコマンドパレットに表示されます",
};

View File

@@ -1,4 +1,15 @@
export const zh = {
databaseFormat: "数据库格式",
databaseFormatDesc: "选择你想要同步的数据库格式Next 或者 普通",
databaseNext: "NotionNext",
databaseGeneral: "普通",
databaseCustom: "自定义",
databaseFullName: "数据库全称",
databaseFullNameDesc: "给你的数据库起一个全称",
databaseFullNameText: "输入你的数据库全称",
databaseAbbreviateName: "数据库简称",
databaseAbbreviateNameDesc: "给你的数据库起一个简称",
databaseAbbreviateNameText: "输入你的数据库简称",
ribbonIcon: "分享到 NotionNext",
GeneralSetting: "通用设置",
CommandID: "share-to-notionnext",
@@ -31,6 +42,9 @@ export const zh = {
NotionCustomTitleName: "想要修改的表头名",
NotionCustomTitleNameDesc: "输入你想要修改的notion数据库的表头名默认title",
NotionCustomTitleText: "输入表头名",
NotionCustomValues: "自定义Notion 数据库表头",
NotionCustomValuesDesc: "自定义Notion 数据库表头,每行一个",
NotionCustomValuesText: "输入你想要同步的所有属性",
NotYetFinish: "未完成。此功能将在之后版本中提供",
PlaceHolder: "输入数据库名称",
"notion-logo": "分享到NotionNext",

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,36 @@ 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();
}
// previewDatabase(dbDetails: DatabaseDetails) {
//
// }
}

View File

@@ -0,0 +1,116 @@
import {App, PluginSettingTab, Setting} from "obsidian";
import ObsidianSyncNotionPlugin from "../main";
import {i18nConfig} from "../lang/I18n";
import {ObsidianSettingTab} from "./settingTabs";
export class SettingGeneralTabs extends PluginSettingTab {
plugin: ObsidianSyncNotionPlugin;
private settingTab: ObsidianSettingTab;
constructor(app: App, plugin: ObsidianSyncNotionPlugin, settingTab: ObsidianSettingTab) {
super(app, plugin);
this.plugin = plugin;
this.settingTab = settingTab;
}
display(): void {
// General Database Settings
this.settingTab.containerEl.createEl('h2', { text: i18nConfig.NotionGeneralSettingHeader });
// new Setting(containerEl)
// .setName(i18nConfig.NotYetFinish)
new Setting(this.settingTab.containerEl)
.setName(i18nConfig.NotionGeneralButton)
.setDesc(i18nConfig.NotionGeneralButtonDesc)
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.GeneralButton)
.onChange(async (value) => {
this.plugin.settings.GeneralButton = value;
this.settingTab.updateSettingEl(tagButtonEl, value)
this.settingTab.updateSettingEl(CustomTitleEl, value)
// name should follow the result of the title button
if (value) {
this.settingTab.updateSettingEl(CustomNameEl, this.plugin.settings.CustomTitleButton)
this.settingTab.updateSettingEl(CustomValuesEl, this.plugin.settings.CustomTitleButton)
} else {
this.settingTab.updateSettingEl(CustomNameEl, value)
this.settingTab.updateSettingEl(CustomValuesEl, value)
}
this.settingTab.updateSettingEl(notionAPIGeneralEl, value)
this.settingTab.updateSettingEl(databaseIDGeneralEl, value)
await this.plugin.saveSettings();
await this.plugin.commands.updateCommand();
})
);
// add the tagButton to control whether to add tags to the general database
const tagButtonEl = this.settingTab.createStyleDiv('tag-button', (this.plugin.settings.GeneralButton && this.plugin.settings.CustomTitleButton));
this.settingTab.createSettingEl(tagButtonEl, i18nConfig.NotionTagButton, i18nConfig.NotionTagButtonDesc, 'toggle', i18nConfig.NotionCustomTitleText, this.plugin.settings.tagButton, 'tagButton')
// Custom Title Button
const CustomTitleEl = this.settingTab.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;
this.settingTab.updateSettingEl(CustomNameEl, value)
this.settingTab.updateSettingEl(CustomValuesEl, value)
await this.plugin.saveSettings();
await this.plugin.commands.updateCommand();
})
);
// Custom Title Name
const CustomNameEl = this.settingTab.createStyleDiv('custom-name', (this.plugin.settings.CustomTitleButton && this.plugin.settings.GeneralButton));
this.settingTab.createSettingEl(CustomNameEl, i18nConfig.NotionCustomTitleName, i18nConfig.NotionCustomTitleNameDesc, 'text', i18nConfig.NotionCustomTitleText, this.plugin.settings.CustomTitleName, 'CustomTitleName')
// Custom database properties
const CustomValuesEl = this.settingTab.createStyleDiv('custom-values', (this.plugin.settings.CustomTitleButton && this.plugin.settings.GeneralButton));
new Setting(CustomValuesEl)
.setName(i18nConfig.NotionCustomValues)
.setDesc(i18nConfig.NotionCustomValuesDesc)
.addTextArea((text) =>
text
.setPlaceholder(i18nConfig.NotionCustomValuesText)
.setValue(this.plugin.settings.CustomValues)
.onChange(async (value) => {
this.plugin.settings.CustomValues = value;
await this.plugin.saveSettings();
await this.plugin.commands.updateCommand();
})
);
// 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.settingTab.createStyleDiv('api-general', this.plugin.settings.GeneralButton);
this.settingTab.createSettingEl(notionAPIGeneralEl, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.plugin.settings.notionAPIGeneral, 'notionAPIGeneral')
const databaseIDGeneralEl = this.settingTab.createStyleDiv('databaseID-general', this.plugin.settings.GeneralButton);
this.settingTab.createSettingEl(databaseIDGeneralEl, i18nConfig.DatabaseID, i18nConfig.NotionAPIDesc, 'password', i18nConfig.DatabaseIDText, this.plugin.settings.databaseIDGeneral, 'databaseIDGeneral')
}
}

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

@@ -0,0 +1,301 @@
import {
Modal,
Setting,
PluginSettingTab,
ButtonComponent, App
} from 'obsidian';
import { i18nConfig } from "../lang/I18n";
import ObsidianSyncNotionPlugin from "../main";
import {DatabaseDetails, ObsidianSettingTab} from "./settingTabs";
import {SettingNextTabs} from "./settingNextTabs";
import {SettingGeneralTabs} from "./settingGeneralTabs";
export class SettingModal extends Modal {
data: Record<string, any> = {
databaseFormat: 'none',
databaseFullName: '',
databaseAbbreviateName: '',
notionAPI: '',
databaseID: '',
tagButton: true,
customTitleButton: false,
customTitleName: '',
// customValues: '',
saved: false,
};
plugin: ObsidianSyncNotionPlugin;
settingTab: ObsidianSettingTab;
constructor(app: App, plugin: ObsidianSyncNotionPlugin, settingTab: ObsidianSettingTab, dbDetails?: DatabaseDetails) {
super(app);
this.plugin = plugin;
this.settingTab = settingTab;
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.customValues = dbDetails.customValues;
}
}
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.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.close();
});
}
);
saveButton.addExtraButton((button) => {
return button
.setTooltip('Cancel')
.setIcon('cross')
.onClick(() => {
this.data.saved = false;
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, 'databaseFullName')
// add abbreviate name
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.data.databaseAbbreviateName, 'databaseAbbreviateName')
// tag button
this.createSettingEl(nextTabs, i18nConfig.NotionTagButton, i18nConfig.NotionTagButtonDesc, 'toggle', i18nConfig.NotionCustomTitleText, this.data.tagButton, '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)
// this.updateSettingEl(CustomValuesEl, value)
await this.plugin.saveSettings();
await this.plugin.commands.updateCommand();
})
);
// 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, 'CustomTitleName')
// add api key
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.data.notionAPI, 'notionAPI')
// add database id
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.NotionAPIDesc, 'password', i18nConfig.DatabaseIDText, this.data.databaseID, '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, 'databaseFullName')
// add abbreviate name
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.data.databaseAbbreviateName, 'databaseAbbreviateName')
// add api key
this.createSettingEl(nextTabs, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.data.notionAPI, 'notionAPI')
// add database id
this.createSettingEl(nextTabs, i18nConfig.DatabaseID, i18nConfig.NotionAPIDesc, 'password', i18nConfig.DatabaseIDText, this.data.databaseID, '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, 'databaseFullName')
// add abbreviate name
this.createSettingEl(nextTabs, i18nConfig.databaseAbbreviateName, i18nConfig.databaseAbbreviateNameDesc, 'text', i18nConfig.databaseAbbreviateNameText, this.data.databaseAbbreviateName, 'databaseAbbreviateName')
// tag button
this.createSettingEl(nextTabs, i18nConfig.NotionTagButton, i18nConfig.NotionTagButtonDesc, 'toggle', i18nConfig.NotionCustomTitleText, this.data.tagButton, '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)
// this.updateSettingEl(CustomValuesEl, value)
await this.plugin.saveSettings();
await this.plugin.commands.updateCommand();
})
);
// 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, 'CustomTitleName')
// // add custom values
// const CustomValuesEl = this.createStyleDiv('custom-values', (this.data.CustomTitleButton), nextTabs);
// new Setting(CustomValuesEl)
// .setName(i18nConfig.NotionCustomValues)
// .setDesc(i18nConfig.NotionCustomValuesDesc)
// .addTextArea((text) => {
// return text
// .setPlaceholder(i18nConfig.NotionCustomValuesText)
// .setValue(this.data.CustomValues)
// .onChange(async (value) => {
// this.data.CustomValues = value;
// await this.plugin.saveSettings();
// });
// });
// add api key
const notionAPIGeneralEl = this.createStyleDiv('api-general', this.plugin.settings.GeneralButton, nextTabs);
this.createSettingEl(notionAPIGeneralEl, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.data.notionAPI, 'notionAPI')
// add database id
const databaseIDGeneralEl = this.createStyleDiv('databaseID-general', this.plugin.settings.GeneralButton, nextTabs);
this.createSettingEl(databaseIDGeneralEl, i18nConfig.DatabaseID, i18nConfig.NotionAPIDesc, 'password', i18nConfig.DatabaseIDText, this.data.databaseID, 'databaseID')
}
}
onOpen() {
// add console log to check if the modal is opened
this.display()
}
// 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,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.data[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.data[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.data[settingsKey] = value; // Update the settings dictionary await this.plugin.saveSettings();
})
);
}
}
}

49
src/ui/settingNextTabs.ts Normal file
View File

@@ -0,0 +1,49 @@
import {App, PluginSettingTab, Setting} from "obsidian";
import ObsidianSyncNotionPlugin from "../main";
import {i18nConfig} from "../lang/I18n";
import {ObsidianSettingTab} from "./settingTabs";
import {SettingModal} from "./settingModal";
export class SettingNextTabs extends PluginSettingTab {
plugin: ObsidianSyncNotionPlugin;
settingModal: SettingModal;
constructor(app: App, plugin: ObsidianSyncNotionPlugin, settingTab: ObsidianSettingTab) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
// notion next database settings
this.containerEl.createEl('h2', { text: i18nConfig.NotionNextSettingHeader });
const NextButtonEl = this.containerEl.createDiv();
new Setting(NextButtonEl)
.setName(i18nConfig.NotionNextButton)
.setDesc(i18nConfig.NotionNextButtonDesc)
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.NextButton)
.onChange(async (value) => {
this.plugin.settings.NextButton = value;
this.settingModal.updateSettingEl(notionAPINextEl, value)
this.settingModal.updateSettingEl(databaseIDNextEl, value)
await this.plugin.saveSettings();
await this.plugin.commands.updateCommand();
})
);
const notionAPINextEl = this.settingModal.createStyleDiv('api-next', this.plugin.settings.NextButton,NextButtonEl)
this.settingModal.createSettingEl(notionAPINextEl, i18nConfig.NotionAPI, i18nConfig.NotionAPIDesc, 'password', i18nConfig.NotionAPIText, this.plugin.settings.notionAPINext,'notionAPINext')
const databaseIDNextEl = this.settingModal.createStyleDiv('databaseID-next', this.plugin.settings.NextButton,NextButtonEl)
this.settingModal.createSettingEl(databaseIDNextEl, i18nConfig.DatabaseID, i18nConfig.NotionAPIDesc, 'password', i18nConfig.DatabaseIDText, this.plugin.settings.databaseIDNext,'databaseIDNext')
}
}

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 {SettingNextTabs} from "./settingNextTabs";
import {SettingGeneralTabs} from "./settingGeneralTabs";
import {set} from "yaml/dist/schema/yaml-1.1/set";
export interface PluginSettings {
NextButton: boolean;
@@ -16,9 +20,23 @@ export interface PluginSettings {
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;
// customValues: string;
}
export const DEFAULT_SETTINGS: PluginSettings = {
@@ -35,13 +53,16 @@ export const DEFAULT_SETTINGS: PluginSettings = {
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);
@@ -60,112 +81,69 @@ 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 })
// 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(tagButtonEl, 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,
// customValues: modal.data.customValues,
}
this.updateSettingEl(notionAPIGeneralEl, value)
this.updateSettingEl(databaseIDGeneralEl, value)
this.plugin.addDatabaseDetails(dbDetails);
this.plugin.commands.updateCommand();
this.display()
}
}
modal.open();
});
});
// new section to display all created database
containerEl.createEl('h2', {text: "Database List"});
this.databaseEl = containerEl.createDiv('database-list');
// list all created database
this.showDatabase();
await this.plugin.saveSettings();
await this.plugin.commands.updateCommand();
})
);
// add the tagButton to control whether to add tags to the general database
const tagButtonEl = this.createStyleDiv('tag-button', (this.plugin.settings.GeneralButton && this.plugin.settings.CustomTitleButton));
this.createSettingEl(tagButtonEl, i18nConfig.NotionTagButton, i18nConfig.NotionTagButtonDesc, 'toggle', i18nConfig.NotionCustomTitleText, this.plugin.settings.tagButton, 'tagButton')
// Custom Title Button
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;
this.updateSettingEl(CustomNameEl, value)
await this.plugin.saveSettings();
await this.plugin.commands.updateCommand();
})
);
// Custom Title Name
const CustomNameEl = this.createStyleDiv('custom-name', (this.plugin.settings.CustomTitleButton && this.plugin.settings.GeneralButton));
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')
// // notion next database settings
//
// const NextTabs = new SettingNextTabs(this.app, this.plugin, this);
//
// NextTabs.display();
//
//
// // General Database Settings
// const GeneralTabs = new SettingGeneralTabs(this.app, this.plugin, this);
//
// GeneralTabs.display();
// Custom Database Settings
@@ -185,13 +163,13 @@ export class ObsidianSettingTab extends PluginSettingTab {
}
// create a function to create a div with a style for pop over elements
private createStyleDiv(className: string, commandValue: boolean = false) {
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";
@@ -199,7 +177,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)
@@ -244,4 +222,82 @@ 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 () => {
// this.plugin.previewDatabase(dbDetails);
// });
// });
// settingEl
// .addButton((button: ButtonComponent): ButtonComponent => {
// return button
// .setTooltip("Edit Database")
// .setIcon("pencil")
// .onClick(async () => {
// let modal = new SettingModal(this.app, this.plugin, this, dbDetails);
//
// 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,
// // customValues: modal.data.customValues,
// }
//
// this.plugin.updateDatabaseDetails(dbDetails);
//
// this.plugin.commands.updateCommand();
//
// this.display()
// }
// }
//
// modal.open();
// });
// });
settingEl
.addButton((button: ButtonComponent): ButtonComponent => {
return button
.setTooltip("Delete Database")
.setIcon("trash")
.onClick(async () => {
await this.plugin.deleteDatabaseDetails(dbDetails);
await this.plugin.commands.updateCommand();
this.display()
});
});
}
}
}
function addExtraButton(arg0: (button: ButtonComponent) => ButtonComponent): any {
throw new Error("Function not implemented.");
}

View File

@@ -1,6 +1,6 @@
import { App, Notice, TFile } from "obsidian";
import ObsidianSyncNotionPlugin from "../main";
import { PluginSettings } from "../ui/settingTabs";
import {DatabaseDetails, PluginSettings} from "../ui/settingTabs";
export async function updateYamlInfo(
yamlContent: string,
@@ -8,10 +8,14 @@ export async function updateYamlInfo(
res: any,
app: App,
plugin: ObsidianSyncNotionPlugin,
dbDetails: DatabaseDetails,
) {
let { url, id } = res.json
// replace www to notionID
const { notionUser } = plugin.settings;
const { abName } = dbDetails
const notionIDKey = `NotionID-${abName}`;
const linkKey = `link-${abName}`;
if (notionUser !== "") {
// replace url str "www" to notionID
@@ -19,15 +23,15 @@ 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;
yamlContent[linkKey] = url;
});
try {

View File

@@ -2,18 +2,21 @@ 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;
const { notionAPI, databaseID } = dbDetails;
// Check if NNon exists
// if (NNon === undefined) {
@@ -23,7 +26,7 @@ export async function uploadCommandNext(
// }
// 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;
@@ -33,7 +36,7 @@ export async function uploadCommandNext(
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) {
@@ -50,13 +53,14 @@ 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,7 +71,7 @@ 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) {
@@ -78,3 +82,37 @@ export async function uploadCommandGeneral(
}
}
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, tags ,customValues} = await getNowFileMarkdownContentCustom(app, settings)
if (markDownData) {
const { basename } = nowFile;
const upload = new Upload2NotionCustom(plugin,dbDetails);
const res = await upload.syncMarkdownToNotionCustom(basename, cover, tags, customValues, markDownData, nowFile, this.app);
if (res.status === 200) {
new Notice(`${i18nConfig["sync-success"]}${basename}`);
} else {
new Notice(`${i18nConfig["sync-fail"]}${basename}`, 5000);
}
}
}

View File

@@ -4,23 +4,27 @@ import {markdownToBlocks,} from "@tryfabric/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;
constructor(plugin: MyPlugin) {
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 ' + this.plugin.settings.notionAPINext,
'Authorization': 'Bearer ' + notionAPI,
'Notion-Version': '2022-06-28',
},
body: ''
@@ -28,11 +32,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

@@ -4,15 +4,17 @@ import { markdownToBlocks } from "@tryfabric/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";
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进行对比非常的麻烦
@@ -26,12 +28,14 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
) {
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);
@@ -43,13 +47,22 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
tags: string[],
childArr: any,
) {
const {
databaseID,
customTitleButton,
customTitleName,
tagButton,
notionAPI
} = this.dbDetails;
const bodyString: any = {
parent: {
database_id: this.plugin.settings.databaseIDGeneral,
database_id: databaseID,
},
properties: {
[this.plugin.settings.CustomTitleButton
? this.plugin.settings.CustomTitleName
[customTitleButton
? customTitleName
: "title"]: {
title: [
{
@@ -59,7 +72,7 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
},
],
},
...(this.plugin.settings.tagsButton
...(tagButton
? {
tags: {
multi_select: tags && true ? tags.map((tag) => ({ name: tag })) : [],
@@ -96,7 +109,7 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
"Content-Type": "application/json",
// 'User-Agent': 'obsidian.md',
Authorization:
"Bearer " + this.plugin.settings.notionAPIGeneral,
"Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
@@ -125,7 +138,10 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
const file2Block = markdownToBlocks(__content, options);
const frontmasster =
app.metadataCache.getFileCache(nowFile)?.frontmatter;
const notionID = frontmasster ? frontmasster.notionID : null;
const {abName} = this.dbDetails
const notionIDKey = `${abName}-NotionID`;
const notionID = frontmasster ? frontmasster[notionIDKey] : null;
if (notionID) {
res = await this.updatePage(
@@ -139,7 +155,7 @@ export class Upload2NotionGeneral extends UploadBaseGeneral {
res = await this.createPage(title, cover, tags, file2Block);
}
if (res.status === 200) {
await updateYamlInfo(markdown, nowFile, res, app, this.plugin);
await updateYamlInfo(markdown, nowFile, res, app, this.plugin, this.dbDetails);
} else {
new Notice(`${res.text}`);
}

View File

@@ -4,23 +4,27 @@ import { markdownToBlocks, } from "@tryfabric/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

@@ -5,14 +5,17 @@ import { markdownToBlocks, } from "@tryfabric/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 "@tryfabric/martian/src/notion";
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进行对比非常的麻烦
@@ -35,10 +38,12 @@ export class Upload2NotionNext extends UploadBaseNext {
) {
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(
@@ -72,9 +77,16 @@ export class Upload2NotionNext extends UploadBaseNext {
datetime: string,
childArr: any
) {
const {
databaseID,
notionAPI
} = this.dbDetails
const bodyString: any = {
parent: {
database_id: this.plugin.settings.databaseIDNext
database_id: databaseID,
},
icon: {
emoji: emoji || '📜'
@@ -178,7 +190,7 @@ export class Upload2NotionNext extends UploadBaseNext {
headers: {
'Content-Type': 'application/json',
// 'User-Agent': 'obsidian.md',
'Authorization': 'Bearer ' + this.plugin.settings.notionAPINext,
'Authorization': 'Bearer ' + notionAPI,
'Notion-Version': '2022-06-28',
},
body: JSON.stringify(bodyString),
@@ -215,7 +227,31 @@ export class Upload2NotionNext extends UploadBaseNext {
const __content = yamlContent.__content
const file2Block = markdownToBlocks(__content, options);
const frontmasster = app.metadataCache.getFileCache(nowFile)?.frontmatter
const notionID = frontmasster ? frontmasster.notionID : null
const {abName} = this.dbDetails
const notionIDKey = `${abName}-NotionID`;
const notionID = frontmasster ? frontmasster[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(
@@ -251,8 +287,9 @@ export class Upload2NotionNext extends UploadBaseNext {
file2Block
);
}
if (res.status === 200) {
await updateYamlInfo(markdown, nowFile, res, app, this.plugin)
await updateYamlInfo(markdown, nowFile, res, app, this.plugin, this.dbDetails)
} else {
new Notice(`${res.text}`)
}

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,95 @@
import {App, Notice, requestUrl, TFile} from "obsidian";
import {Client} from '@notionhq/client';
import {markdownToBlocks,} from "@tryfabric/martian";
import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main";
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
}
}
// async updateYamlInfo(yamlContent: string, nowFile: TFile, res: any, app: App, settings: any) {
// let {url, id} = res.json
// // replace www to notionID
// const {notionUser} = this.plugin.settings;
//
// if (notionUser !== "") {
// // replace url str "www" to notionID
// url = url.replace("www.notion.so", `${notionUser}.notion.site`)
// }
//
// await app.fileManager.processFrontMatter(nowFile, yamlContent => {
// if (yamlContent['notionID']) {
// delete yamlContent['notionID']
// }
// if (yamlContent['link']) {
// delete yamlContent['link']
// }
// // add new notionID and link
// yamlContent.notionID = id;
// yamlContent.link = url;
// });
//
// try {
// await navigator.clipboard.writeText(url)
// } catch (error) {
// new Notice(`复制链接失败,请手动复制${error}`)
// }
// // const __content = yamlContent.__content;
// // delete yamlContent.__content
// // const yamlhead = yaml.stringify(yamlContent)
// // // if yamlhead hava last \n remove it
// // const yamlhead_remove_n = yamlhead.replace(/\n$/, '')
// // // if __content have start \n remove it
// // const __content_remove_n = __content.replace(/^\n/, '')
// // const content = '---\n' +yamlhead_remove_n +'\n---\n' + __content_remove_n;
// // try {
// // await nowFile.vault.modify(nowFile, content)
// // } catch (error) {
// // new Notice(`write file error ${error}`)
// // }
// }
}

View File

@@ -0,0 +1,170 @@
import { App, Notice, requestUrl, TFile } from "obsidian";
import { Client } from "@notionhq/client";
import { markdownToBlocks } from "@tryfabric/martian";
import * as yamlFrontMatter from "yaml-front-matter";
// import * as yaml from "yaml"
import MyPlugin from "src/main";
import {DatabaseDetails, PluginSettings} from "../../ui/settingTabs";
import { updateYamlInfo } from "../updateYaml";
import {UploadBaseCustom} from "./BaseUpload2NotionCustom";
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,
title: string,
cover: string,
tags: 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(title, cover, tags, customValues, childArr);
}
async createPage(
title: string,
cover: string,
tags: string[],
customValues: Record<string, string>,
childArr: any,
) {
const {
databaseID,
customTitleButton,
customTitleName,
tagButton,
notionAPI
} = this.dbDetails;
const bodyString: any = {
parent: {
database_id: databaseID,
},
properties: {
[customTitleButton
? customTitleName
: "title"]: {
title: [
{
text: {
content: title,
},
},
],
},
...(Object.keys(customValues).reduce((acc, key) => {
acc[key] = {
rich_text: [
{
text: {
content: customValues[key] || '',
},
},
],
};
return acc;
}, {} as Record<string, { rich_text: { text: { content: string } }[] } >)),
},
children: childArr,
};
if (cover) {
bodyString.cover = {
type: "external",
external: {
url: cover,
},
};
}
if (!bodyString.cover && this.plugin.settings.bannerUrl) {
bodyString.cover = {
type: "external",
external: {
url: this.plugin.settings.bannerUrl,
},
};
}
try {
return await requestUrl({
url: `https://api.notion.com/v1/pages`,
method: "POST",
headers: {
"Content-Type": "application/json",
// 'User-Agent': 'obsidian.md',
Authorization:
"Bearer " + notionAPI,
"Notion-Version": "2022-06-28",
},
body: JSON.stringify(bodyString),
});
} catch (error) {
new Notice(`network error ${error}`);
}
}
async syncMarkdownToNotionCustom(
title: string,
cover: string,
tags: string[],
customValues: Record<string, string>,
markdown: string,
nowFile: TFile,
app: App,
): Promise<any> {
const options = {
notionLimits: {
truncate: false,
}
}
let res: any;
const yamlContent: any = yamlFrontMatter.loadFront(markdown);
const __content = yamlContent.__content;
const file2Block = markdownToBlocks(__content, options);
const frontmasster =
app.metadataCache.getFileCache(nowFile)?.frontmatter;
const notionID = frontmasster ? frontmasster.notionID : null;
if (notionID) {
res = await this.updatePage(
notionID,
title,
cover,
tags,
customValues,
file2Block,
);
} else {
res = await this.createPage(title, cover, tags, customValues, file2Block);
}
if (res.status === 200) {
await updateYamlInfo(markdown, nowFile, res, app, this.plugin, this.dbDetails);
} else {
new Notice(`${res.text}`);
}
return res;
}
}

View File

@@ -0,0 +1,43 @@
import {App, Notice} from "obsidian";
import {i18nConfig} from "../../lang/I18n";
import {PluginSettings} from "../../ui/settingTabs";
export async function getNowFileMarkdownContentCustom(
app: App,
settings: PluginSettings,
) {
const nowFile = app.workspace.getActiveFile();
let cover = '';
let tags = [];
let customValues: Record<string, string> = {};
const FileCache = app.metadataCache.getFileCache(nowFile);
try {
cover = FileCache.frontmatter.coverurl;
tags = FileCache.frontmatter.tags;
// split the CustomValues into an array
const customValuesNames = settings.CustomValues.split('\n').map(value => value.trim());
// get the custom values from the frontmatter
customValuesNames.forEach(valueName => {
customValues[valueName] = FileCache.frontmatter[valueName];
});
} catch (error) {
new Notice(i18nConfig["set-tags-fail"]);
}
if (nowFile) {
const markDownData = await nowFile.vault.read(nowFile);
return {
markDownData,
nowFile,
cover,
tags,
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,

122
yarn.lock
View File

@@ -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.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.16.0.tgz#cc29fbd208ea976de3db7feb07755bba0ce8d8bc"
integrity sha512-O5f7Kv5o4dLWQtPX4ywPPa+v9G+1q1x8mz0Kr0pXUtKsevo+gIJHLkGc8RxaZWtP8RrhwhSNIWThnW42K9/0rQ==
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.16.0"
"@typescript-eslint/type-utils" "6.16.0"
"@typescript-eslint/utils" "6.16.0"
"@typescript-eslint/visitor-keys" "6.16.0"
debug "^4.3.4"
graphemer "^1.4.0"
ignore "^5.2.4"
@@ -286,72 +286,73 @@
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.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.16.0.tgz#36f39f63b126aa25af2ad2df13d9891e9fd5b40c"
integrity sha512-H2GM3eUo12HpKZU9njig3DF5zJ58ja6ahj1GoHEHOgQvYxzoFJJEvC1MQ7T2l9Ha+69ZSOn7RTxOdpC/y3ikMw==
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.16.0"
"@typescript-eslint/types" "6.16.0"
"@typescript-eslint/typescript-estree" "6.16.0"
"@typescript-eslint/visitor-keys" "6.16.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.16.0":
version "6.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.16.0.tgz#f3e9a00fbc1d0701356359cd56489c54d9e37168"
integrity sha512-0N7Y9DSPdaBQ3sqSCwlrm9zJwkpOuc6HYm7LpzLAPqBL7dmzAUimr4M29dMkOP/tEwvOCC/Cxo//yOfJD3HUiw==
dependencies:
"@typescript-eslint/types" "6.11.0"
"@typescript-eslint/visitor-keys" "6.11.0"
"@typescript-eslint/types" "6.16.0"
"@typescript-eslint/visitor-keys" "6.16.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.16.0":
version "6.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.16.0.tgz#5f21c3e49e540ad132dc87fc99af463c184d5ed1"
integrity sha512-ThmrEOcARmOnoyQfYkHw/DX2SEYBalVECmoldVuH6qagKROp/jMnfXpAU/pAIWub9c4YTxga+XwgAkoA0pxfmg==
dependencies:
"@typescript-eslint/typescript-estree" "6.11.0"
"@typescript-eslint/utils" "6.11.0"
"@typescript-eslint/typescript-estree" "6.16.0"
"@typescript-eslint/utils" "6.16.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.16.0":
version "6.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.16.0.tgz#a3abe0045737d44d8234708d5ed8fef5d59dc91e"
integrity sha512-hvDFpLEvTJoHutVl87+MG/c5C8I6LOgEx05zExTSJDEVU7hhR3jhV8M5zuggbdFCw98+HhZWPHZeKS97kS3JoQ==
"@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.16.0":
version "6.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.16.0.tgz#d6e0578e4f593045f0df06c4b3a22bd6f13f2d03"
integrity sha512-VTWZuixh/vr7nih6CfrdpmFNLEnoVBF1skfjdyGnNwXOH1SLeHItGdZDHhhAIzd3ACazyY2Fg76zuzOVTaknGA==
dependencies:
"@typescript-eslint/types" "6.11.0"
"@typescript-eslint/visitor-keys" "6.11.0"
"@typescript-eslint/types" "6.16.0"
"@typescript-eslint/visitor-keys" "6.16.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.16.0":
version "6.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.16.0.tgz#1c291492d34670f9210d2b7fcf6b402bea3134ae"
integrity sha512-T83QPKrBm6n//q9mv7oiSvy/Xq/7Hyw9SzSEhMHJwznEmQayfBM87+oAlkNAMEO7/MjIwKyOHgBJbxB0s7gx2A==
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.16.0"
"@typescript-eslint/types" "6.16.0"
"@typescript-eslint/typescript-estree" "6.16.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.16.0":
version "6.16.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.16.0.tgz#d50da18a05d91318ed3e7e8889bda0edc35f3a10"
integrity sha512-QSFQLruk7fhs91a/Ep/LqRdbJCZ1Rq03rqBdKT5Ky17Sz8zRLUksqIe9DW0pKtg/Z35/ztbLQ6qpOCN6rOC11A==
dependencies:
"@typescript-eslint/types" "6.11.0"
"@typescript-eslint/types" "6.16.0"
eslint-visitor-keys "^3.4.1"
agent-base@^7.0.2:
@@ -388,6 +389,18 @@ bail@^2.0.0:
resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d"
integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==
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==
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.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
@@ -395,7 +408,7 @@ braces@^3.0.2:
dependencies:
fill-range "^7.0.1"
builtin-modules@^3.2.0:
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==
@@ -1146,6 +1159,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"