vault backup: 2025-04-10 14:02:57
This commit is contained in:
13
.obsidian/plugins/obsidian-enhancing-export-main/.editorconfig
vendored
Normal file
13
.obsidian/plugins/obsidian-enhancing-export-main/.editorconfig
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
insert_final_newline = false
|
||||||
|
max_line_length = 140
|
3
.obsidian/plugins/obsidian-enhancing-export-main/.env
vendored
Normal file
3
.obsidian/plugins/obsidian-enhancing-export-main/.env
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
# export to obsidian plugin directory directly?
|
||||||
|
OUT_DIR="./dist"
|
1
.obsidian/plugins/obsidian-enhancing-export-main/.env.development
vendored
Normal file
1
.obsidian/plugins/obsidian-enhancing-export-main/.env.development
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
NODE_ENV=development
|
52
.obsidian/plugins/obsidian-enhancing-export-main/.eslintrc.json
vendored
Normal file
52
.obsidian/plugins/obsidian-enhancing-export-main/.eslintrc.json
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"es2021": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended"
|
||||||
|
],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
"SwitchCase": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"linebreak-style": [
|
||||||
|
"error",
|
||||||
|
"unix"
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"single",
|
||||||
|
{
|
||||||
|
"avoidEscape": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"always"
|
||||||
|
],
|
||||||
|
"no-prototype-builtins": "off",
|
||||||
|
"no-constant-condition": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"checkLoops": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ignorePatterns": [
|
||||||
|
"dist/*"
|
||||||
|
]
|
||||||
|
}
|
1
.obsidian/plugins/obsidian-enhancing-export-main/.gitattributes
vendored
Normal file
1
.obsidian/plugins/obsidian-enhancing-export-main/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
* text=auto eol=lf
|
20
.obsidian/plugins/obsidian-enhancing-export-main/.github/dependabot.yml
vendored
Normal file
20
.obsidian/plugins/obsidian-enhancing-export-main/.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "npm" # See documentation for possible values
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
time: "13:00"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
time: "13:00"
|
||||||
|
open-pull-requests-limit: 10
|
119
.obsidian/plugins/obsidian-enhancing-export-main/.github/workflows/release.yml
vendored
Normal file
119
.obsidian/plugins/obsidian-enhancing-export-main/.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
name: Release Obsidian plugin
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'version'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
PLUGIN_NAME: obsidian-enhancing-export
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
bump:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal access token.
|
||||||
|
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.
|
||||||
|
- name: Use Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
- name: Create local changes
|
||||||
|
run: |
|
||||||
|
npm run version ${{ github.event.inputs.version }}
|
||||||
|
- name: Commit files
|
||||||
|
run: |
|
||||||
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
|
git config --local user.name "github-actions[bot]"
|
||||||
|
git commit -a -m "🔖 Bump version number to ${{ github.event.inputs.version }}"
|
||||||
|
git tag -a ${{ github.event.inputs.version }} -m ""
|
||||||
|
- name: Push changes
|
||||||
|
uses: ad-m/github-push-action@master
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
branch: ${{ github.ref }}
|
||||||
|
tags: true
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ "bump"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.inputs.version }}
|
||||||
|
- name: Use Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
- name: Build
|
||||||
|
id: build
|
||||||
|
run: |
|
||||||
|
pnpm install
|
||||||
|
npm run build
|
||||||
|
mkdir ${{ env.PLUGIN_NAME }}
|
||||||
|
cp dist/* ${{ env.PLUGIN_NAME }}
|
||||||
|
zip -r ${{ env.PLUGIN_NAME }}.zip ${{ env.PLUGIN_NAME }}
|
||||||
|
ls
|
||||||
|
|
||||||
|
- name: Create Release
|
||||||
|
id: create_release
|
||||||
|
uses: actions/create-release@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
VERSION: ${{ github.event.inputs.version }}
|
||||||
|
with:
|
||||||
|
tag_name: ${{ github.event.inputs.version }}
|
||||||
|
release_name: ${{ github.event.inputs.version }}
|
||||||
|
draft: false
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
|
- name: Upload zip file
|
||||||
|
id: upload-zip
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./${{ env.PLUGIN_NAME }}.zip
|
||||||
|
asset_name: ${{ env.PLUGIN_NAME }}-${{ steps.build.outputs.tag_name }}.zip
|
||||||
|
asset_content_type: application/zip
|
||||||
|
|
||||||
|
- name: Upload main.js
|
||||||
|
id: upload-main
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./dist/main.js
|
||||||
|
asset_name: main.js
|
||||||
|
asset_content_type: text/javascript
|
||||||
|
|
||||||
|
- name: Upload manifest.json
|
||||||
|
id: upload-manifest
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./dist/manifest.json
|
||||||
|
asset_name: manifest.json
|
||||||
|
asset_content_type: application/json
|
||||||
|
|
||||||
|
- name: Upload styles.css
|
||||||
|
id: upload-css
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./dist/styles.css
|
||||||
|
asset_name: styles.css
|
||||||
|
asset_content_type: text/css
|
34
.obsidian/plugins/obsidian-enhancing-export-main/.github/workflows/test.yml
vendored
Normal file
34
.obsidian/plugins/obsidian-enhancing-export-main/.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
node-version: [20.x]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: ${{ matrix.node-version }}
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version: 9
|
||||||
|
- name: Install Pandoc
|
||||||
|
run: |
|
||||||
|
wget https://github.com/jgm/pandoc/releases/download/3.1.11.1/pandoc-3.1.11.1-1-amd64.deb
|
||||||
|
sudo dpkg -i pandoc-3.1.11.1-1-amd64.deb
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: pnpm install
|
||||||
|
- name: Test
|
||||||
|
run: npm test
|
6
.obsidian/plugins/obsidian-enhancing-export-main/.gitignore
vendored
Normal file
6
.obsidian/plugins/obsidian-enhancing-export-main/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/dist
|
||||||
|
/node_modules
|
||||||
|
/coverage
|
||||||
|
/lua/main.lua
|
||||||
|
/.env.local
|
||||||
|
/.idea
|
1
.obsidian/plugins/obsidian-enhancing-export-main/.npmrc
vendored
Normal file
1
.obsidian/plugins/obsidian-enhancing-export-main/.npmrc
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
tag-version-prefix=""
|
8
.obsidian/plugins/obsidian-enhancing-export-main/.prettierrc
vendored
Normal file
8
.obsidian/plugins/obsidian-enhancing-export-main/.prettierrc
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
tabWidth: 2
|
||||||
|
useTabs: false
|
||||||
|
semi: true
|
||||||
|
singleQuote: true
|
||||||
|
quoteProps: preserve
|
||||||
|
trailingComma: es5
|
||||||
|
arrowParens: avoid
|
||||||
|
|
2
.obsidian/plugins/obsidian-enhancing-export-main/.vscode/settings.json
vendored
Normal file
2
.obsidian/plugins/obsidian-enhancing-export-main/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
63
.obsidian/plugins/obsidian-enhancing-export-main/CONTRIBUTING.md
vendored
Normal file
63
.obsidian/plugins/obsidian-enhancing-export-main/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Contributing to Obsidian Enhancing Export
|
||||||
|
|
||||||
|
First, thank you for your willingness to contribute to this project.
|
||||||
|
|
||||||
|
## Simple guide
|
||||||
|
|
||||||
|
1. Environment Preparing
|
||||||
|
|
||||||
|
- Install the `nodejs`
|
||||||
|
|
||||||
|
[https://nodejs.org/en/download](https://nodejs.org/en/download)
|
||||||
|
|
||||||
|
- Install the `pnpm`
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm install -g pnpm
|
||||||
|
```
|
||||||
|
|
||||||
|
- Clone the repository
|
||||||
|
|
||||||
|
```shell'
|
||||||
|
git clone https://github.com/mokeyish/obsidian-enhancing-export.git
|
||||||
|
```
|
||||||
|
|
||||||
|
- Install the dependencies
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd obsidian-enhancing-export
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Development & debugging (Recommend [VsCode](https://code.visualstudio.com/))
|
||||||
|
|
||||||
|
- Add `.env.local` to project root with following content
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# export to obsidian plugin directory directly
|
||||||
|
OUT_DIR="path/to/.obsidian/plugins/obsidian-enhancing-export"
|
||||||
|
```
|
||||||
|
|
||||||
|
- Enable `dev-mode `
|
||||||
|
|
||||||
|
To enable dev-mode in the obsidian, use the shortcut `Ctrl+Shift+I` or the `<F12>` key to open DevTools. and run following commands in the Console Tab of DevTools.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
localStorage.setItem('debug-plugin', '1')
|
||||||
|
```
|
||||||
|
|
||||||
|
- Build the code for debugging
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
More debug tips please see: [How to debug TypeScript in Chrome](https://blog.logrocket.com/how-to-debug-typescript-chrome/)
|
||||||
|
|
||||||
|
3. Building for Production
|
||||||
|
|
||||||
|
```shell
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Other commands please see `sciprts` of `package.json` in the project root.
|
21
.obsidian/plugins/obsidian-enhancing-export-main/LICENSE
vendored
Normal file
21
.obsidian/plugins/obsidian-enhancing-export-main/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 YISH
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
67
.obsidian/plugins/obsidian-enhancing-export-main/README.md
vendored
Normal file
67
.obsidian/plugins/obsidian-enhancing-export-main/README.md
vendored
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# Obsidian Enhancing Export Plugin
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
English | [中文](https://github.com/mokeyish/obsidian-enhancing-export/blob/master/README_zh-CN.md)
|
||||||
|
|
||||||
|
This is an enhancing export plugin base on `Pandoc` for Obsidian ([https://obsidian.md/](https://obsidian.md/)). It's allow you to export to formats like `Markdown`,`Markdown (Hugo)`,`HTML`,`docx`,`Latex` etc.
|
||||||
|
|
||||||
|
Where `Markdown`,`Markdown (Hugo)`,`HTML` will export and its media resource together.
|
||||||
|
|
||||||
|
**Note:** `Markdown`,`Markdown (Hugo)`,`HTML` are tested in Mac OS, Windows, and Linux as I used it for myself, others are not tested well.
|
||||||
|
|
||||||
|
|
||||||
|
**Ads**: You might like my other plugins 🤪
|
||||||
|
- [Obsidian Code Emitter](https://github.com/mokeyish/obsidian-code-emitter)
|
||||||
|
|
||||||
|
|
||||||
|
## Screen shot
|
||||||
|
|
||||||
|
- Export view,click on `Export to...` on file menu.
|
||||||
|
|
||||||
|

|
||||||
|
- Setting view
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. First install the latest `pandoc` (3.1.9+), and then add `pandoc` path to environment variable `PATH` or set absolute path of `pandoc` in the plugin setting view.
|
||||||
|
|
||||||
|
See more details in [https://pandoc.org/installing.html](https://pandoc.org/installing.html)。
|
||||||
|
|
||||||
|
2. Search `obsidian-enhancing-export` in the community plugins of obsidian, and install it.
|
||||||
|
|
||||||
|
## Customize export commands
|
||||||
|
|
||||||
|
You can customize your export command by yourself, click `add` in the plugin setting view and then choose template `custom` to add new custom configuration.
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
|
You can use `${variables}` in custom export command, their values are:
|
||||||
|
|
||||||
|
| Key | Value |
|
||||||
|
| ------------------------- | ------------------------------------------------------------ |
|
||||||
|
| `${outputPath}` | Output file path after export. For example, if your export to location `/User/aaa/Documents/test.pdf`, then `${outputDir}` will be replace that path. |
|
||||||
|
| `${outputDir}` | Output directory of saved exported file,It will be `/User/aaa/Documents` in above case. |
|
||||||
|
| `${outputFileName}` | File name (without extension) of the saved exported file. It will be `test` in above case. |
|
||||||
|
| `${outputFileFullName}` | File name (with extension) of the saved exported file. It will be `test.pdf` in above case. |
|
||||||
|
| `${currentPath}` | Path of currently edited file. For example, if your are editing `/User/aaa/Documents/readme.md`, the the value will be `/User/aaa/Documents/readme.md`. |
|
||||||
|
| `${currentDir}` | Current directory of currently edited file, It will be`/User/aaa/Documents` in above case. |
|
||||||
|
| `${currentFileName}` | Filename without extension of currently edited file, It will be `readme` in above case. |
|
||||||
|
| `${currentFileFullName}` | Filename with extension of currently edited file. It will be `readme.md` in above case. |
|
||||||
|
| `${vaultDir}` | The obsidian current vaultDir. |
|
||||||
|
| `${attachmentFolderPath}` | The `attachmentFolderPath` of Obsidian. |
|
||||||
|
| Others variables | You can use `keyword: value` in [YAML Front Matter](https://jekyllrb.com/docs/front-matter/), then use `${metadata.keyword}` |
|
||||||
|
|
||||||
|
## Related resources
|
||||||
|
|
||||||
|
- **Tutorial**: [Obsidian Tutorial for Academic Writing](https://betterhumans.pub/obsidian-tutorial-for-academic-writing-87b038060522) - tutorial on how to setup this plugin and use it for academic writing (export to `.docx`, `.pdf`, `.tex`, `.bib`)
|
||||||
|
- **A collection of lua filters for pandoc**: [https://github.com/pandoc-ext](https://github.com/pandoc-ext) - Filters and other goodies to get the most out of pandoc, the universal document converter.
|
||||||
|
- **Math latex editor**: [https://math.yish.org/](https://math.yish.org/)
|
||||||
|
|
||||||
|
## Finally
|
||||||
|
|
||||||
|
- Welcome to provide more command templates to [here](src/export_templates.ts).
|
||||||
|
- Feel free to file an issue for any questions.
|
52
.obsidian/plugins/obsidian-enhancing-export-main/README_zh-CN.md
vendored
Normal file
52
.obsidian/plugins/obsidian-enhancing-export-main/README_zh-CN.md
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# Obsidian Enhancing Export Plugin
|
||||||
|
|
||||||
|
[English](https://github.com/mokeyish/obsidian-enhancing-export/blob/master/README.md) | 中文
|
||||||
|
|
||||||
|
这是一个基于 Pandoc 的 Obsidian 加强版导出插件。提供了基本的导出格式:Markdown 、Markdown(Hugo [https://gohugo.io/](https://gohugo.io/))、Html、docx、Latex等。
|
||||||
|
其中 Markdown 、Markdown(Hugo)、Html 会把媒体资源一并导出。
|
||||||
|
|
||||||
|
**注意:** 目前自用的就是 Markdown 、Markdown(Hugo)、Html,在 Mac OS、Windows、Linux 可正常使用,其他未经严格测试。
|
||||||
|
|
||||||
|
## 界面截图
|
||||||
|
- 导出界面,在文件菜单上点击 `导出为......`
|
||||||
|
|
||||||
|

|
||||||
|
- 设置界面
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 安装
|
||||||
|
1. 需要先安装最新的 `pandoc`(3.1.9+),最好配置到 PATH 环境变量,或者设置界面指定路径。
|
||||||
|
参考地址:[https://pandoc.org/installing.html](https://pandoc.org/installing.html)
|
||||||
|
2. 在 Obsidian 插件市场,搜索 `obsidian-enhancing-export` 进行安装。
|
||||||
|
|
||||||
|
## 自定义命令
|
||||||
|
|
||||||
|
本插件是支持自定义导出命令的,在设置界面,点击添加按钮,选择 `Custom` 作为模板,即可新增一个自定义导出的配置了。
|
||||||
|
|
||||||
|
### 变量
|
||||||
|
你可以使用 `${variable}` 在自定义导出的命令中。它们的值是:
|
||||||
|
|
||||||
|
| 变量名 | 值 |
|
||||||
|
| -- | -- |
|
||||||
|
| `${outputPath}` |导出路径,例如,你的导出位置是:`/User/aaa/Documents/test.pdf` ,则 `${outputDir}` 会替换为那个路径。|
|
||||||
|
| `${outputDir}` | 导出目录,按上面的例子,它会被替换为 `/User/aaa/Documents`。 |
|
||||||
|
| `${outputFileName}` | 没有扩展名的文件名,按上面的例子,它会被替换为 `test`。 |
|
||||||
|
| `${outputFileFullName}` | 文件的全名,按上面的例子,它会被替换为 `test.pdf`。 |
|
||||||
|
| `${currentPath}` | 当前文件路径,例如当前的文件位置是 `/User/aaa/Documents/readme.md`,那么它会被替换为这个文件的位置。 |
|
||||||
|
| `${currentDir}` | 当前文件所在目录,按上面的例子,值为 `/User/aaa/Documents`。 |
|
||||||
|
| `${currentFileName}` | 当前文件不带扩展名的名字,值是 `readme` |
|
||||||
|
| `${currentFileFullName}` | 当前文件全名,值是 `readme.md`。 |
|
||||||
|
| `${vaultDir}` | Obsidian 当前的 vaultDir. |
|
||||||
|
| `${attachmentFolderPath}`| Obsidian 的附件目录 |
|
||||||
|
| 其他变量 | 你可以在 [YAML Front Matter](https://jekyllrb.com/docs/front-matter/) 中定义 `keyword: value` 变量,然后以 `${metadata.keyword}`引用它。 |
|
||||||
|
|
||||||
|
## Related resources
|
||||||
|
|
||||||
|
- **Pandoc 的 lua filters 集合**: [https://github.com/pandoc-ext](https://github.com/pandoc-ext)
|
||||||
|
- **Latex 数学公式编辑器**: [https://math.yish.org/](https://math.yish.org/)
|
||||||
|
|
||||||
|
## 最后
|
||||||
|
|
||||||
|
- 欢迎提供更多命令模板到[这里](src/export_templates.ts).。
|
||||||
|
- 有问题可以提交 Issue 给我。
|
5
.obsidian/plugins/obsidian-enhancing-export-main/jest.config.cjs
vendored
Normal file
5
.obsidian/plugins/obsidian-enhancing-export-main/jest.config.cjs
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||||
|
module.exports = {
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node'
|
||||||
|
};
|
6
.obsidian/plugins/obsidian-enhancing-export-main/lua/citefilter.lua
vendored
Normal file
6
.obsidian/plugins/obsidian-enhancing-export-main/lua/citefilter.lua
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
-- credits to tarleb — StackExchange: https://tex.stackexchange.com/questions/392070/pandoc-markdown-create-self-contained-bib-file-from-cited-references
|
||||||
|
function Pandoc(d)
|
||||||
|
d.meta.references = pandoc.utils.references(d)
|
||||||
|
d.meta.bibliography = nil
|
||||||
|
return d
|
||||||
|
end
|
5
.obsidian/plugins/obsidian-enhancing-export-main/lua/markdown+hugo.lua
vendored
Normal file
5
.obsidian/plugins/obsidian-enhancing-export-main/lua/markdown+hugo.lua
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package.path=package.path..";" ..debug.getinfo(1).source:match("(.*[/\\])"):sub(2) .. "?.lua"
|
||||||
|
|
||||||
|
Mode='hugo'
|
||||||
|
|
||||||
|
require('markdown')
|
237
.obsidian/plugins/obsidian-enhancing-export-main/lua/markdown.lua
vendored
Normal file
237
.obsidian/plugins/obsidian-enhancing-export-main/lua/markdown.lua
vendored
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
package.path=debug.getinfo(1).source:gsub('@',''):sub(0):match('(.*[/\\])'):sub(0) .. '?.lua' .. ';' .. package.path
|
||||||
|
|
||||||
|
require("polyfill")
|
||||||
|
local url = require('url')
|
||||||
|
|
||||||
|
local pandoc=pandoc
|
||||||
|
local PANDOC_STATE=PANDOC_STATE
|
||||||
|
|
||||||
|
PANDOC_VERSION:must_be_at_least '3.1.7'
|
||||||
|
|
||||||
|
os.text = pandoc.text
|
||||||
|
|
||||||
|
local PATH = pandoc.path
|
||||||
|
local doc_dir = nil
|
||||||
|
local media_dir = nil
|
||||||
|
|
||||||
|
if Mode == nil then
|
||||||
|
Mode = 'default'
|
||||||
|
end
|
||||||
|
|
||||||
|
-- print("Mode: "..Mode)
|
||||||
|
|
||||||
|
if PANDOC_STATE.output_file then
|
||||||
|
local output_file = PANDOC_STATE.output_file
|
||||||
|
doc_dir = PATH.directory(output_file)
|
||||||
|
if PANDOC_WRITER_OPTIONS.variables["media_dir"] then
|
||||||
|
media_dir = tostring(PANDOC_WRITER_OPTIONS.variables["media_dir"])
|
||||||
|
else
|
||||||
|
media_dir = PATH.split_extension(output_file)
|
||||||
|
if Mode ~= 'hugo' then
|
||||||
|
media_dir = media_dir .. '-media'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
assert(doc_dir, "doc_dir is nil")
|
||||||
|
assert(media_dir, "media_dir is nil")
|
||||||
|
|
||||||
|
|
||||||
|
local function get_absolute_path(file_path)
|
||||||
|
if PATH.is_absolute(file_path) then
|
||||||
|
return file_path
|
||||||
|
end
|
||||||
|
for _, dir in pairs(PANDOC_STATE.resource_path) do
|
||||||
|
local full_path = PATH.join({dir, file_path})
|
||||||
|
if os.exists(full_path) then
|
||||||
|
return full_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for _, file in pairs(PANDOC_STATE.input_files) do
|
||||||
|
if not PATH.is_absolute(file) then
|
||||||
|
file = PATH.join({pandoc.system.get_working_directory(), file_path})
|
||||||
|
end
|
||||||
|
local dir = PATH.directory(file)
|
||||||
|
local full_path = PATH.join({dir, file_path})
|
||||||
|
if os.exists(full_path) then
|
||||||
|
return full_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_output_file(file_path)
|
||||||
|
if media_dir then
|
||||||
|
local new_file_name = pandoc.utils.sha1(file_path)
|
||||||
|
local _, new_file_ext = PATH.split_extension(file_path)
|
||||||
|
file_path = new_file_name .. new_file_ext
|
||||||
|
local full_path = PATH.join({media_dir, file_path})
|
||||||
|
return full_path
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function extract_media(file_path)
|
||||||
|
os.mkdir(media_dir)
|
||||||
|
file_path = url.decode(file_path)
|
||||||
|
local abs_path = get_absolute_path(file_path)
|
||||||
|
local file = get_output_file(file_path)
|
||||||
|
if abs_path and file then
|
||||||
|
if not os.exists(file) then
|
||||||
|
os.copy(abs_path, file)
|
||||||
|
end
|
||||||
|
local rel_path = PATH.make_relative(file, doc_dir, false)
|
||||||
|
local parts = PATH.split(rel_path)
|
||||||
|
for i,v in ipairs(parts) do
|
||||||
|
parts[i] = url.encode(v)
|
||||||
|
end
|
||||||
|
local encoded_rel_path = table.concat(parts, "/")
|
||||||
|
if Mode == 'hugo' then
|
||||||
|
encoded_rel_path = '../' .. encoded_rel_path
|
||||||
|
end
|
||||||
|
return encoded_rel_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function raw(s)
|
||||||
|
return pandoc.RawInline('markdown', s)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Image(el)
|
||||||
|
local src = extract_media(el.src)
|
||||||
|
if src then
|
||||||
|
el.src = src
|
||||||
|
end
|
||||||
|
return el
|
||||||
|
end
|
||||||
|
|
||||||
|
function Space()
|
||||||
|
return raw(' ')
|
||||||
|
end
|
||||||
|
|
||||||
|
function SoftBreak()
|
||||||
|
return raw('\n')
|
||||||
|
end
|
||||||
|
|
||||||
|
function RawInline(el)
|
||||||
|
if el.format == "html" then
|
||||||
|
el.format = 'markdown'
|
||||||
|
el.text = string.gsub(el.text, '<img[^>]+>', function(img)
|
||||||
|
return string.gsub(img, 'src="([^"]+)"', function(url)
|
||||||
|
if string.find(url, '^[Hh][Tt][Tt][Pp][Ss]?://') == nil then
|
||||||
|
local extract_media_url = extract_media(url)
|
||||||
|
if extract_media_url then
|
||||||
|
return 'src="' .. extract_media_url .. '"'
|
||||||
|
end
|
||||||
|
return '123'
|
||||||
|
end
|
||||||
|
return 'src="' .. url .. '"'
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
return el
|
||||||
|
end
|
||||||
|
|
||||||
|
function RawBlock(el)
|
||||||
|
if el.format == "html" then
|
||||||
|
el.format = 'markdown'
|
||||||
|
end
|
||||||
|
return el
|
||||||
|
end
|
||||||
|
|
||||||
|
function Math(el)
|
||||||
|
if Mode == 'hugo' then
|
||||||
|
if el.mathtype == 'DisplayMath' then
|
||||||
|
return raw('{{< mathjax >}}\n$$' .. el.text .. '$$\n{{</mathjax >}}')
|
||||||
|
else
|
||||||
|
el.text = string.gsub(el.text, '\\[\\{\\}]', function (v)
|
||||||
|
return '\\' .. v
|
||||||
|
end)
|
||||||
|
el.text = string.gsub(el.text, '_', function (v)
|
||||||
|
return '\\' .. v
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return el
|
||||||
|
end
|
||||||
|
|
||||||
|
local function headerLink(input)
|
||||||
|
-- github style section link
|
||||||
|
return "#"..input:gsub(' ', '-')
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function insertLink(content, linkDescription)
|
||||||
|
local descriptionText = table.concat(linkDescription, "")
|
||||||
|
|
||||||
|
if string.find(descriptionText, '|') then
|
||||||
|
local target, desc = descriptionText:match("(.*)|(.*)")
|
||||||
|
table.insert(content, pandoc.Link(desc, headerLink(target)))
|
||||||
|
else
|
||||||
|
table.insert(content, pandoc.Link(descriptionText, headerLink(descriptionText)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Para(el)
|
||||||
|
local content = el.content
|
||||||
|
content = ProcessMath(content)
|
||||||
|
content = ProcessInternalLinks(content)
|
||||||
|
el.content = content
|
||||||
|
return el
|
||||||
|
end
|
||||||
|
|
||||||
|
function ProcessMath(elements)
|
||||||
|
local content = {}
|
||||||
|
local in_display_math = false
|
||||||
|
for _, item in pairs(elements) do
|
||||||
|
if item.t == 'Str'and item.text == "$$" then
|
||||||
|
in_display_math = not in_display_math
|
||||||
|
else
|
||||||
|
if in_display_math then
|
||||||
|
if item.t == 'RawInline' and item.format == 'tex' then
|
||||||
|
local n = pandoc.Math('DisplayMath', '\n' .. item.text .. '\n')
|
||||||
|
table.insert(content, Math(n))
|
||||||
|
else
|
||||||
|
table.insert(content, item)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(content, item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
function ProcessInternalLinks(elements)
|
||||||
|
local content = {}
|
||||||
|
local in_section_link = false
|
||||||
|
local linkDescription = {}
|
||||||
|
|
||||||
|
for _, item in pairs(elements) do
|
||||||
|
if item.t == 'Str' and string.starts_with(item.text, '[[#') then
|
||||||
|
in_section_link = true
|
||||||
|
table.insert(linkDescription, string.sub(item.text, 4))
|
||||||
|
elseif in_section_link then
|
||||||
|
if string.ends_with(item.text, ']]') then
|
||||||
|
table.insert(linkDescription, string.sub(item.text, 1, -3))
|
||||||
|
insertLink(content, linkDescription)
|
||||||
|
in_section_link = false
|
||||||
|
linkDescription = {}
|
||||||
|
else
|
||||||
|
table.insert(linkDescription, item.text)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(content, item)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return content
|
||||||
|
end
|
||||||
|
|
||||||
|
function Plain(el)
|
||||||
|
el.content = ProcessInternalLinks(el.content)
|
||||||
|
return el
|
||||||
|
end
|
||||||
|
|
||||||
|
function Pandoc(el)
|
||||||
|
return el
|
||||||
|
end
|
68
.obsidian/plugins/obsidian-enhancing-export-main/lua/math_block.lua
vendored
Normal file
68
.obsidian/plugins/obsidian-enhancing-export-main/lua/math_block.lua
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
traverse = 'topdown'
|
||||||
|
|
||||||
|
math_block_text = nil
|
||||||
|
function process(el)
|
||||||
|
|
||||||
|
-- MathBlock start or end
|
||||||
|
if el.t == 'Str' and el.text == '$$' then
|
||||||
|
if math_block_text == nil then -- start
|
||||||
|
math_block_text = ''
|
||||||
|
else -- end
|
||||||
|
local math_block = pandoc.Math('DisplayMath', '\n' .. math_block_text .. '\n')
|
||||||
|
math_block_text = nil
|
||||||
|
return math_block
|
||||||
|
end
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
if math_block_text then
|
||||||
|
if (el.t == 'RawInline' or el.t == 'RawBlock') and el.format == 'tex' then
|
||||||
|
math_block_text = math_block_text .. el.text
|
||||||
|
return {}
|
||||||
|
elseif el.t == 'Str' then
|
||||||
|
math_block_text = math_block_text .. el.text
|
||||||
|
return {}
|
||||||
|
elseif el.t == 'SoftBreak' or el.t == 'BulletList' then
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return el
|
||||||
|
end
|
||||||
|
|
||||||
|
function RawInline(el)
|
||||||
|
return process(el)
|
||||||
|
end
|
||||||
|
|
||||||
|
function RawBlock(el)
|
||||||
|
return process(el)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Str(el)
|
||||||
|
return process(el)
|
||||||
|
end
|
||||||
|
|
||||||
|
function SoftBreak(el)
|
||||||
|
return process(el)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Header(el)
|
||||||
|
return process(el)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Para(el)
|
||||||
|
return process(el)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Plain(el)
|
||||||
|
return process(el)
|
||||||
|
end
|
||||||
|
|
||||||
|
function BulletList(el)
|
||||||
|
return process(el)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
12
.obsidian/plugins/obsidian-enhancing-export-main/lua/pdf.lua
vendored
Normal file
12
.obsidian/plugins/obsidian-enhancing-export-main/lua/pdf.lua
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
Math = function (elem)
|
||||||
|
if elem.text:find("^%s*\\begin{") ~= nil then
|
||||||
|
return pandoc.RawInline('tex', elem.text)
|
||||||
|
else
|
||||||
|
return elem
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
}
|
61
.obsidian/plugins/obsidian-enhancing-export-main/lua/polyfill.lua
vendored
Normal file
61
.obsidian/plugins/obsidian-enhancing-export-main/lua/polyfill.lua
vendored
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
os.platform = nil
|
||||||
|
if os.platform == nil then
|
||||||
|
local libExt = package.cpath:match("%p[\\|/]?\\.%p(%a+)")
|
||||||
|
if libExt == 'dll' then
|
||||||
|
os.platform = "Windows"
|
||||||
|
elseif libExt == 'so' then
|
||||||
|
os.platform = "Linux"
|
||||||
|
elseif libExt == 'dylib' then
|
||||||
|
os.platform = "MacOS"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
os.copy = function(src, dest)
|
||||||
|
if os.platform == "Windows" then
|
||||||
|
src = string.gsub(src, "/", "\\")
|
||||||
|
src = os.text.toencoding(src)
|
||||||
|
dest = os.text.toencoding(dest)
|
||||||
|
os.execute('copy "' .. src .. '" "' .. dest .. '" >NUL')
|
||||||
|
else
|
||||||
|
os.execute('cp "' .. src .. '" "' .. dest .. '"')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
os.mkdir = function(dir)
|
||||||
|
if os.exists(dir) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if os.platform == "Windows" then
|
||||||
|
dir = os.text.toencoding(dir)
|
||||||
|
os.execute('mkdir "' .. dir .. '"')
|
||||||
|
else
|
||||||
|
os.execute('mkdir -p "' .. dir .. '"')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
os.exists = function(path)
|
||||||
|
if os.platform == "Windows" then
|
||||||
|
path = string.gsub(path, "/", "\\")
|
||||||
|
path = os.text.toencoding(path)
|
||||||
|
local _, _, code = os.execute('if exist "' .. path .. '" (exit 0) else (exit 1)')
|
||||||
|
return code == 0
|
||||||
|
else
|
||||||
|
local _, _, code = os.execute('test -e "' .. path .. '"')
|
||||||
|
return code == 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
string.starts_with = function(str, start)
|
||||||
|
return str:sub(1, #start) == start
|
||||||
|
end
|
||||||
|
|
||||||
|
string.ends_with = function(str, ending)
|
||||||
|
return ending == "" or str:sub(-#ending) == ending
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
os = os,
|
||||||
|
string = string
|
||||||
|
}
|
18
.obsidian/plugins/obsidian-enhancing-export-main/lua/url.lua
vendored
Normal file
18
.obsidian/plugins/obsidian-enhancing-export-main/lua/url.lua
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
local function encode (str)
|
||||||
|
str = string.gsub (str, "([^0-9a-zA-Z !'()*._~-])", -- locale independent
|
||||||
|
function (c) return string.format ("%%%02X", string.byte(c)) end)
|
||||||
|
str = string.gsub (str, " ", "%%20")
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function decode (str)
|
||||||
|
str = string.gsub (str, "%%20", " ")
|
||||||
|
str = string.gsub (str, "%%(%x%x)", function(h) return string.char(tonumber(h,16)) end)
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
return {
|
||||||
|
encode = encode,
|
||||||
|
decode = decode
|
||||||
|
}
|
1278
.obsidian/plugins/obsidian-enhancing-export-main/lua/utf8_filenames.lua.bak
vendored
Normal file
1278
.obsidian/plugins/obsidian-enhancing-export-main/lua/utf8_filenames.lua.bak
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10
.obsidian/plugins/obsidian-enhancing-export-main/manifest.json
vendored
Normal file
10
.obsidian/plugins/obsidian-enhancing-export-main/manifest.json
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"id": "obsidian-enhancing-export",
|
||||||
|
"name": "Enhancing Export",
|
||||||
|
"version": "1.10.7",
|
||||||
|
"minAppVersion": "1.6.3",
|
||||||
|
"description": "This is a enhancing export plugin for Obsidian. It allows to export to formats like Html, DOCX, ePub and PDF or Markdown(Hugo) etc.",
|
||||||
|
"author": "YISH",
|
||||||
|
"authorUrl": "https://github.com/mokeyish",
|
||||||
|
"isDesktopOnly": true
|
||||||
|
}
|
46
.obsidian/plugins/obsidian-enhancing-export-main/package.json
vendored
Normal file
46
.obsidian/plugins/obsidian-enhancing-export-main/package.json
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "obsidian-enhancing-export",
|
||||||
|
"version": "1.10.7",
|
||||||
|
"description": "This is a enhancing export plugin for Obsidian. It allows to export to formats like Html, DOCX, ePub and PDF or Markdown(Hugo) etc.",
|
||||||
|
"main": "dist/main.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite build -w -m development",
|
||||||
|
"build": "vite build",
|
||||||
|
"version": "node version-bump.mjs",
|
||||||
|
"lint": "eslint --ext .ts,.js src",
|
||||||
|
"lint-fix": "eslint --fix --ext .ts,.js src",
|
||||||
|
"format-check": "prettier --check \"src/**/*.ts\"",
|
||||||
|
"format-fix": "prettier --write \"src/**/*.ts\"",
|
||||||
|
"test": "jest"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "YISH",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": "https://github.com/mokeyish/obsidian-enhancing-export",
|
||||||
|
"type": "module",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^29.5.1",
|
||||||
|
"@types/node": "^20.14.8",
|
||||||
|
"@types/semver": "^7.5.0",
|
||||||
|
"@types/yargs-parser": "^21.0.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
||||||
|
"@typescript-eslint/parser": "^5.59.7",
|
||||||
|
"builtin-modules": "^3.3.0",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
|
"eslint": "^8.41.0",
|
||||||
|
"jest": "^29.5.0",
|
||||||
|
"obsidian": "latest",
|
||||||
|
"prettier": "^3.3.3",
|
||||||
|
"ts-jest": "^29.1.0",
|
||||||
|
"tslib": "2.6.3",
|
||||||
|
"typescript": "5.0.4",
|
||||||
|
"vite": "^5.2.13",
|
||||||
|
"vite-plugin-solid": "^2.10.2",
|
||||||
|
"vite-plugin-static-copy": "^1.0.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"semver": "^7.5.1",
|
||||||
|
"solid-js": "^1.8.20",
|
||||||
|
"yargs-parser": "^21.1.1"
|
||||||
|
}
|
||||||
|
}
|
4201
.obsidian/plugins/obsidian-enhancing-export-main/pnpm-lock.yaml
generated
vendored
Normal file
4201
.obsidian/plugins/obsidian-enhancing-export-main/pnpm-lock.yaml
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
BIN
.obsidian/plugins/obsidian-enhancing-export-main/screenshot/exportview_en-US.png
vendored
Normal file
BIN
.obsidian/plugins/obsidian-enhancing-export-main/screenshot/exportview_en-US.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
.obsidian/plugins/obsidian-enhancing-export-main/screenshot/exportview_zh-CN.png
vendored
Normal file
BIN
.obsidian/plugins/obsidian-enhancing-export-main/screenshot/exportview_zh-CN.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
BIN
.obsidian/plugins/obsidian-enhancing-export-main/screenshot/settingview_en-US.png
vendored
Normal file
BIN
.obsidian/plugins/obsidian-enhancing-export-main/screenshot/settingview_en-US.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 46 KiB |
BIN
.obsidian/plugins/obsidian-enhancing-export-main/screenshot/settingview_zh-CN.png
vendored
Normal file
BIN
.obsidian/plugins/obsidian-enhancing-export-main/screenshot/settingview_zh-CN.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
141
.obsidian/plugins/obsidian-enhancing-export-main/src/export_templates.ts
vendored
Normal file
141
.obsidian/plugins/obsidian-enhancing-export-main/src/export_templates.ts
vendored
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import type { ExportSetting } from './settings';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Variables
|
||||||
|
* - ${attachmentFolderPath} --> obsidian' settings.
|
||||||
|
*
|
||||||
|
* /User/aaa/Documents/test.pdf
|
||||||
|
* - ${outputDir} --> /User/aaa/Documents/
|
||||||
|
* - ${outputPath} --> /User/aaa/Documents/test.pdf
|
||||||
|
* - ${outputFileName} --> test
|
||||||
|
* - ${outputFileFullName} --> test.pdf
|
||||||
|
*
|
||||||
|
* /User/aaa/Documents/test.pdf
|
||||||
|
* - ${currentDir} --> /User/aaa/Documents/
|
||||||
|
* - ${currentPath} --> /User/aaa/Documents/test.pdf
|
||||||
|
* - ${currentFileName} --> test
|
||||||
|
* - ${CurrentFileFullName} --> test.pdf
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'Markdown': {
|
||||||
|
name: 'Markdown',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments:
|
||||||
|
'-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" --lua-filter="${luaDir}/markdown.lua" -s -o "${outputPath}" -t commonmark_x-attributes',
|
||||||
|
extension: '.md',
|
||||||
|
},
|
||||||
|
'Markdown (Hugo)': {
|
||||||
|
name: 'Markdown (Hugo)',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments:
|
||||||
|
'-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" --lua-filter="${luaDir}/markdown+hugo.lua" -s -o "${outputPath}" -t commonmark_x-attributes',
|
||||||
|
extension: '.md',
|
||||||
|
},
|
||||||
|
'Html': {
|
||||||
|
name: 'Html',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments:
|
||||||
|
'-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" --lua-filter="${luaDir}/math_block.lua" --embed-resources --standalone --metadata title="${currentFileName}" -s -o "${outputPath}" -t html',
|
||||||
|
customArguments: '--mathjax="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg-full.js"',
|
||||||
|
extension: '.html',
|
||||||
|
},
|
||||||
|
'TextBundle': {
|
||||||
|
name: 'TextBundle',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments:
|
||||||
|
'-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" --lua-filter="${luaDir}/markdown.lua" -V media_dir="${outputDir}/${outputFileName}.textbundle/assets" -s -o "${outputDir}/${outputFileName}.textbundle/text.md" -t commonmark_x-attributes',
|
||||||
|
extension: '.md',
|
||||||
|
},
|
||||||
|
'Typst': {
|
||||||
|
name: 'Typst',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments:
|
||||||
|
'-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" --lua-filter="${luaDir}/markdown.lua" -s -o "${outputPath}" -t typst',
|
||||||
|
extension: '.typ',
|
||||||
|
},
|
||||||
|
'PDF': {
|
||||||
|
name: 'PDF',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments:
|
||||||
|
'-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" --lua-filter="${luaDir}/pdf.lua" ${ options.textemplate ? `--resource-path="${pluginDir}/textemplate" --template="${options.textemplate}"` : ` ` } -o "${outputPath}" -t pdf',
|
||||||
|
customArguments: '--pdf-engine=pdflatex',
|
||||||
|
optionsMeta: {
|
||||||
|
'textemplate': 'preset:textemplate', // reference from `PresetOptionsMeta` in `src/settings.ts`
|
||||||
|
},
|
||||||
|
extension: '.pdf',
|
||||||
|
},
|
||||||
|
'Word (.docx)': {
|
||||||
|
name: 'Word (.docx)',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments: '-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" -o "${outputPath}" -t docx',
|
||||||
|
extension: '.docx',
|
||||||
|
},
|
||||||
|
'OpenOffice': {
|
||||||
|
name: 'OpenOffice',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments: '-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" -o "${outputPath}" -t odt',
|
||||||
|
extension: '.odt',
|
||||||
|
},
|
||||||
|
'RTF': {
|
||||||
|
name: 'RTF',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments: '-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" -s -o "${outputPath}" -t rtf',
|
||||||
|
extension: '.rtf',
|
||||||
|
},
|
||||||
|
'Epub': {
|
||||||
|
name: 'Epub',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments: '-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" -o "${outputPath}" -t epub',
|
||||||
|
extension: '.epub',
|
||||||
|
},
|
||||||
|
'Latex': {
|
||||||
|
name: 'Latex',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments:
|
||||||
|
'-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" ${ options.textemplate ? `--resource-path="${pluginDir}/textemplate" --template="${options.textemplate}"` : ` ` } --extract-media="${outputDir}" -s -o "${outputPath}" -t latex',
|
||||||
|
optionsMeta: {
|
||||||
|
'textemplate': 'preset:textemplate', // reference from `PresetOptionsMeta` in `src/settings.ts`
|
||||||
|
},
|
||||||
|
extension: '.tex',
|
||||||
|
},
|
||||||
|
'Media Wiki': {
|
||||||
|
name: 'Media Wiki',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments:
|
||||||
|
'-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" -s -o "${outputPath}" -t mediawiki',
|
||||||
|
extension: '.mediawiki',
|
||||||
|
},
|
||||||
|
'reStructuredText': {
|
||||||
|
name: 'reStructuredText',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments: '-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" -s -o "${outputPath}" -t rst',
|
||||||
|
extension: '.rst',
|
||||||
|
},
|
||||||
|
'Textile': {
|
||||||
|
name: 'Textile',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments:
|
||||||
|
'-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" -s -o "${outputPath}" -t textile',
|
||||||
|
extension: '.textile',
|
||||||
|
},
|
||||||
|
'OPML': {
|
||||||
|
name: 'OPML',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments: '-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" -s -o "${outputPath}" -t opml',
|
||||||
|
extension: '.opml',
|
||||||
|
},
|
||||||
|
'Bibliography (.bib)': {
|
||||||
|
name: 'Bibliography',
|
||||||
|
type: 'pandoc',
|
||||||
|
arguments:
|
||||||
|
'-f ${fromFormat} --resource-path="${currentDir}" --resource-path="${attachmentFolderPath}" --lua-filter="${luaDir}/citefilter.lua" -o "${outputPath}" --to=bibtex "${currentPath}"',
|
||||||
|
extension: '.bib',
|
||||||
|
},
|
||||||
|
'Custom': {
|
||||||
|
name: 'Custom',
|
||||||
|
type: 'custom',
|
||||||
|
command: 'your command',
|
||||||
|
targetFileExtensions: '.ext',
|
||||||
|
},
|
||||||
|
} satisfies Record<string, ExportSetting> as Record<string, ExportSetting>;
|
234
.obsidian/plugins/obsidian-enhancing-export-main/src/exporto0o.ts
vendored
Normal file
234
.obsidian/plugins/obsidian-enhancing-export-main/src/exporto0o.ts
vendored
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
import * as ct from 'electron';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import process from 'process';
|
||||||
|
import path from 'path';
|
||||||
|
import argsParser from 'yargs-parser';
|
||||||
|
import { Variables, ExportSetting, extractDefaultExtension as extractExtension, createEnv } from './settings';
|
||||||
|
import { MessageBox } from './ui/message_box';
|
||||||
|
import { Notice, TFile } from 'obsidian';
|
||||||
|
import { exec, renderTemplate, getPlatformValue, trimQuotes } from './utils';
|
||||||
|
import ProgressBar from './ui/components/ProgressBar';
|
||||||
|
import type ExportPlugin from './main';
|
||||||
|
import pandoc from './pandoc';
|
||||||
|
|
||||||
|
export async function exportToOo(
|
||||||
|
plugin: ExportPlugin,
|
||||||
|
currentFile: TFile,
|
||||||
|
candidateOutputDirectory: string,
|
||||||
|
candidateOutputFileName: string | undefined,
|
||||||
|
setting: ExportSetting,
|
||||||
|
showOverwriteConfirmation?: boolean,
|
||||||
|
options?: unknown,
|
||||||
|
onSuccess?: () => void,
|
||||||
|
onFailure?: () => void,
|
||||||
|
beforeExport?: () => void
|
||||||
|
) {
|
||||||
|
const {
|
||||||
|
settings: globalSetting,
|
||||||
|
lang,
|
||||||
|
manifest,
|
||||||
|
app: {
|
||||||
|
vault: { adapter, config: obsidianConfig },
|
||||||
|
metadataCache,
|
||||||
|
},
|
||||||
|
} = plugin;
|
||||||
|
|
||||||
|
if (!candidateOutputFileName) {
|
||||||
|
const extension = extractExtension(setting);
|
||||||
|
candidateOutputFileName = `${currentFile.basename}${extension}`;
|
||||||
|
}
|
||||||
|
if (showOverwriteConfirmation == undefined) {
|
||||||
|
showOverwriteConfirmation = globalSetting.showOverwriteConfirmation;
|
||||||
|
}
|
||||||
|
|
||||||
|
const showExportProgressBar = globalSetting.showExportProgressBar;
|
||||||
|
|
||||||
|
/* Variables
|
||||||
|
* /User/aaa/Documents/test.pdf
|
||||||
|
* - ${outputDir} --> /User/aaa/Documents/
|
||||||
|
* - ${outputPath} --> /User/aaa/Documents/test.pdf
|
||||||
|
* - ${outputFileName} --> test
|
||||||
|
* - ${outputFileFullName} --> test.pdf
|
||||||
|
*
|
||||||
|
* /User/aaa/Documents/test.pdf
|
||||||
|
* - ${currentDir} --> /User/aaa/Documents/
|
||||||
|
* - ${currentPath} --> /User/aaa/Documents/test.pdf
|
||||||
|
* - ${CurrentFileName} --> test
|
||||||
|
* - ${CurrentFileFullName} --> test.pdf
|
||||||
|
*/
|
||||||
|
const vaultDir = adapter.getBasePath();
|
||||||
|
const pluginDir = `${vaultDir}/${manifest.dir}`;
|
||||||
|
const luaDir = `${pluginDir}/lua`;
|
||||||
|
const outputDir = candidateOutputDirectory;
|
||||||
|
const outputPath = `${outputDir}/${candidateOutputFileName}`;
|
||||||
|
const outputFileName = candidateOutputFileName.substring(0, candidateOutputFileName.lastIndexOf('.'));
|
||||||
|
const outputFileFullName = candidateOutputFileName;
|
||||||
|
|
||||||
|
const currentPath = adapter.getFullPath(currentFile.path);
|
||||||
|
const currentDir = path.dirname(currentPath);
|
||||||
|
const currentFileName = currentFile.basename;
|
||||||
|
const currentFileFullName = currentFile.name;
|
||||||
|
|
||||||
|
let attachmentFolderPath = obsidianConfig.attachmentFolderPath ?? '/';
|
||||||
|
if (attachmentFolderPath === '/') {
|
||||||
|
attachmentFolderPath = vaultDir;
|
||||||
|
} else if (attachmentFolderPath.startsWith('.')) {
|
||||||
|
attachmentFolderPath = path.join(currentDir, attachmentFolderPath.substring(1));
|
||||||
|
} else {
|
||||||
|
attachmentFolderPath = path.join(vaultDir, attachmentFolderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
let frontMatter: unknown = null;
|
||||||
|
try {
|
||||||
|
frontMatter = metadataCache.getCache(currentFile.path).frontmatter;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
const variables: Variables = {
|
||||||
|
pluginDir,
|
||||||
|
luaDir,
|
||||||
|
outputDir,
|
||||||
|
outputPath,
|
||||||
|
outputFileName,
|
||||||
|
outputFileFullName,
|
||||||
|
currentDir,
|
||||||
|
currentPath,
|
||||||
|
currentFileName,
|
||||||
|
currentFileFullName,
|
||||||
|
attachmentFolderPath,
|
||||||
|
vaultDir,
|
||||||
|
// date: new Date(currentFile.stat.ctime),
|
||||||
|
// lastMod: new Date(currentFile.stat.mtime),
|
||||||
|
// now: new Date()
|
||||||
|
metadata: frontMatter,
|
||||||
|
options,
|
||||||
|
fromFormat: app.vault.config.useMarkdownLinks ? 'markdown' : 'markdown+wikilinks_title_after_pipe',
|
||||||
|
};
|
||||||
|
|
||||||
|
const showCommandLineOutput = setting.type === 'custom' && setting.showCommandOutput;
|
||||||
|
const openExportedFileLocation = setting.openExportedFileLocation ?? globalSetting.openExportedFileLocation;
|
||||||
|
const openExportedFile = setting.openExportedFile ?? globalSetting.openExportedFile;
|
||||||
|
|
||||||
|
if (showOverwriteConfirmation && fs.existsSync(outputPath)) {
|
||||||
|
// const msgBox = new MessageBox(this.app, {
|
||||||
|
// message: lang.overwriteConfirmationDialog.message(outputDir),
|
||||||
|
// title: lang.overwriteConfirmationDialog.title(outputFileFullName),
|
||||||
|
// buttons: 'OkCancel',
|
||||||
|
// buttonsLabel: {
|
||||||
|
// ok: lang.overwriteConfirmationDialog.replace,
|
||||||
|
// },
|
||||||
|
// buttonsClass: {
|
||||||
|
// ok: 'mod-warning'
|
||||||
|
// },
|
||||||
|
// callback: {
|
||||||
|
// ok: () => doExport()
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// msgBox.open();
|
||||||
|
|
||||||
|
const result = await ct.remote.dialog.showSaveDialog({
|
||||||
|
title: lang.overwriteConfirmationDialog.title(outputFileFullName),
|
||||||
|
defaultPath: outputPath,
|
||||||
|
properties: ['showOverwriteConfirmation', 'createDirectory'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.canceled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
variables.outputPath = result.filePath;
|
||||||
|
variables.outputDir = path.dirname(variables.outputPath);
|
||||||
|
variables.outputFileFullName = path.basename(variables.outputPath);
|
||||||
|
variables.outputFileName = path.basename(variables.outputFileFullName, path.extname(variables.outputFileFullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// show progress
|
||||||
|
let progressBarHide: (() => void) | undefined = undefined;
|
||||||
|
if (showExportProgressBar) {
|
||||||
|
beforeExport?.();
|
||||||
|
progressBarHide = ProgressBar.show(lang.preparing(variables.outputFileFullName));
|
||||||
|
}
|
||||||
|
|
||||||
|
// process Environment variables..
|
||||||
|
const env = (variables.env = createEnv(getPlatformValue(globalSetting.env) ?? {}, variables));
|
||||||
|
|
||||||
|
let pandocPath = pandoc.normalizePath(getPlatformValue(globalSetting.pandocPath));
|
||||||
|
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
// https://github.com/mokeyish/obsidian-enhancing-export/issues/153
|
||||||
|
pandocPath = pandocPath.replaceAll('\\', '/');
|
||||||
|
const pathKeys: Array<keyof Variables> = [
|
||||||
|
'pluginDir',
|
||||||
|
'luaDir',
|
||||||
|
'outputDir',
|
||||||
|
'outputPath',
|
||||||
|
'currentDir',
|
||||||
|
'currentPath',
|
||||||
|
'attachmentFolderPath',
|
||||||
|
'vaultDir',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const pathKey of pathKeys) {
|
||||||
|
const path = variables[pathKey] as string;
|
||||||
|
variables[pathKey] = path.replaceAll('\\', '/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmdTpl =
|
||||||
|
setting.type === 'pandoc'
|
||||||
|
? `${pandocPath} "\${currentPath}" ${setting.arguments ?? ''} ${setting.customArguments ?? ''}`
|
||||||
|
: setting.command;
|
||||||
|
|
||||||
|
const cmd = renderTemplate(cmdTpl, variables);
|
||||||
|
const args = argsParser(cmd.match(/(?:[^\s"]+|"[^"]*")+/g), {
|
||||||
|
alias: {
|
||||||
|
output: ['o'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const actualOutputPath = path.normalize(trimQuotes(args.output));
|
||||||
|
|
||||||
|
const actualOutputDir = path.dirname(actualOutputPath);
|
||||||
|
if (!fs.existsSync(actualOutputDir)) {
|
||||||
|
fs.mkdirSync(actualOutputDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`[${plugin.manifest.name}]: export command and options:`, {
|
||||||
|
cmd,
|
||||||
|
options: { cwd: variables.currentDir, env },
|
||||||
|
});
|
||||||
|
await exec(cmd, { cwd: variables.currentDir, env });
|
||||||
|
progressBarHide?.();
|
||||||
|
|
||||||
|
const next = async () => {
|
||||||
|
if (openExportedFileLocation) {
|
||||||
|
setTimeout(() => {
|
||||||
|
ct.remote.shell.showItemInFolder(actualOutputPath);
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
if (openExportedFile) {
|
||||||
|
await ct.remote.shell.openPath(actualOutputPath);
|
||||||
|
}
|
||||||
|
if (setting.type === 'pandoc' && setting.runCommand === true && setting.command) {
|
||||||
|
const extCmd = renderTemplate(setting.command, variables);
|
||||||
|
await exec(extCmd, { cwd: variables.currentDir, env });
|
||||||
|
}
|
||||||
|
// success
|
||||||
|
onSuccess && onSuccess();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (showCommandLineOutput) {
|
||||||
|
const box = new MessageBox(app, lang.exportCommandOutputMessage(cmd));
|
||||||
|
box.onClose = next;
|
||||||
|
box.open();
|
||||||
|
} else {
|
||||||
|
new Notice(lang.exportSuccessNotice(variables.outputFileFullName), 1500);
|
||||||
|
await next();
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
progressBarHide?.();
|
||||||
|
new MessageBox(app, lang.exportErrorOutputMessage(cmd, err)).open();
|
||||||
|
onFailure && onFailure();
|
||||||
|
}
|
||||||
|
}
|
79
.obsidian/plugins/obsidian-enhancing-export-main/src/hmr.ts
vendored
Normal file
79
.obsidian/plugins/obsidian-enhancing-export-main/src/hmr.ts
vendored
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import type { Plugin } from 'obsidian';
|
||||||
|
import { debounce, Platform } from 'obsidian';
|
||||||
|
import { normalize, join } from 'path';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HmrOptions {
|
||||||
|
watchFiles?: Array<'main.js' | 'manifest.json' | 'styles.css'> | string[];
|
||||||
|
}
|
||||||
|
interface Window {
|
||||||
|
hmr(plugin: Plugin, options?: HmrOptions): void;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Window.prototype.hmr = function (plugin: Plugin, options?: HmrOptions): void {
|
||||||
|
if (Platform.isMobile) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[hmr: ${plugin.manifest.name}]`, new Date());
|
||||||
|
|
||||||
|
options ??= {};
|
||||||
|
options.watchFiles ??= ['main.js', 'manifest.json', 'styles.css'];
|
||||||
|
const { watchFiles } = options;
|
||||||
|
|
||||||
|
const {
|
||||||
|
app: {
|
||||||
|
vault: { adapter },
|
||||||
|
plugins,
|
||||||
|
},
|
||||||
|
manifest: { dir: pluginDir, id },
|
||||||
|
} = plugin;
|
||||||
|
const {
|
||||||
|
app: { vault },
|
||||||
|
} = plugin;
|
||||||
|
|
||||||
|
const restartPlugin = async () => {
|
||||||
|
const dbgKey = 'debug-plugin';
|
||||||
|
const oldDebug = localStorage.getItem(dbgKey);
|
||||||
|
try {
|
||||||
|
localStorage.setItem(dbgKey, '1');
|
||||||
|
await plugins.disablePlugin(id);
|
||||||
|
await plugins.enablePlugin(id);
|
||||||
|
} finally {
|
||||||
|
if (oldDebug) {
|
||||||
|
localStorage.setItem(dbgKey, oldDebug);
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(dbgKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const entry = normalize(join(pluginDir, 'main.js'));
|
||||||
|
const onChange = debounce(
|
||||||
|
async (file: string) => {
|
||||||
|
if (file.startsWith(pluginDir)) {
|
||||||
|
if (!(await adapter.exists(entry))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (file === pluginDir) {
|
||||||
|
// reload
|
||||||
|
} else if (watchFiles?.length > 0) {
|
||||||
|
if (!watchFiles.some(o => file.endsWith(o))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await restartPlugin();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
500,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
plugin.registerEvent(vault.on('raw', onChange));
|
||||||
|
|
||||||
|
plugin.register(() => adapter.stopWatchPath(pluginDir));
|
||||||
|
adapter.startWatchPath(pluginDir);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {};
|
71
.obsidian/plugins/obsidian-enhancing-export-main/src/lang/de-DE.ts
vendored
Normal file
71
.obsidian/plugins/obsidian-enhancing-export-main/src/lang/de-DE.ts
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { strTpl } from '../utils';
|
||||||
|
import type { Lang } from '.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
exportToOo: 'Export to...',
|
||||||
|
exportSuccessNotice: strTpl`Export der Datei ${0} erfolgreich!`,
|
||||||
|
exportCommandOutputMessage: strTpl`Command: ${0}`,
|
||||||
|
exportErrorOutputMessage: strTpl`Command: ${0},Fehler:${1}`,
|
||||||
|
exportWithPrevious: 'Exportiere mit Vorherigem',
|
||||||
|
pleaseOpenFile: 'Bitte öffne zunächst eine Datei.',
|
||||||
|
preparing: strTpl`generating "${0}"...`,
|
||||||
|
exportDialog: {
|
||||||
|
exportTo: 'Exportiere nach',
|
||||||
|
fileName: 'Dateiname',
|
||||||
|
title: strTpl`Export to ${0}`,
|
||||||
|
export: 'Export',
|
||||||
|
selectExportFolder: 'Zielordner auswählen',
|
||||||
|
overwriteConfirmation: 'Überschreibe den Zielordner',
|
||||||
|
type: 'Typ',
|
||||||
|
},
|
||||||
|
messageBox: {
|
||||||
|
yes: 'Ja',
|
||||||
|
no: 'Nein',
|
||||||
|
ok: 'Ok',
|
||||||
|
cancel: 'Abbrechen',
|
||||||
|
},
|
||||||
|
overwriteConfirmationDialog: {
|
||||||
|
replace: 'Ersetze',
|
||||||
|
title: strTpl`"${0}" existiert bereits. Soll er ersetzt werden?`,
|
||||||
|
message: strTpl`Eine Datei oder ein Ordner mit dem gleichen Namen existiert bereits im Ordner "${0}". Das Ersetzen wird die jetzigen Inhalte überschreiben.`,
|
||||||
|
},
|
||||||
|
settingTab: {
|
||||||
|
general: 'Allgemein',
|
||||||
|
name: 'Name',
|
||||||
|
title: 'Export-Einstellungen',
|
||||||
|
pandocVersion: strTpl`Version: ${0}`,
|
||||||
|
pandocVersionWithWarning: strTpl`Version: ${0}, please upgrade version to ${1}`,
|
||||||
|
pandocNotFound:
|
||||||
|
'Pandoc.exe wurde nicht gefunden. Bitte geben Sie den Pfad zur Pandoc.exe ein oder fügen Sie ihn den Window Systemumgebungsvariablen hinzu.',
|
||||||
|
defaultFolderForExportedFile: 'Standardordner für exportierte Dateien',
|
||||||
|
openExportedFileLocation: 'Speicherort der exportierten Datei öffnen',
|
||||||
|
openExportedFile: 'Exportierte Datei öffnen',
|
||||||
|
pandocPath: 'Pfad zur Datei Pandoc.exe',
|
||||||
|
pandocPathPlaceholder: '(Automatische Erkennung)',
|
||||||
|
editCommandTemplate: '‘Befehlsvorlage bearbeiten',
|
||||||
|
chooseCommandTemplate: 'Vorlage auswählen',
|
||||||
|
customLocation: 'Benutzerdefinierter Speicherort',
|
||||||
|
template: 'Vorlage',
|
||||||
|
command: 'Befehl',
|
||||||
|
reset: 'Zurücksetzen',
|
||||||
|
auto: 'Auto',
|
||||||
|
add: 'Hinzufügen',
|
||||||
|
remove: 'Entfernen',
|
||||||
|
rename: 'Umbenennen',
|
||||||
|
sameFolderWithCurrentFile: 'Der gleiche Ordner mit der aktuellen Datei',
|
||||||
|
afterExport: 'Nach dem Export',
|
||||||
|
targetFileExtensions: 'Dateinamenserweiterung der Zieldatei',
|
||||||
|
targetFileExtensionsTip: '(Mit Leerzeichen getrennt)',
|
||||||
|
showCommandOutput: 'Zeige die Ausgabe des Befehls',
|
||||||
|
runCommand: 'Starte den Befehl',
|
||||||
|
extraArguments: 'Zusätzliche Parameter',
|
||||||
|
save: 'Speichern',
|
||||||
|
new: 'Neu',
|
||||||
|
arguments: 'Parameter',
|
||||||
|
|
||||||
|
advanced: 'Advanced',
|
||||||
|
environmentVariables: 'Environment Variables',
|
||||||
|
environmentVariablesDesc: 'Define the Environment Variables for exporting.',
|
||||||
|
ShowExportProgressBar: 'Show export progressBar',
|
||||||
|
},
|
||||||
|
} satisfies Lang;
|
69
.obsidian/plugins/obsidian-enhancing-export-main/src/lang/en-US.ts
vendored
Normal file
69
.obsidian/plugins/obsidian-enhancing-export-main/src/lang/en-US.ts
vendored
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { strTpl } from '../utils';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
exportToOo: 'Export to...',
|
||||||
|
exportSuccessNotice: strTpl`Export file ${0} success!`,
|
||||||
|
exportCommandOutputMessage: strTpl`Command: ${0}`,
|
||||||
|
exportErrorOutputMessage: strTpl`Command: ${0},Error:${1}`,
|
||||||
|
exportWithPrevious: 'Export with Previous',
|
||||||
|
pleaseOpenFile: 'Please open a file first.',
|
||||||
|
preparing: strTpl`generating "${0}"...`,
|
||||||
|
exportDialog: {
|
||||||
|
exportTo: 'Export to',
|
||||||
|
fileName: 'File Name',
|
||||||
|
title: strTpl`Export to ${0}`,
|
||||||
|
export: 'Export',
|
||||||
|
selectExportFolder: 'Please select an export folder.',
|
||||||
|
overwriteConfirmation: 'Overwrite confirmation',
|
||||||
|
type: 'Type',
|
||||||
|
},
|
||||||
|
messageBox: {
|
||||||
|
yes: 'Yes',
|
||||||
|
no: 'No',
|
||||||
|
ok: 'Ok',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
},
|
||||||
|
overwriteConfirmationDialog: {
|
||||||
|
replace: 'Replace',
|
||||||
|
title: strTpl`"${0}" already exists. Do you want to replace it?`,
|
||||||
|
message: strTpl`A file or folder with the same name already exists in the folder "${0}". Replacing it will overwrite its current contents.`,
|
||||||
|
},
|
||||||
|
settingTab: {
|
||||||
|
general: 'General',
|
||||||
|
name: 'Name',
|
||||||
|
title: 'Export Settings',
|
||||||
|
pandocVersion: strTpl`Version: ${0}`,
|
||||||
|
pandocVersionWithWarning: strTpl`Version: ${0}, please upgrade version to ${1}`,
|
||||||
|
pandocNotFound: 'Pandoc not found, please fill in the Pandoc file path, or add it to the system environment variables.',
|
||||||
|
defaultFolderForExportedFile: 'Default Folder for Exported File',
|
||||||
|
openExportedFileLocation: 'Open exported file location',
|
||||||
|
ShowExportProgressBar: 'Show export progress bar',
|
||||||
|
openExportedFile: 'Open exported file',
|
||||||
|
pandocPath: 'Pandoc path',
|
||||||
|
pandocPathPlaceholder: '(Auto Detect)',
|
||||||
|
editCommandTemplate: 'Edit Command Template',
|
||||||
|
chooseCommandTemplate: 'Choose template',
|
||||||
|
customLocation: 'Custom location',
|
||||||
|
template: 'Template',
|
||||||
|
command: 'Command',
|
||||||
|
reset: 'Reset',
|
||||||
|
auto: 'Auto',
|
||||||
|
add: 'Add',
|
||||||
|
remove: 'Remove',
|
||||||
|
rename: 'Rename',
|
||||||
|
sameFolderWithCurrentFile: 'Same folder with current file',
|
||||||
|
afterExport: 'After Export',
|
||||||
|
targetFileExtensions: 'Target file extensions',
|
||||||
|
targetFileExtensionsTip: '(Separated by whitespace)',
|
||||||
|
showCommandOutput: 'Show command output',
|
||||||
|
runCommand: 'Run command',
|
||||||
|
extraArguments: 'Extra arguments',
|
||||||
|
save: 'Save',
|
||||||
|
new: 'New',
|
||||||
|
arguments: 'Arguments',
|
||||||
|
|
||||||
|
advanced: 'Advanced',
|
||||||
|
environmentVariables: 'Environment Variables',
|
||||||
|
environmentVariablesDesc: 'Define the Environment Variables for exporting.',
|
||||||
|
},
|
||||||
|
};
|
27
.obsidian/plugins/obsidian-enhancing-export-main/src/lang/index.ts
vendored
Normal file
27
.obsidian/plugins/obsidian-enhancing-export-main/src/lang/index.ts
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import zhCN from './zh-CN';
|
||||||
|
import enUS from './en-US';
|
||||||
|
import deDE from './de-DE';
|
||||||
|
import { moment } from 'obsidian';
|
||||||
|
|
||||||
|
export type Lang = typeof enUS;
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'de-DE': deDE,
|
||||||
|
'en-US': enUS,
|
||||||
|
'zh-CN': zhCN,
|
||||||
|
get current() {
|
||||||
|
const langIds = Object.keys(this);
|
||||||
|
const locale = moment.locale().toLowerCase();
|
||||||
|
let langId = langIds.find(id => id.toLowerCase() === locale.toLowerCase());
|
||||||
|
if (langId) {
|
||||||
|
return this[langId];
|
||||||
|
}
|
||||||
|
|
||||||
|
const localePrefix = locale.split('-')[0];
|
||||||
|
langId = langIds.find(id => id.toLowerCase().startsWith(localePrefix));
|
||||||
|
if (langId) {
|
||||||
|
return this[langId];
|
||||||
|
}
|
||||||
|
return this['en-US'];
|
||||||
|
},
|
||||||
|
};
|
70
.obsidian/plugins/obsidian-enhancing-export-main/src/lang/zh-CN.ts
vendored
Normal file
70
.obsidian/plugins/obsidian-enhancing-export-main/src/lang/zh-CN.ts
vendored
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { strTpl } from '../utils';
|
||||||
|
import type { Lang } from '.';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
exportToOo: '导出为......',
|
||||||
|
exportWithPrevious: '使用上一次设置导出',
|
||||||
|
exportSuccessNotice: strTpl`导出文件 ${0} 成功!`,
|
||||||
|
exportCommandOutputMessage: strTpl`命令:${0}`,
|
||||||
|
exportErrorOutputMessage: strTpl`命令 ${0},错误:${1}`,
|
||||||
|
pleaseOpenFile: '请打开一个文件先。',
|
||||||
|
preparing: strTpl`正在生成 "${0}" ......`,
|
||||||
|
exportDialog: {
|
||||||
|
fileName: '文件名',
|
||||||
|
type: '类型',
|
||||||
|
exportTo: '导出到',
|
||||||
|
title: strTpl`导出为 ${0}`,
|
||||||
|
export: '导出',
|
||||||
|
selectExportFolder: '请选择导出文件夹',
|
||||||
|
overwriteConfirmation: '覆盖提示',
|
||||||
|
},
|
||||||
|
messageBox: {
|
||||||
|
yes: '是',
|
||||||
|
no: '否',
|
||||||
|
ok: '确认',
|
||||||
|
cancel: '取消',
|
||||||
|
},
|
||||||
|
overwriteConfirmationDialog: {
|
||||||
|
replace: '替换',
|
||||||
|
title: strTpl`"${0}" 已经存在。您要替换它吗?`,
|
||||||
|
message: strTpl`"${0}" 文件夹中已有相同的文件或文件夹,若替换,则会覆盖其当前内容。`,
|
||||||
|
},
|
||||||
|
settingTab: {
|
||||||
|
title: '导出设置',
|
||||||
|
general: '通用',
|
||||||
|
name: '名称',
|
||||||
|
customLocation: '自定义',
|
||||||
|
pandocVersion: strTpl`版本: ${0}`,
|
||||||
|
pandocVersionWithWarning: strTpl`Version: ${0}, 请升级版本到 ${1}`,
|
||||||
|
pandocNotFound: '找不到 Pandoc,请填写 Pandoc 文件路径,或者将其添加到系统环境变量中。',
|
||||||
|
pandocPath: 'Pandoc 路径',
|
||||||
|
defaultFolderForExportedFile: '默认的导出文件夹',
|
||||||
|
openExportedFileLocation: '打开导出文件所在目录',
|
||||||
|
sameFolderWithCurrentFile: '与原文件同一目录下',
|
||||||
|
openExportedFile: '打开导出文件',
|
||||||
|
pandocPathPlaceholder: '(自动检测)',
|
||||||
|
editCommandTemplate: '编辑命令模板',
|
||||||
|
chooseCommandTemplate: '选择模板',
|
||||||
|
afterExport: '导出后',
|
||||||
|
command: '命令',
|
||||||
|
arguments: '参数',
|
||||||
|
auto: '自动',
|
||||||
|
reset: '重置',
|
||||||
|
add: '添加',
|
||||||
|
remove: '移除',
|
||||||
|
rename: '重命名',
|
||||||
|
targetFileExtensions: '目标文件扩展名',
|
||||||
|
targetFileExtensionsTip: '(用空格分开)',
|
||||||
|
showCommandOutput: '显示命令行输出',
|
||||||
|
runCommand: '运行自定义命令',
|
||||||
|
extraArguments: '自定义参数',
|
||||||
|
save: '保存',
|
||||||
|
new: '新建',
|
||||||
|
template: '模板',
|
||||||
|
|
||||||
|
advanced: '高级',
|
||||||
|
environmentVariables: '环境变量',
|
||||||
|
environmentVariablesDesc: '定义导出的环境变量.',
|
||||||
|
ShowExportProgressBar: '显示导出进度条',
|
||||||
|
},
|
||||||
|
} satisfies Lang;
|
153
.obsidian/plugins/obsidian-enhancing-export-main/src/main.ts
vendored
Normal file
153
.obsidian/plugins/obsidian-enhancing-export-main/src/main.ts
vendored
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import { App, Menu, Plugin, PluginManifest, TFile, Notice, debounce } from 'obsidian';
|
||||||
|
import { UniversalExportPluginSettings, ExportSetting, DEFAULT_SETTINGS, DEFAULT_ENV } from './settings';
|
||||||
|
// import { ExportSettingTab, ExportDialog } from './ui/legacy';
|
||||||
|
import { ExportSettingTab, ExportDialog } from './ui';
|
||||||
|
import { exportToOo } from './exporto0o';
|
||||||
|
import { getPlatformValue, PlatformKey } from './utils';
|
||||||
|
import lang, { Lang } from './lang';
|
||||||
|
import path from 'path';
|
||||||
|
import resources from './resources';
|
||||||
|
import './styles.css';
|
||||||
|
|
||||||
|
export default class UniversalExportPlugin extends Plugin {
|
||||||
|
settings: UniversalExportPluginSettings;
|
||||||
|
lang: Lang;
|
||||||
|
|
||||||
|
constructor(app: App, manifest: PluginManifest) {
|
||||||
|
super(app, manifest);
|
||||||
|
this.lang = lang.current;
|
||||||
|
this.saveSettings = debounce(this.saveSettings.bind(this), 1000, true) as unknown as typeof this.saveSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
async onload() {
|
||||||
|
await this.releaseResources();
|
||||||
|
|
||||||
|
await this.loadSettings();
|
||||||
|
const { lang } = this;
|
||||||
|
|
||||||
|
this.addSettingTab(new ExportSettingTab(this));
|
||||||
|
|
||||||
|
this.addCommand({
|
||||||
|
id: 'obsidian-enhancing-export:export',
|
||||||
|
name: lang.exportToOo,
|
||||||
|
icon: 'document',
|
||||||
|
callback: () => {
|
||||||
|
const file = this.app.workspace.getActiveFile();
|
||||||
|
if (file) {
|
||||||
|
ExportDialog.show(this, file);
|
||||||
|
} else {
|
||||||
|
new Notice(lang.pleaseOpenFile, 2000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.addCommand({
|
||||||
|
id: 'obsidian-enhancing-export:export-with-previous',
|
||||||
|
name: lang.exportWithPrevious,
|
||||||
|
icon: 'document',
|
||||||
|
callback: async () => {
|
||||||
|
const file = this.app.workspace.getActiveFile();
|
||||||
|
if (file) {
|
||||||
|
if (this.settings.lastExportType && this.settings.lastExportDirectory) {
|
||||||
|
const setting = this.settings.items.find(s => s.name === this.settings.lastExportType);
|
||||||
|
if (setting) {
|
||||||
|
await exportToOo(this, file, getPlatformValue(this.settings.lastExportDirectory), undefined, setting);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExportDialog.show(this, file);
|
||||||
|
} else {
|
||||||
|
new Notice(lang.pleaseOpenFile, 2000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.registerEvent(
|
||||||
|
this.app.workspace.on('file-menu', (menu: Menu, file) => {
|
||||||
|
if (file instanceof TFile) {
|
||||||
|
menu
|
||||||
|
.addItem(item => {
|
||||||
|
item
|
||||||
|
.setTitle(lang.exportToOo)
|
||||||
|
.setIcon('document')
|
||||||
|
.onClick((): void => {
|
||||||
|
ExportDialog.show(this, file);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.addSeparator();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// this.downloadLuaScripts().then();
|
||||||
|
if (import.meta.env.DEV) {
|
||||||
|
window.hmr && window.hmr(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async resetSettings(): Promise<void> {
|
||||||
|
this.settings = {
|
||||||
|
...JSON.parse(JSON.stringify(DEFAULT_SETTINGS)),
|
||||||
|
lastExportDirectory: this.settings.lastExportDirectory,
|
||||||
|
};
|
||||||
|
await this.saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async loadSettings(): Promise<void> {
|
||||||
|
const settings: UniversalExportPluginSettings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||||
|
settings.items.forEach(v => {
|
||||||
|
Object.assign(v, Object.assign({}, DEFAULT_SETTINGS.items.find(o => o.name === v.name) ?? {}, v));
|
||||||
|
});
|
||||||
|
for (const item of DEFAULT_SETTINGS.items) {
|
||||||
|
if (settings.items.every(o => o.name !== item.name)) {
|
||||||
|
settings.items.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async saveSettings(): Promise<void> {
|
||||||
|
console.log('[obsidian-enhancing-export] saveSettings', this.settings);
|
||||||
|
const settings: UniversalExportPluginSettings = JSON.parse(JSON.stringify(this.settings));
|
||||||
|
settings.items.forEach(v => {
|
||||||
|
const def = DEFAULT_SETTINGS.items.find(o => o.name === v.name);
|
||||||
|
if (def) {
|
||||||
|
Object.keys(v).forEach((k: keyof ExportSetting) => {
|
||||||
|
if (k !== 'name' && JSON.stringify(v[k]) === JSON.stringify(def[k])) {
|
||||||
|
delete v[k];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (settings.env) {
|
||||||
|
for (const platform of Object.keys(settings.env) as PlatformKey[]) {
|
||||||
|
const env = settings.env[platform];
|
||||||
|
if (JSON.stringify(env) === JSON.stringify(DEFAULT_ENV[platform])) {
|
||||||
|
delete settings.env[platform];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const refEnv = getPlatformValue(DEFAULT_ENV, platform);
|
||||||
|
for (const [name, value] of Object.entries(env)) {
|
||||||
|
if (value === refEnv[name]) {
|
||||||
|
delete env[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(env).length === 0) {
|
||||||
|
delete settings.env[platform];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.saveData(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
async releaseResources(): Promise<void> {
|
||||||
|
const { adapter } = this.app.vault;
|
||||||
|
for (const [dir, res] of resources) {
|
||||||
|
const resDir = path.join(this.manifest.dir, dir);
|
||||||
|
await adapter.mkdir(resDir);
|
||||||
|
for (const [fileName, bytes] of res) {
|
||||||
|
const filePath = path.join(resDir, fileName);
|
||||||
|
await adapter.writeBinary(filePath, bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resources.length = 0;
|
||||||
|
}
|
||||||
|
}
|
28
.obsidian/plugins/obsidian-enhancing-export-main/src/pandoc.ts
vendored
Normal file
28
.obsidian/plugins/obsidian-enhancing-export-main/src/pandoc.ts
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { exec } from './utils';
|
||||||
|
import semver from 'semver/preload';
|
||||||
|
|
||||||
|
export const normalizePandocPath = (path?: string) => (path?.includes(' ') ? `"${path}"` : `${path ?? 'pandoc'}`);
|
||||||
|
|
||||||
|
export async function getPandocVersion(path?: string, env?: Record<string, string>) {
|
||||||
|
path = normalizePandocPath(path);
|
||||||
|
let version = await exec(`${path} --version`, { env });
|
||||||
|
version = version.substring(0, version.indexOf('\n')).replace('pandoc.exe', '').replace('pandoc', '').trim();
|
||||||
|
let dotCount = [...version].filter(c => c === '.').length;
|
||||||
|
if (dotCount === 1) {
|
||||||
|
version = `${version}.0`;
|
||||||
|
} else {
|
||||||
|
while (dotCount > 2) {
|
||||||
|
version = version.substring(0, version.lastIndexOf('.'));
|
||||||
|
dotCount -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return semver.parse(version, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PANDOC_REQUIRED_VERSION = '3.1.7';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
normalizePath: normalizePandocPath,
|
||||||
|
getVersion: getPandocVersion,
|
||||||
|
requiredVersion: PANDOC_REQUIRED_VERSION,
|
||||||
|
};
|
9
.obsidian/plugins/obsidian-enhancing-export-main/src/resources.ts
vendored
Normal file
9
.obsidian/plugins/obsidian-enhancing-export-main/src/resources.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const embed = (dir: string, res: Record<string, { default: Uint8Array }>) =>
|
||||||
|
[dir, Object.entries(res).map(([k, m]) => [k.substring(dir.length + 3), m.default] as const)] as const;
|
||||||
|
|
||||||
|
// The embedded resource
|
||||||
|
export default [
|
||||||
|
// For other file types, the Loader must be configured in the <root>/vite.config.ts.
|
||||||
|
embed('lua', import.meta.glob<{ default: Uint8Array }>('../lua/*.lua', { eager: true })),
|
||||||
|
embed('textemplate', import.meta.glob<{ default: Uint8Array }>('../textemplate/*.{tex,sty}', { eager: true })),
|
||||||
|
];
|
172
.obsidian/plugins/obsidian-enhancing-export-main/src/settings.ts
vendored
Normal file
172
.obsidian/plugins/obsidian-enhancing-export-main/src/settings.ts
vendored
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import export_templates from './export_templates';
|
||||||
|
import { setPlatformValue, PlatformValue, renderTemplate, getPlatformValue } from './utils';
|
||||||
|
import type { PropertyGridMeta } from './ui/components/PropertyGrid';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Variables
|
||||||
|
* /User/aaa/Documents/test.pdf
|
||||||
|
* - ${outputDir} --> /User/aaa/Documents/
|
||||||
|
* - ${outputPath} --> /User/aaa/Documents/test.pdf
|
||||||
|
* - ${outputFileName} --> test
|
||||||
|
* - ${outputFileFullName} --> test.pdf
|
||||||
|
*
|
||||||
|
* /User/aaa/Documents/test.pdf
|
||||||
|
* - ${currentDir} --> /User/aaa/Documents/
|
||||||
|
* - ${currentPath} --> /User/aaa/Documents/test.pdf
|
||||||
|
* - ${CurrentFileName} --> test
|
||||||
|
* - ${CurrentFileFullName} --> test.pdf
|
||||||
|
*/
|
||||||
|
export interface Variables extends Record<string, unknown> {
|
||||||
|
attachmentFolderPath: string;
|
||||||
|
pluginDir: string;
|
||||||
|
luaDir: string;
|
||||||
|
outputDir: string;
|
||||||
|
outputPath: string;
|
||||||
|
outputFileName: string;
|
||||||
|
outputFileFullName: string;
|
||||||
|
currentDir: string;
|
||||||
|
currentPath: string;
|
||||||
|
currentFileName: string;
|
||||||
|
currentFileFullName: string;
|
||||||
|
vaultDir: string;
|
||||||
|
// date: new Date(currentFile.stat.ctime),
|
||||||
|
// lastMod: new Date(currentFile.stat.mtime),
|
||||||
|
// now: new Date()
|
||||||
|
metadata?: unknown;
|
||||||
|
options?: unknown;
|
||||||
|
env?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UniversalExportPluginSettings {
|
||||||
|
version?: string;
|
||||||
|
pandocPath?: PlatformValue<string>;
|
||||||
|
showOverwriteConfirmation?: boolean;
|
||||||
|
defaultExportDirectoryMode: 'Auto' | 'Same' | 'Custom';
|
||||||
|
customDefaultExportDirectory?: PlatformValue<string>;
|
||||||
|
env: PlatformValue<Record<string, string>>;
|
||||||
|
items: ExportSetting[];
|
||||||
|
|
||||||
|
openExportedFile?: boolean; // open exported file after export
|
||||||
|
openExportedFileLocation?: boolean; // open exported file location after export
|
||||||
|
|
||||||
|
lastEditName?: string;
|
||||||
|
|
||||||
|
lastExportDirectory?: PlatformValue<string>;
|
||||||
|
lastExportType?: string;
|
||||||
|
|
||||||
|
showExportProgressBar?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type OptionsMeta = {
|
||||||
|
[optionsName: string]: PropertyGridMeta[string] | `preset:${keyof typeof PRESET_OPTIONS_META}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface CommonExportSetting {
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
openExportedFileLocation?: boolean; // open exported file location after export
|
||||||
|
openExportedFile?: boolean; // open exported file after export
|
||||||
|
optionsMeta?: OptionsMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PandocExportSetting extends CommonExportSetting {
|
||||||
|
type: 'pandoc';
|
||||||
|
arguments: string;
|
||||||
|
customArguments?: string;
|
||||||
|
extension: string;
|
||||||
|
|
||||||
|
runCommand?: boolean; // run command after export
|
||||||
|
command?: string; // command to run after export
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CustomExportSetting extends CommonExportSetting {
|
||||||
|
type: 'custom';
|
||||||
|
command: string;
|
||||||
|
targetFileExtensions?: string;
|
||||||
|
|
||||||
|
showCommandOutput?: boolean; // show command output in console after export
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ExportSetting = PandocExportSetting | CustomExportSetting;
|
||||||
|
|
||||||
|
export const PRESET_OPTIONS_META: PropertyGridMeta = {
|
||||||
|
'textemplate': {
|
||||||
|
title: 'Latex Template',
|
||||||
|
type: 'dropdown',
|
||||||
|
options: [
|
||||||
|
{ name: 'None', value: null },
|
||||||
|
{ name: 'Dissertation', value: 'dissertation.tex' },
|
||||||
|
{ name: 'Academic Paper', value: 'neurips.tex' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DEFAULT_ENV = (() => {
|
||||||
|
let env: PlatformValue<Record<string, string>> = {};
|
||||||
|
env = setPlatformValue(
|
||||||
|
env,
|
||||||
|
{
|
||||||
|
'HOME': '${HOME}',
|
||||||
|
'PATH': '${PATH}',
|
||||||
|
'TEXINPUTS': '${pluginDir}/textemplate/:', // It is necessary to **append** to the current TEXINPUTS wtih ":" - NOT REPLACE. TEXINPUTS contains the basic latex classes.
|
||||||
|
},
|
||||||
|
'*' // available for all platforms.
|
||||||
|
);
|
||||||
|
|
||||||
|
env = setPlatformValue(
|
||||||
|
env,
|
||||||
|
{
|
||||||
|
'TEXINPUTS': '${pluginDir}/textemplate/;', // Windows uses ; rather than : for appending
|
||||||
|
'PATH': '${HOME}\\AppData\\Local\\Pandoc;${PATH}',
|
||||||
|
},
|
||||||
|
'win32' // available for windows only.
|
||||||
|
);
|
||||||
|
|
||||||
|
env = setPlatformValue(
|
||||||
|
env,
|
||||||
|
{
|
||||||
|
'PATH': '/opt/homebrew/bin:/usr/local/bin:/Library/TeX/texbin:${PATH}', // Add HomebrewBin and TexBin. see: https://docs.brew.sh/Installation
|
||||||
|
},
|
||||||
|
'darwin' // for MacOS only.
|
||||||
|
);
|
||||||
|
|
||||||
|
return env;
|
||||||
|
})();
|
||||||
|
|
||||||
|
export const DEFAULT_SETTINGS: UniversalExportPluginSettings = {
|
||||||
|
items: Object.values(export_templates).filter(o => o.type !== 'custom'),
|
||||||
|
pandocPath: undefined,
|
||||||
|
defaultExportDirectoryMode: 'Auto',
|
||||||
|
openExportedFile: true,
|
||||||
|
env: DEFAULT_ENV,
|
||||||
|
showExportProgressBar: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function extractDefaultExtension(s: ExportSetting): string {
|
||||||
|
if (s.type === 'pandoc') {
|
||||||
|
return s.extension;
|
||||||
|
} else if (s.type === 'custom') {
|
||||||
|
return s.targetFileExtensions?.split(',')[0];
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createEnv(env: Record<string, string>, envVars?: Record<string, unknown>) {
|
||||||
|
env = Object.assign({}, getPlatformValue(DEFAULT_ENV), env);
|
||||||
|
envVars = Object.assign({ HOME: process.env['HOME'] ?? process.env['USERPROFILE'] }, process.env, envVars ?? {});
|
||||||
|
return Object.fromEntries(Object.entries(env).map(([n, v]) => [n, renderTemplate(v, envVars)]));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function finalizeOptionsMeta(meta?: OptionsMeta): PropertyGridMeta {
|
||||||
|
if (meta) {
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(meta).map(([optionsName, optionsMetaOrPresetName]) => [
|
||||||
|
optionsName,
|
||||||
|
typeof optionsMetaOrPresetName === 'string'
|
||||||
|
? PRESET_OPTIONS_META[optionsMetaOrPresetName.startsWith('preset:') ? optionsMetaOrPresetName.substring(7) : '']
|
||||||
|
: optionsMetaOrPresetName,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
8
.obsidian/plugins/obsidian-enhancing-export-main/src/styles.css
vendored
Normal file
8
.obsidian/plugins/obsidian-enhancing-export-main/src/styles.css
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.setting-item.ex-setting-item {
|
||||||
|
border-top: unset;
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
141
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/ExportDialog.tsx
vendored
Normal file
141
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/ExportDialog.tsx
vendored
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import * as ct from 'electron';
|
||||||
|
import { TFile } from 'obsidian';
|
||||||
|
import { createSignal, createRoot, onCleanup, createMemo, untrack, createEffect, Show } from 'solid-js';
|
||||||
|
import { insert } from 'solid-js/web';
|
||||||
|
import type UniversalExportPlugin from '../main';
|
||||||
|
import { extractDefaultExtension as extractExtension, finalizeOptionsMeta } from '../settings';
|
||||||
|
import { setPlatformValue, getPlatformValue, } from '../utils';
|
||||||
|
import { exportToOo } from '../exporto0o';
|
||||||
|
import Modal from './components/Modal';
|
||||||
|
import Button from './components/Button';
|
||||||
|
import PropertyGrid, { createDefaultObject } from './components/PropertyGrid';
|
||||||
|
import Setting, {Text, DropDown, ExtraButton, Toggle} from './components/Setting';
|
||||||
|
|
||||||
|
|
||||||
|
const Dialog = (props: { plugin: UniversalExportPlugin, currentFile: TFile, onClose?: () => void }) => {
|
||||||
|
const { plugin: { app, settings: globalSetting, lang }, currentFile } = props;
|
||||||
|
|
||||||
|
const [hidden, setHidden] = createSignal(false);
|
||||||
|
const [showOverwriteConfirmation, setShowOverwriteConfirmation] = createSignal(globalSetting.showOverwriteConfirmation);
|
||||||
|
const [exportType, setExportType] = createSignal(globalSetting.lastExportType ?? globalSetting.items.first()?.name);
|
||||||
|
const [options, setOptions] = createSignal({});
|
||||||
|
const setting = createMemo(() => globalSetting.items.find(o => o.name === exportType()));
|
||||||
|
const extension = createMemo(() => extractExtension(setting()));
|
||||||
|
const title = createMemo(() => lang.exportDialog.title(setting().name));
|
||||||
|
const optionsMeta = createMemo(() => finalizeOptionsMeta(setting().optionsMeta));
|
||||||
|
|
||||||
|
const [candidateOutputDirectory, setCandidateOutputDirectory] = createSignal(`${getPlatformValue(globalSetting.lastExportDirectory) ?? ct.remote.app.getPath('documents')}`);
|
||||||
|
const [candidateOutputFileName, setCandidateOutputFileName] = createSignal(`${currentFile.basename}${extension()}`);
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
const meta = optionsMeta();
|
||||||
|
setOptions(meta ? createDefaultObject(meta) : {});
|
||||||
|
});
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
|
let fileName = untrack(candidateOutputFileName);
|
||||||
|
fileName = fileName.includes('.') ? fileName.substring(0, fileName.lastIndexOf('.')) : fileName;
|
||||||
|
setCandidateOutputFileName(`${fileName}${extension()}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
const exportTypes = globalSetting.items.map(o => ({ name: o.name, value: o.name }));
|
||||||
|
|
||||||
|
if (globalSetting.defaultExportDirectoryMode === 'Same') {
|
||||||
|
const path = currentFile.vault.adapter.getBasePath() + '/' + currentFile.parent.path;
|
||||||
|
setCandidateOutputDirectory(path);
|
||||||
|
} else if (globalSetting.defaultExportDirectoryMode === 'Custom') {
|
||||||
|
setCandidateOutputDirectory(getPlatformValue(globalSetting.customDefaultExportDirectory));
|
||||||
|
}
|
||||||
|
|
||||||
|
const chooseFolder = async () => {
|
||||||
|
const retval = await ct.remote.dialog.showOpenDialog({
|
||||||
|
title: lang.exportDialog.selectExportFolder,
|
||||||
|
defaultPath: candidateOutputDirectory(),
|
||||||
|
properties: ['createDirectory', 'openDirectory'],
|
||||||
|
});
|
||||||
|
if (!retval.canceled && retval.filePaths?.length > 0) {
|
||||||
|
setCandidateOutputDirectory(retval.filePaths[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const doExport = async () => {
|
||||||
|
const plugin = props.plugin;
|
||||||
|
setHidden(true);
|
||||||
|
await exportToOo(
|
||||||
|
plugin,
|
||||||
|
currentFile,
|
||||||
|
untrack(candidateOutputDirectory),
|
||||||
|
untrack(candidateOutputFileName),
|
||||||
|
untrack(setting),
|
||||||
|
untrack(showOverwriteConfirmation),
|
||||||
|
options(),
|
||||||
|
async () => {
|
||||||
|
globalSetting.showOverwriteConfirmation = untrack(showOverwriteConfirmation);
|
||||||
|
globalSetting.lastExportDirectory = setPlatformValue(globalSetting.lastExportDirectory, untrack(candidateOutputDirectory));
|
||||||
|
|
||||||
|
globalSetting.lastExportType = untrack(setting).name;
|
||||||
|
await plugin.saveSettings();
|
||||||
|
props.onClose && props.onClose();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
setHidden(false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<Modal app={app} title={title()} hidden={hidden()} onClose={props.onClose} >
|
||||||
|
<Setting name={lang.exportDialog.type}>
|
||||||
|
<DropDown options={exportTypes} onChange={(typ) => setExportType(typ)} selected={exportType()}/>
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<Setting name={lang.exportDialog.fileName}>
|
||||||
|
<Text
|
||||||
|
title={candidateOutputFileName()}
|
||||||
|
value={candidateOutputFileName()}
|
||||||
|
onChange={(value) => setCandidateOutputFileName(value)}
|
||||||
|
/>
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<Show when={optionsMeta()}>
|
||||||
|
<PropertyGrid meta={optionsMeta()} value={options()} onChange={ (o) => setOptions(o)}/>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Setting name={lang.exportDialog.exportTo}>
|
||||||
|
<Text title={candidateOutputDirectory()} value={candidateOutputDirectory()} disabled />
|
||||||
|
<ExtraButton icon='folder' onClick={chooseFolder} />
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
|
||||||
|
<Setting name={lang.exportDialog.overwriteConfirmation} class="mod-toggle">
|
||||||
|
<Toggle checked={showOverwriteConfirmation()} onChange={setShowOverwriteConfirmation} />
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<div class="modal-button-container">
|
||||||
|
<Button cta={true} onClick={doExport}>{lang.exportDialog.export}</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const show = (plugin: UniversalExportPlugin, currentFile: TFile) => createRoot(dispose => {
|
||||||
|
let disposed = false;
|
||||||
|
const cleanup = () => {
|
||||||
|
if (disposed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
disposed = true;
|
||||||
|
dispose();
|
||||||
|
};
|
||||||
|
const el = insert(document.body, () => <Dialog onClose={cleanup} plugin={plugin} currentFile={currentFile} />);
|
||||||
|
onCleanup(() => {
|
||||||
|
el instanceof Node && document.body.contains(el) && document.body.removeChild(el);
|
||||||
|
});
|
||||||
|
return cleanup;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default {
|
||||||
|
show
|
||||||
|
};
|
362
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/SettingTab.tsx
vendored
Normal file
362
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/SettingTab.tsx
vendored
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
import * as ct from 'electron';
|
||||||
|
import process from 'process';
|
||||||
|
import { PluginSettingTab } from 'obsidian';
|
||||||
|
import type { SemVer } from 'semver'
|
||||||
|
import type UniversalExportPlugin from '../main';
|
||||||
|
import {
|
||||||
|
CustomExportSetting,
|
||||||
|
ExportSetting,
|
||||||
|
PandocExportSetting,
|
||||||
|
createEnv,
|
||||||
|
DEFAULT_ENV
|
||||||
|
} from '../settings';
|
||||||
|
import { setPlatformValue, getPlatformValue } from '../utils';
|
||||||
|
|
||||||
|
import { createSignal, createRoot, onCleanup, createMemo, createEffect, Show, batch, Match, Switch, JSX } from 'solid-js';
|
||||||
|
import { createStore, produce } from 'solid-js/store';
|
||||||
|
import { insert, Dynamic } from 'solid-js/web';
|
||||||
|
import type { Lang } from '../lang';
|
||||||
|
|
||||||
|
import pandoc from '../pandoc';
|
||||||
|
import Modal from './components/Modal';
|
||||||
|
import Button from './components/Button';
|
||||||
|
import Setting, { Text, Toggle, ExtraButton, DropDown, TextArea } from './components/Setting';
|
||||||
|
import export_templates from '../export_templates';
|
||||||
|
|
||||||
|
|
||||||
|
const SettingTab = (props: { lang: Lang, plugin: UniversalExportPlugin }) => {
|
||||||
|
const { plugin, lang } = props;
|
||||||
|
const [settings, setSettings0] = createStore(plugin.settings);
|
||||||
|
const [pandocVersion, setPandocVersion] = createSignal<SemVer>();
|
||||||
|
const envVars = createMemo(() => Object.entries(Object.assign({}, getPlatformValue(DEFAULT_ENV), getPlatformValue(settings.env) ?? {})).map(([n, v]) => `${n}="${v}"`).join('\n'));
|
||||||
|
const setSettings: typeof setSettings0 = (...args: unknown[]) => {
|
||||||
|
(setSettings0 as ((...args: unknown[]) => void))(...args);
|
||||||
|
plugin.saveSettings();
|
||||||
|
};
|
||||||
|
const setEnvVars = (envItems: string) => {
|
||||||
|
try {
|
||||||
|
const env: Record<string, string> = {};
|
||||||
|
for (let line of envItems.split('\n')) {
|
||||||
|
line = line.trim();
|
||||||
|
const sepIdx = line.indexOf('=');
|
||||||
|
if (sepIdx > 0) {
|
||||||
|
const name = line.substring(0, sepIdx);
|
||||||
|
let value = line.substring(sepIdx + 1).trim();
|
||||||
|
if (value.startsWith('"') && value.endsWith('"')) {
|
||||||
|
value = value.substring(1, value.length - 1);
|
||||||
|
}
|
||||||
|
env[name] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSettings('env', setPlatformValue(settings.env ?? {}, env));
|
||||||
|
} catch (e) {
|
||||||
|
alert(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const currentCommandTemplate = createMemo(() => settings.items.find(v => v.name === settings.lastEditName) ?? settings.items.first());
|
||||||
|
const currentEditCommandTemplate = <T extends 'custom' | 'pandoc'>(type?: T) => {
|
||||||
|
const template = currentCommandTemplate();
|
||||||
|
return (type === undefined || type === template.type ? template : undefined) as T extends 'custom' ? CustomExportSetting : T extends 'pandoc' ? PandocExportSetting : ExportSetting;
|
||||||
|
};
|
||||||
|
const customDefaultExportDirectory = createMemo(() => getPlatformValue(settings.customDefaultExportDirectory));
|
||||||
|
|
||||||
|
const updateCurrentEditCommandTemplate = (update: (prev: Partial<ExportSetting>) => void) => {
|
||||||
|
const idx = settings.items.findIndex(v => v.name === settings.lastEditName);
|
||||||
|
setSettings('items', idx === -1 ? 0 : idx, produce(item => {
|
||||||
|
update(item);
|
||||||
|
return item;
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const pandocDescription = createMemo(() => {
|
||||||
|
const version = pandocVersion();
|
||||||
|
if (version) {
|
||||||
|
if (app.vault.config.useMarkdownLinks && version.compare(pandoc.requiredVersion) === -1) {
|
||||||
|
return lang.settingTab.pandocVersionWithWarning(pandoc.requiredVersion)
|
||||||
|
}
|
||||||
|
return lang.settingTab.pandocVersion(version)
|
||||||
|
}
|
||||||
|
return lang.settingTab.pandocNotFound;
|
||||||
|
});
|
||||||
|
|
||||||
|
const [modal, setModal] = createSignal<() => JSX.Element>();
|
||||||
|
|
||||||
|
const AddCommandTemplateModal = () => {
|
||||||
|
type TemplateKey = keyof typeof export_templates;
|
||||||
|
const [templateName, setTemplateName] = createSignal(Object.keys(export_templates)[0] as TemplateKey);
|
||||||
|
const [name, setName] = createSignal<string>();
|
||||||
|
const doAdd = () => {
|
||||||
|
const template = JSON.parse(JSON.stringify(export_templates[templateName()]));
|
||||||
|
template.name = name();
|
||||||
|
batch(() => {
|
||||||
|
setSettings('items', items => [...items, template]);
|
||||||
|
setSettings('lastEditName', template.name);
|
||||||
|
});
|
||||||
|
setModal(undefined);
|
||||||
|
};
|
||||||
|
return <>
|
||||||
|
<Modal app={app} title={lang.settingTab.new} onClose={() => setModal(undefined)}>
|
||||||
|
<Setting name={lang.settingTab.template}>
|
||||||
|
<DropDown
|
||||||
|
options={Object.entries(export_templates).map(([k, v]) => ({ name: v.name, value: k }))}
|
||||||
|
selected={name() ?? templateName()}
|
||||||
|
onChange={(v: TemplateKey) => setTemplateName(v)}
|
||||||
|
/>
|
||||||
|
</Setting>
|
||||||
|
<Setting name={lang.settingTab.name}>
|
||||||
|
<Text value={name() ?? ''} onChange={(value) => setName(value)} />
|
||||||
|
</Setting>
|
||||||
|
<div class="modal-button-container">
|
||||||
|
<Button cta={true} onClick={doAdd}>{lang.settingTab.save}</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const RenameCommandTemplateModal = () => {
|
||||||
|
const [name, setName] = createSignal(currentEditCommandTemplate().name);
|
||||||
|
const doRename = () => {
|
||||||
|
batch(() => {
|
||||||
|
updateCurrentEditCommandTemplate((v) => v.name = name());
|
||||||
|
setSettings('lastEditName', name());
|
||||||
|
});
|
||||||
|
setModal(undefined);
|
||||||
|
};
|
||||||
|
return <>
|
||||||
|
<Modal app={app} title={lang.settingTab.rename} onClose={() => setModal(undefined)}>
|
||||||
|
<Setting name={lang.settingTab.name}>
|
||||||
|
<Text value={name() ?? ''} onChange={(value) => setName(value)} />
|
||||||
|
</Setting>
|
||||||
|
<div class="modal-button-container">
|
||||||
|
<Button cta={true} onClick={doRename}>{lang.settingTab.add}</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const PandocCommandTempateEditBlock = () => {
|
||||||
|
const template = () => currentEditCommandTemplate('pandoc');
|
||||||
|
const updateTemplate = (update: (prev: Partial<PandocExportSetting>) => void) => {
|
||||||
|
updateCurrentEditCommandTemplate(prev => prev.type === 'pandoc' ? update(prev) : undefined);
|
||||||
|
};
|
||||||
|
return <>
|
||||||
|
<Setting name={lang.settingTab.arguments}>
|
||||||
|
<Text style="width: 100%" value={template().arguments ?? ''} onChange={(value) => updateTemplate(v => v.arguments = value)} />
|
||||||
|
</Setting>
|
||||||
|
<Setting name={lang.settingTab.extraArguments}>
|
||||||
|
<Text style="width: 100%" value={template().customArguments ?? ''} title={template().customArguments} onChange={(value) => updateTemplate(v => v.customArguments = value)} />
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<Setting name={lang.settingTab.afterExport} heading={true} />
|
||||||
|
<Setting name={lang.settingTab.openExportedFileLocation}>
|
||||||
|
<Toggle checked={template().openExportedFileLocation ?? false} onChange={(checked) => updateTemplate(v => v.openExportedFileLocation = checked)} />
|
||||||
|
</Setting>
|
||||||
|
<Setting name={lang.settingTab.openExportedFile}>
|
||||||
|
<Toggle checked={template().openExportedFile ?? false} onChange={(checked) => updateTemplate(v => v.openExportedFile = checked)} />
|
||||||
|
</Setting>
|
||||||
|
<Setting name={lang.settingTab.runCommand}>
|
||||||
|
<Toggle checked={template().runCommand} onChange={(checked) => updateTemplate(v => v.runCommand = checked)} />
|
||||||
|
</Setting>
|
||||||
|
<Show when={template().runCommand}>
|
||||||
|
<Setting>
|
||||||
|
<Text style="width: 100%" value={template().command ?? ''} onChange={(value) => updateTemplate(v => v.command = value)} />
|
||||||
|
</Setting>
|
||||||
|
</Show>
|
||||||
|
</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const CustomCommandTempateEditBlock = () => {
|
||||||
|
const template = () => currentEditCommandTemplate('custom');
|
||||||
|
const updateTemplate = (update: (prev: Partial<CustomExportSetting>) => void) => {
|
||||||
|
updateCurrentEditCommandTemplate(prev => prev.type === 'custom' ? update(prev) : undefined);
|
||||||
|
};
|
||||||
|
return <>
|
||||||
|
<Setting name={lang.settingTab.command}>
|
||||||
|
<Text style="width: 100%" value={template().command} onChange={(value) => updateTemplate(v => v.command = value)} />
|
||||||
|
</Setting>
|
||||||
|
<Setting name={lang.settingTab.targetFileExtensions}>
|
||||||
|
<Text value={template().targetFileExtensions ?? ''} onChange={(value) => updateTemplate(v => v.targetFileExtensions = value)} />
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<Setting name={lang.settingTab.afterExport} heading={true} />
|
||||||
|
<Setting name={lang.settingTab.showCommandOutput} >
|
||||||
|
<Toggle checked={template().showCommandOutput ?? false} onChange={(checked) => updateTemplate(v => v.showCommandOutput = checked)} />
|
||||||
|
</Setting>
|
||||||
|
<Setting name={lang.settingTab.openExportedFileLocation}>
|
||||||
|
<Toggle checked={template().openExportedFileLocation ?? false} onChange={(checked) => updateTemplate(v => v.openExportedFileLocation = checked)} />
|
||||||
|
</Setting>
|
||||||
|
<Setting name={lang.settingTab.openExportedFile}>
|
||||||
|
<Toggle checked={template().openExportedFile ?? false} onChange={(checked) => updateTemplate(v => v.openExportedFile = checked)} />
|
||||||
|
</Setting>
|
||||||
|
</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetSettings = async () => {
|
||||||
|
await plugin.resetSettings();
|
||||||
|
setSettings(plugin.settings);
|
||||||
|
};
|
||||||
|
|
||||||
|
const chooseCustomDefaultExportDirectory = async () => {
|
||||||
|
const retval = await ct.remote.dialog.showOpenDialog({
|
||||||
|
defaultPath: customDefaultExportDirectory() ?? ct.remote.app.getPath('documents'),
|
||||||
|
properties: ['createDirectory', 'openDirectory'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!retval.canceled && retval.filePaths.length > 0) {
|
||||||
|
setSettings('customDefaultExportDirectory', v => setPlatformValue(v, retval.filePaths[0]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const choosePandocPath = async () => {
|
||||||
|
const retval = await ct.remote.dialog.showOpenDialog({
|
||||||
|
filters: process.platform == 'win32' ? [{ extensions: ['exe'], name: 'pandoc' }]: undefined,
|
||||||
|
properties: ['openFile'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!retval.canceled && retval.filePaths.length > 0) {
|
||||||
|
setSettings('pandocPath', (v) => setPlatformValue(v, retval.filePaths[0]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
createEffect(async () => {
|
||||||
|
try {
|
||||||
|
const env = createEnv(getPlatformValue(settings.env) ?? {});
|
||||||
|
setPandocVersion(await pandoc.getVersion(getPlatformValue(settings.pandocPath), env));
|
||||||
|
} catch {
|
||||||
|
setPandocVersion(undefined);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<Setting name={lang.settingTab.general} heading={true}>
|
||||||
|
<ExtraButton icon='reset' onClick={resetSettings} />
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<Setting name={lang.settingTab.pandocPath} description={pandocDescription()}>
|
||||||
|
<Text
|
||||||
|
placeholder={lang.settingTab.pandocPathPlaceholder}
|
||||||
|
value={getPlatformValue(settings.pandocPath) ?? ''}
|
||||||
|
onChange={(value) => setSettings('pandocPath', (v) => setPlatformValue(v, value))}
|
||||||
|
/>
|
||||||
|
<ExtraButton icon="folder" onClick={choosePandocPath} />
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<Setting name={lang.settingTab.defaultFolderForExportedFile}>
|
||||||
|
<DropDown options={[
|
||||||
|
{ name: lang.settingTab.auto, value: 'Auto' },
|
||||||
|
{ name: lang.settingTab.sameFolderWithCurrentFile, value: 'Same' },
|
||||||
|
{ name: lang.settingTab.customLocation, value: 'Custom' }
|
||||||
|
]} selected={settings.defaultExportDirectoryMode} onChange={(v: 'Auto' | 'Same' | 'Custom') => setSettings('defaultExportDirectoryMode', v)} />
|
||||||
|
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<Show when={settings.defaultExportDirectoryMode === 'Custom'}>
|
||||||
|
<Setting>
|
||||||
|
<Text value={customDefaultExportDirectory() ?? ''} title={customDefaultExportDirectory()} />
|
||||||
|
<ExtraButton icon="folder" onClick={chooseCustomDefaultExportDirectory} />
|
||||||
|
</Setting>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Setting name={lang.settingTab.openExportedFileLocation}>
|
||||||
|
<Toggle
|
||||||
|
checked={settings.openExportedFileLocation}
|
||||||
|
onChange={(v) => setSettings('openExportedFileLocation', v)}
|
||||||
|
/>
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<Setting name={lang.settingTab.openExportedFile} >
|
||||||
|
<Toggle
|
||||||
|
checked={settings.openExportedFile}
|
||||||
|
onChange={(v) => setSettings('openExportedFile', v)} />
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
|
||||||
|
<Setting name={lang.settingTab.ShowExportProgressBar}>
|
||||||
|
<Toggle
|
||||||
|
checked={settings.showExportProgressBar}
|
||||||
|
onChange={(v) => setSettings('showExportProgressBar', v)}
|
||||||
|
/>
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<Setting name={lang.settingTab.editCommandTemplate} heading={true} />
|
||||||
|
|
||||||
|
<Setting name={lang.settingTab.chooseCommandTemplate}>
|
||||||
|
<DropDown
|
||||||
|
options={settings.items.map(o => ({ name: o.name, value: o.name }))}
|
||||||
|
selected={settings.lastEditName}
|
||||||
|
onChange={(v) => setSettings('lastEditName', v)}
|
||||||
|
/>
|
||||||
|
<ExtraButton
|
||||||
|
icon="plus"
|
||||||
|
tooltip={lang.settingTab.add}
|
||||||
|
onClick={() => setModal(() => AddCommandTemplateModal)} />
|
||||||
|
<ExtraButton
|
||||||
|
icon="pencil"
|
||||||
|
tooltip={lang.settingTab.rename}
|
||||||
|
onClick={() => setModal(() => RenameCommandTemplateModal)} />
|
||||||
|
<ExtraButton
|
||||||
|
icon="trash"
|
||||||
|
tooltip={lang.settingTab.remove}
|
||||||
|
onClick={() => batch(() => {
|
||||||
|
setSettings('items', (items) => items.filter(n => n.name !== currentEditCommandTemplate()?.name));
|
||||||
|
setSettings('lastEditName', settings.items.first()?.name);
|
||||||
|
})} />
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<Switch>
|
||||||
|
<Match when={currentEditCommandTemplate('pandoc')}>
|
||||||
|
<PandocCommandTempateEditBlock />
|
||||||
|
</Match>
|
||||||
|
<Match when={currentEditCommandTemplate('custom')}>
|
||||||
|
<CustomCommandTempateEditBlock />
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
|
||||||
|
|
||||||
|
<Setting name={lang.settingTab.advanced} heading={true} />
|
||||||
|
|
||||||
|
{/* TODO:// optimize UI as https://www.jetbrains.com/help/idea/absolute-path-variables.html */}
|
||||||
|
<Setting name={lang.settingTab.environmentVariables} description={lang.settingTab.environmentVariablesDesc}>
|
||||||
|
<TextArea
|
||||||
|
style='width: 100%;height: 5em'
|
||||||
|
value={envVars()}
|
||||||
|
onChange={setEnvVars}
|
||||||
|
/>
|
||||||
|
</Setting>
|
||||||
|
|
||||||
|
<Show when={modal()}>
|
||||||
|
<Dynamic component={modal()} ref={(el: Node) => document.body.appendChild(el)} />
|
||||||
|
</Show>
|
||||||
|
</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default class extends PluginSettingTab {
|
||||||
|
plugin: UniversalExportPlugin;
|
||||||
|
#dispose?: () => void;
|
||||||
|
|
||||||
|
public get lang() {
|
||||||
|
return this.plugin.lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(plugin: UniversalExportPlugin) {
|
||||||
|
super(plugin.app, plugin);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.name = this.plugin.lang.settingTab.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
display() {
|
||||||
|
this.#dispose = createRoot(dispose => {
|
||||||
|
insert(this.containerEl, <SettingTab plugin={this.plugin} lang={this.lang} />);
|
||||||
|
onCleanup(() => {
|
||||||
|
this.containerEl.empty();
|
||||||
|
});
|
||||||
|
return dispose;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.#dispose();
|
||||||
|
}
|
||||||
|
}
|
5
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/Button.tsx
vendored
Normal file
5
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/Button.tsx
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import type { ParentProps } from 'solid-js/types';
|
||||||
|
|
||||||
|
export default (props: ParentProps<{ cta?: boolean, onClick?: () => void}> ) => {
|
||||||
|
return <button classList={{'mod-cta': props.cta}} onClick={props.onClick}>{props.children}</button>;
|
||||||
|
};
|
12
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/Icon.tsx
vendored
Normal file
12
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/Icon.tsx
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { setIcon } from 'obsidian';
|
||||||
|
import type { JSX } from 'solid-js/jsx-runtime';
|
||||||
|
|
||||||
|
export default (props: { name: string, title?: string, class?: string, onClick?: JSX.EventHandlerUnion<HTMLDivElement, MouseEvent> }) => {
|
||||||
|
return <div
|
||||||
|
ref={(el) => setIcon(el, props.name)}
|
||||||
|
class={props.class}
|
||||||
|
classList={{ 'clickable-icon': !!props.onClick }}
|
||||||
|
onClick={props.onClick}
|
||||||
|
title={props.title}
|
||||||
|
/>;
|
||||||
|
};
|
53
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/Modal.tsx
vendored
Normal file
53
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/Modal.tsx
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { App, Modal } from 'obsidian';
|
||||||
|
import { JSX, createEffect, onCleanup, onMount } from 'solid-js';
|
||||||
|
import { insert } from 'solid-js/web';
|
||||||
|
|
||||||
|
export default (props: {
|
||||||
|
app: App,
|
||||||
|
title?: JSX.Element,
|
||||||
|
children: JSX.Element,
|
||||||
|
classList?: {
|
||||||
|
[k: string]: boolean;
|
||||||
|
},
|
||||||
|
hidden?: boolean,
|
||||||
|
onClose?: () => void
|
||||||
|
}) => {
|
||||||
|
const modal = new Modal(props.app);
|
||||||
|
let classes: string[] = [];
|
||||||
|
let clean = false;
|
||||||
|
createEffect(() => {
|
||||||
|
insert(modal.titleEl, () => props.title);
|
||||||
|
});
|
||||||
|
createEffect(() => {
|
||||||
|
insert(modal.contentEl, () => props.children);
|
||||||
|
});
|
||||||
|
createEffect(() => {
|
||||||
|
const newClasses = Object.entries(props.classList ?? {}).filter(([, v]) => v).map(([k,]) => k);
|
||||||
|
if (classes.length > 0) {
|
||||||
|
modal.containerEl.removeClasses(classes);
|
||||||
|
}
|
||||||
|
if (newClasses.length > 0) {
|
||||||
|
modal.containerEl.addClasses(newClasses);
|
||||||
|
}
|
||||||
|
classes = newClasses;
|
||||||
|
});
|
||||||
|
createEffect(() => {
|
||||||
|
modal.containerEl.style.display = props.hidden ? 'None' : '';
|
||||||
|
});
|
||||||
|
|
||||||
|
modal.onClose = () => {
|
||||||
|
if (clean) return;
|
||||||
|
clean = true;
|
||||||
|
props.onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
onMount(() => modal.open());
|
||||||
|
|
||||||
|
onCleanup(() => {
|
||||||
|
if (!clean) {
|
||||||
|
modal.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return document.createTextNode('');
|
||||||
|
};
|
36
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/ProgressBar.tsx
vendored
Normal file
36
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/ProgressBar.tsx
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { createRoot, onCleanup } from 'solid-js';
|
||||||
|
import { insert } from 'solid-js/web';
|
||||||
|
|
||||||
|
const ProgressBar = (props: { message: string, ref: HTMLDivElement }) => {
|
||||||
|
return <>
|
||||||
|
<div ref={props.ref} class="progress-bar">
|
||||||
|
<div class="progress-bar-message u-center-text">{props.message}</div>
|
||||||
|
<div class="progress-bar-indicator">
|
||||||
|
<div class="progress-bar-line"></div>
|
||||||
|
<div class="progress-bar-subline" style="display: none;"></div>
|
||||||
|
<div class="progress-bar-subline mod-increase"></div>
|
||||||
|
<div class="progress-bar-subline mod-decrease"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
|
||||||
|
const show = (message: string) => createRoot(dispose => {
|
||||||
|
let disposed = false;
|
||||||
|
const cleanup = () => {
|
||||||
|
if (disposed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
disposed = true;
|
||||||
|
dispose();
|
||||||
|
};
|
||||||
|
let el: HTMLDivElement;
|
||||||
|
insert(document.body, () => <ProgressBar ref={el} message={message} />);
|
||||||
|
onCleanup(() => {
|
||||||
|
el instanceof Node && document.body.contains(el) && document.body.removeChild(el);
|
||||||
|
});
|
||||||
|
return cleanup;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default { show }
|
155
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/PropertyGrid.tsx
vendored
Normal file
155
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/PropertyGrid.tsx
vendored
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
import { FileFilter, remote } from 'electron';
|
||||||
|
import { For, JSX, createEffect, createSignal, untrack } from 'solid-js';
|
||||||
|
import Setting, { Toggle, DropDown, Text, ExtraButton } from './Setting';
|
||||||
|
|
||||||
|
|
||||||
|
const editors = {
|
||||||
|
checkbox: (props: { meta: CheckboxMeta, onChange?: (value: unknown) => void }) => {
|
||||||
|
return <>
|
||||||
|
<Setting name={props.meta.title} description={props.meta.description}>
|
||||||
|
<Toggle checked={getDefaultValue(props.meta)} onChange={props.onChange} />
|
||||||
|
</Setting>
|
||||||
|
</>;
|
||||||
|
},
|
||||||
|
textInput: (props: { meta: TextInputMeta, onChange?: (value: unknown) => void }) => {
|
||||||
|
return <>
|
||||||
|
<Setting name={props.meta.title} description={props.meta.description}>
|
||||||
|
<Text value={getDefaultValue(props.meta)} onChange={props.onChange} />
|
||||||
|
</Setting>
|
||||||
|
</>;
|
||||||
|
},
|
||||||
|
dropdown: (props: { meta: DropDownMeta, onChange?: (value: unknown) => void }) => {
|
||||||
|
return <>
|
||||||
|
<Setting name={props.meta.title} description={props.meta.description}>
|
||||||
|
<DropDown selected={getDefaultValue(props.meta)} options={props.meta.options} onChange={(v) => props.onChange(v)} />
|
||||||
|
</Setting>
|
||||||
|
</>;
|
||||||
|
},
|
||||||
|
fileSelectDialog: (props: { meta: FileSelectDialogMeta, onChange?: (value: unknown) => void }) => {
|
||||||
|
const [filePath, setFilePath] = createSignal<string>(getDefaultValue(props.meta));
|
||||||
|
|
||||||
|
const chooseFile = async () => {
|
||||||
|
const retval = await remote.dialog.showOpenDialog({
|
||||||
|
properties: ['openFile'],
|
||||||
|
filters: props.meta.filters
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!retval.canceled && retval.filePaths.length > 0) {
|
||||||
|
setFilePath(retval.filePaths[0]);
|
||||||
|
props.onChange && props.onChange(untrack(filePath));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<Setting name={props.meta.title} description={props.meta.description}>
|
||||||
|
<Text value={filePath() ?? ''} readOnly={true} />
|
||||||
|
<ExtraButton icon='folder' onClick={chooseFile} />
|
||||||
|
</Setting>
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getdefaultEditor = (meta: AnyPropertyGridControl, onChange?: (value: unknown) => void) => {
|
||||||
|
switch (meta.type) {
|
||||||
|
case 'checkbox': {
|
||||||
|
const E = editors[meta.type];
|
||||||
|
return <E meta={meta} onChange={onChange} />;
|
||||||
|
}
|
||||||
|
case 'dropdown': {
|
||||||
|
const E = editors[meta.type];
|
||||||
|
return <E meta={meta} onChange={onChange} />;
|
||||||
|
}
|
||||||
|
case 'textInput': {
|
||||||
|
const E = editors[meta.type];
|
||||||
|
return <E meta={meta} onChange={onChange} />;
|
||||||
|
}
|
||||||
|
case 'fileSelectDialog': {
|
||||||
|
const E = editors[meta.type];
|
||||||
|
return <E meta={meta} onChange={onChange} />;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return <div>Unsupported {JSON.stringify(meta)} </div>;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface PropertyGridControlMeta<T = unknown> {
|
||||||
|
title: string,
|
||||||
|
description?: string,
|
||||||
|
default?: T | (() => T)
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FileSelectDialogMeta extends PropertyGridControlMeta<string> {
|
||||||
|
type: 'fileSelectDialog',
|
||||||
|
filters?: FileFilter[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DropDownMeta extends PropertyGridControlMeta<string> {
|
||||||
|
type: 'dropdown',
|
||||||
|
options: {
|
||||||
|
name?: string,
|
||||||
|
value: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CheckboxMeta extends PropertyGridControlMeta<boolean> {
|
||||||
|
type: 'checkbox'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TextInputMeta extends PropertyGridControlMeta<string> {
|
||||||
|
type: 'textInput'
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AnyPropertyGridControl = DropDownMeta | CheckboxMeta | TextInputMeta | FileSelectDialogMeta;
|
||||||
|
|
||||||
|
|
||||||
|
export type PropertyGridMeta = {
|
||||||
|
[k: string]: AnyPropertyGridControl
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PropertyGridProps = {
|
||||||
|
meta: PropertyGridMeta,
|
||||||
|
value?: Record<string, unknown>,
|
||||||
|
customEditor?: (meta: AnyPropertyGridControl, onChange?: (value: unknown) => void) => JSX.Element | undefined,
|
||||||
|
onChange?: (value: Record<string, unknown>, key: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default (props: PropertyGridProps) => {
|
||||||
|
|
||||||
|
let obj: Record<string, unknown> = {};
|
||||||
|
createEffect(() => obj = props.value ?? createDefaultObject(props.meta));
|
||||||
|
|
||||||
|
const onChange = (key: string, value: unknown) => {
|
||||||
|
obj[key] = value;
|
||||||
|
props.onChange && props.onChange(obj, key);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createEditor = (key: string, meta: AnyPropertyGridControl) => {
|
||||||
|
const onValueChange = (value: unknown) => onChange(key, value);
|
||||||
|
let editor: JSX.Element | undefined = undefined;
|
||||||
|
if (props.customEditor) {
|
||||||
|
editor = props.customEditor(meta, onValueChange);
|
||||||
|
if (editor) {
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getdefaultEditor(meta, onValueChange);
|
||||||
|
};
|
||||||
|
|
||||||
|
return <>
|
||||||
|
<For each={Object.entries(props.meta)}>
|
||||||
|
{([key, meta]) => createEditor(key, meta)}
|
||||||
|
</For>
|
||||||
|
</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createDefaultObject = (meta: PropertyGridMeta): Record<string, unknown> => {
|
||||||
|
return Object.fromEntries(Object.entries(meta).map(([k, m]) => [k, getDefaultValue(m)]));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getDefaultValue = <T, M extends PropertyGridControlMeta<T>>(meta: M) => {
|
||||||
|
if (meta.default) {
|
||||||
|
return meta.default instanceof Function ? meta.default() : meta.default;
|
||||||
|
}
|
||||||
|
};
|
124
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/Setting.tsx
vendored
Normal file
124
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/components/Setting.tsx
vendored
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
import { For, JSX, createContext, onCleanup, onMount, useContext } from 'solid-js';
|
||||||
|
import * as Ob from 'obsidian';
|
||||||
|
|
||||||
|
type SettingContext = {
|
||||||
|
settingEl: HTMLDivElement
|
||||||
|
}
|
||||||
|
|
||||||
|
const Context = createContext<SettingContext>();
|
||||||
|
|
||||||
|
const useSetting = () => useContext(Context);
|
||||||
|
|
||||||
|
export default (props: {
|
||||||
|
name?: string,
|
||||||
|
description?: string,
|
||||||
|
class?: string,
|
||||||
|
heading?: boolean,
|
||||||
|
disabled?: boolean,
|
||||||
|
noInfo?: boolean,
|
||||||
|
children?: JSX.Element
|
||||||
|
}) => {
|
||||||
|
const context: SettingContext = {
|
||||||
|
settingEl: null
|
||||||
|
};
|
||||||
|
return <>
|
||||||
|
<Context.Provider value={context}>
|
||||||
|
<div
|
||||||
|
ref={(el) => context.settingEl = el}
|
||||||
|
class={`setting-item ${props.class ?? ''}`.trimEnd()}
|
||||||
|
classList={{
|
||||||
|
'setting-item-heading': props.heading,
|
||||||
|
'is-disable': props.disabled
|
||||||
|
}}>
|
||||||
|
<div class="setting-item-info">
|
||||||
|
<div class="setting-item-name">{props.name}</div>
|
||||||
|
<div class="setting-item-description">{props.description}</div>
|
||||||
|
</div>
|
||||||
|
<div class="setting-item-control">
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Context.Provider>
|
||||||
|
</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const Toggle = (props: { checked?: boolean, onChange?: (checked: boolean) => void }) => {
|
||||||
|
const setting = useSetting();
|
||||||
|
onMount(() => {
|
||||||
|
setting.settingEl.addClass('mod-toggle');
|
||||||
|
});
|
||||||
|
onCleanup(() => {
|
||||||
|
setting.settingEl.removeClass('mod-toggle');
|
||||||
|
});
|
||||||
|
return <>
|
||||||
|
<div class="checkbox-container" classList={{ 'is-enabled': props.checked }} onClick={() => props.onChange && props.onChange(!props.checked)} >
|
||||||
|
<input type="checkbox" />
|
||||||
|
</div>
|
||||||
|
</>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const ExtraButton = (props: { icon?: string, onClick?: () => void, tooltip?: string }) => {
|
||||||
|
return <div
|
||||||
|
ref={(el) => props.icon && Ob.setIcon(el, props.icon)}
|
||||||
|
class="setting-editor-extra-setting-button"
|
||||||
|
classList={{ 'clickable-icon': props.icon && !!props.onClick }}
|
||||||
|
aria-label={props.tooltip}
|
||||||
|
onClick={props.onClick}
|
||||||
|
/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const Text = (props: { placeholder?: string,
|
||||||
|
title?: string,
|
||||||
|
value?: string,
|
||||||
|
style?: string,
|
||||||
|
disabled?: boolean,
|
||||||
|
readOnly?: boolean,
|
||||||
|
spellcheck?: boolean,
|
||||||
|
onChange?: (value: string) => void }) => {
|
||||||
|
return <input
|
||||||
|
type="text"
|
||||||
|
title={props.title}
|
||||||
|
readOnly={props.readOnly}
|
||||||
|
placeholder={props.placeholder}
|
||||||
|
spellcheck={props.spellcheck ?? false}
|
||||||
|
style={props.style}
|
||||||
|
value={props.value}
|
||||||
|
onChange={(e) => props.onChange?.(e.target.value)}
|
||||||
|
disabled={props.disabled}
|
||||||
|
/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TextArea = (props: { placeholder?: string,
|
||||||
|
title?: string,
|
||||||
|
value?: string,
|
||||||
|
style?: string,
|
||||||
|
disabled?: boolean,
|
||||||
|
spellcheck?: boolean,
|
||||||
|
onChange?: (value: string) => void }) => {
|
||||||
|
return <textarea
|
||||||
|
placeholder={props.placeholder}
|
||||||
|
spellcheck={props.spellcheck ?? false}
|
||||||
|
style={props.style}
|
||||||
|
value={props.value}
|
||||||
|
onChange={(e) => props.onChange?.(e.target.value)}
|
||||||
|
disabled={props.disabled}
|
||||||
|
/>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const DropDown = (props: {
|
||||||
|
options: { name?: string, value: string }[],
|
||||||
|
selected?: string,
|
||||||
|
onChange?: (value: string, index: number) => void
|
||||||
|
}) => {
|
||||||
|
return <>
|
||||||
|
<select class="dropdown" onChange={(e) => props.onChange?.(e.target.value, e.target.selectedIndex)} autofocus={true}>
|
||||||
|
<For each={props.options}>
|
||||||
|
{(item) => <option value={item.value} selected={item.value === props.selected}>{item.name ?? item.value}</option>}
|
||||||
|
</For>
|
||||||
|
</select>
|
||||||
|
</>;
|
||||||
|
};
|
6
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/index.tsx
vendored
Normal file
6
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/index.tsx
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import ExportDialog from './ExportDialog';
|
||||||
|
import ExportSettingTab from './SettingTab';
|
||||||
|
|
||||||
|
export {
|
||||||
|
ExportDialog, ExportSettingTab
|
||||||
|
};
|
158
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/legacy/export_dialog.ts
vendored
Normal file
158
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/legacy/export_dialog.ts
vendored
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import { App, Modal, Setting, TFile, TextComponent } from 'obsidian';
|
||||||
|
import * as ct from 'electron';
|
||||||
|
import { extractDefaultExtension as extractExtension } from '../../settings';
|
||||||
|
import { setPlatformValue, getPlatformValue } from '../../utils';
|
||||||
|
import { exportToOo } from '../../exporto0o';
|
||||||
|
import { setTooltip, setVisible } from './setting_tab';
|
||||||
|
import type UniversalExportPlugin from '../../main';
|
||||||
|
|
||||||
|
export class ExportDialog extends Modal {
|
||||||
|
readonly plugin: UniversalExportPlugin;
|
||||||
|
readonly currentFile: TFile;
|
||||||
|
get lang() {
|
||||||
|
return this.plugin.lang;
|
||||||
|
}
|
||||||
|
constructor(app: App, plugin: UniversalExportPlugin, currentFile: TFile) {
|
||||||
|
super(app);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.currentFile = currentFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpen() {
|
||||||
|
const {
|
||||||
|
titleEl,
|
||||||
|
contentEl,
|
||||||
|
currentFile,
|
||||||
|
plugin: { settings: globalSetting },
|
||||||
|
lang,
|
||||||
|
} = this;
|
||||||
|
|
||||||
|
const exportDirectoryMode = globalSetting.defaultExportDirectoryMode;
|
||||||
|
|
||||||
|
let exportType = globalSetting.lastExportType ?? globalSetting.items.first()?.name;
|
||||||
|
|
||||||
|
let setting = globalSetting.items.find(o => o.name === exportType);
|
||||||
|
let extension = extractExtension(setting);
|
||||||
|
|
||||||
|
let showOverwriteConfirmation = globalSetting.showOverwriteConfirmation;
|
||||||
|
let candidateOutputDirectory = `${getPlatformValue(globalSetting.lastExportDirectory) ?? ct.remote.app.getPath('documents')}`;
|
||||||
|
let candidateOutputFileName = `${currentFile.basename}${extension}`;
|
||||||
|
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
let candidateOutputFileNameSetting: Setting;
|
||||||
|
|
||||||
|
if (exportDirectoryMode === 'Same') {
|
||||||
|
const fullPath: string = this.app.vault.adapter.getFullPath(currentFile.path);
|
||||||
|
candidateOutputDirectory = fullPath.substring(0, fullPath.length - currentFile.name.length - 1);
|
||||||
|
} else if (exportDirectoryMode === 'Custom') {
|
||||||
|
candidateOutputDirectory = getPlatformValue(globalSetting.customDefaultExportDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
titleEl.setText(lang.exportDialog.title(setting.name));
|
||||||
|
|
||||||
|
new Setting(contentEl).setName(lang.exportDialog.type).addDropdown(cb => {
|
||||||
|
cb.addOptions(Object.fromEntries(globalSetting.items.map(o => [o.name, o.name])))
|
||||||
|
.onChange(v => {
|
||||||
|
exportType = v;
|
||||||
|
setting = globalSetting.items.find(o => o.name === exportType);
|
||||||
|
titleEl.setText(lang.exportDialog.title(setting.name));
|
||||||
|
|
||||||
|
extension = extractExtension(setting);
|
||||||
|
if (candidateOutputFileName.includes('.')) {
|
||||||
|
candidateOutputFileName = candidateOutputFileName.substring(0, candidateOutputFileName.lastIndexOf('.')) + extension;
|
||||||
|
} else {
|
||||||
|
candidateOutputFileName = candidateOutputFileName + extension;
|
||||||
|
}
|
||||||
|
(candidateOutputFileNameSetting.components.first() as TextComponent)
|
||||||
|
?.setValue(candidateOutputFileName)
|
||||||
|
.inputEl.setAttribute('title', candidateOutputFileName);
|
||||||
|
})
|
||||||
|
.setValue(exportType);
|
||||||
|
});
|
||||||
|
|
||||||
|
candidateOutputFileNameSetting = new Setting(contentEl).setName(lang.exportDialog.fileName).addText(cb => {
|
||||||
|
cb.setValue(candidateOutputFileName)
|
||||||
|
.onChange(v => {
|
||||||
|
candidateOutputFileName = v;
|
||||||
|
setTooltip(cb.inputEl, v);
|
||||||
|
})
|
||||||
|
.inputEl.setAttribute('title', candidateOutputFileName);
|
||||||
|
});
|
||||||
|
|
||||||
|
const candidateOutputDirectorySetting = new Setting(contentEl)
|
||||||
|
.setName(lang.exportDialog.exportTo)
|
||||||
|
.addText(cb => {
|
||||||
|
cb.setValue(candidateOutputDirectory).onChange(v => {
|
||||||
|
candidateOutputDirectory = v;
|
||||||
|
setTooltip(cb.inputEl, candidateOutputDirectory);
|
||||||
|
});
|
||||||
|
cb.setDisabled(true);
|
||||||
|
setTooltip(cb.inputEl, candidateOutputDirectory);
|
||||||
|
})
|
||||||
|
.addExtraButton(cb => {
|
||||||
|
cb.setIcon('folder').onClick(async () => {
|
||||||
|
const retval = await ct.remote.dialog.showOpenDialog({
|
||||||
|
title: lang.exportDialog.selectExportFolder,
|
||||||
|
defaultPath: candidateOutputDirectory,
|
||||||
|
properties: ['createDirectory', 'openDirectory'],
|
||||||
|
});
|
||||||
|
if (!retval.canceled && retval.filePaths?.length > 0) {
|
||||||
|
candidateOutputDirectory = retval.filePaths[0];
|
||||||
|
(candidateOutputDirectorySetting.components.first() as TextComponent)
|
||||||
|
?.setValue(candidateOutputDirectory)
|
||||||
|
.inputEl.setAttribute('title', candidateOutputDirectory);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(contentEl).setName(lang.exportDialog.overwriteConfirmation).addToggle(cb => {
|
||||||
|
cb.setValue(showOverwriteConfirmation).onChange(v => (showOverwriteConfirmation = v));
|
||||||
|
});
|
||||||
|
|
||||||
|
contentEl.createEl('div', { cls: ['modal-button-container'], parent: contentEl }, el => {
|
||||||
|
el.createEl('button', {
|
||||||
|
text: lang.exportDialog.export,
|
||||||
|
cls: ['mod-cta'],
|
||||||
|
parent: el,
|
||||||
|
}).onclick = async () => {
|
||||||
|
await exportToOo(
|
||||||
|
this.plugin,
|
||||||
|
currentFile,
|
||||||
|
candidateOutputDirectory,
|
||||||
|
candidateOutputFileName,
|
||||||
|
setting,
|
||||||
|
showOverwriteConfirmation,
|
||||||
|
async () => {
|
||||||
|
globalSetting.showOverwriteConfirmation = showOverwriteConfirmation;
|
||||||
|
globalSetting.lastExportDirectory = setPlatformValue(globalSetting.lastExportDirectory, candidateOutputDirectory);
|
||||||
|
|
||||||
|
globalSetting.lastExportType = setting.name;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
setVisible(this.containerEl, true);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
setVisible(this.containerEl, false);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
const { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const show = (plugin: UniversalExportPlugin, currentFile: TFile) => {
|
||||||
|
const dialog = new ExportDialog(plugin.app, plugin, currentFile);
|
||||||
|
dialog.open();
|
||||||
|
return () => dialog.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
show,
|
||||||
|
};
|
4
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/legacy/index.ts
vendored
Normal file
4
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/legacy/index.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import ExportDialog from './export_dialog';
|
||||||
|
import ExportSettingTab from './setting_tab';
|
||||||
|
|
||||||
|
export { ExportDialog, ExportSettingTab };
|
573
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/legacy/setting_tab.ts
vendored
Normal file
573
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/legacy/setting_tab.ts
vendored
Normal file
@ -0,0 +1,573 @@
|
|||||||
|
import { App, PluginSettingTab, Setting, TextComponent } from 'obsidian';
|
||||||
|
import * as ct from 'electron';
|
||||||
|
import { CustomExportSetting, ExportSetting, PandocExportSetting, UniversalExportPluginSettings } from '../../settings';
|
||||||
|
import { setPlatformValue, getPlatformValue } from '../../utils';
|
||||||
|
import pandoc from '../../pandoc';
|
||||||
|
|
||||||
|
import { Modal } from 'obsidian';
|
||||||
|
import export_command_templates from '../../export_templates';
|
||||||
|
import type ExportSettingTab from './setting_tab';
|
||||||
|
import type UniversalExportPlugin from '../../main';
|
||||||
|
|
||||||
|
export default class extends PluginSettingTab {
|
||||||
|
plugin: UniversalExportPlugin;
|
||||||
|
|
||||||
|
public get lang() {
|
||||||
|
return this.plugin.lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(plugin: UniversalExportPlugin) {
|
||||||
|
super(plugin.app, plugin);
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.name = this.plugin.lang.settingTab.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
const { containerEl } = this;
|
||||||
|
containerEl.empty();
|
||||||
|
}
|
||||||
|
display(): void {
|
||||||
|
const { containerEl, lang, plugin } = this;
|
||||||
|
containerEl.empty();
|
||||||
|
const validateCallback = (v: unknown, k: keyof typeof t, t: unknown): boolean => {
|
||||||
|
const sv = t[k];
|
||||||
|
if (v === sv) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// noinspection RedundantIfStatementJS
|
||||||
|
if (k !== 'lastEditName' && sv === undefined && (v === false || v === '')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
const changedCallback = async (v: unknown, k: keyof typeof t, t: unknown) => {
|
||||||
|
if (v !== undefined) {
|
||||||
|
if (v === false || (typeof v === 'string' && v.trim() === '')) {
|
||||||
|
delete t[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await plugin.saveSettings();
|
||||||
|
};
|
||||||
|
|
||||||
|
const globalSettingWatcher = new Watcher<UniversalExportPluginSettings>({
|
||||||
|
onChangingCallback: validateCallback,
|
||||||
|
onChangedCallback: changedCallback,
|
||||||
|
});
|
||||||
|
|
||||||
|
const settingWatcher = new Watcher<ExportSetting>({
|
||||||
|
onChangingCallback: validateCallback,
|
||||||
|
onChangedCallback: changedCallback,
|
||||||
|
});
|
||||||
|
const pandocSettingWatcher = settingWatcher.as<PandocExportSetting>();
|
||||||
|
const customSettingWatcher = settingWatcher.as<CustomExportSetting>();
|
||||||
|
|
||||||
|
let globalSetting = new Proxy(plugin.settings, globalSettingWatcher);
|
||||||
|
let current = new Proxy(
|
||||||
|
globalSetting.items.find(v => v.name === globalSetting.lastEditName) ?? globalSetting.items.first(),
|
||||||
|
settingWatcher
|
||||||
|
);
|
||||||
|
|
||||||
|
const changeEditExportSetting = (name: string) => {
|
||||||
|
const newSetting = globalSetting.items.find(v => v.name === name) ?? globalSetting.items.first();
|
||||||
|
if (globalSetting.lastEditName !== newSetting.name) {
|
||||||
|
globalSetting.lastEditName = newSetting.name;
|
||||||
|
}
|
||||||
|
if (newSetting) {
|
||||||
|
current = new Proxy(newSetting, settingWatcher);
|
||||||
|
settingWatcher.fireChanged(current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
containerEl.createEl('h2', { text: lang.settingTab.title });
|
||||||
|
|
||||||
|
// General
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName(lang.settingTab.general)
|
||||||
|
.addExtraButton(cb => {
|
||||||
|
cb.setIcon('reset')
|
||||||
|
.setTooltip(lang.settingTab.reset)
|
||||||
|
.onClick(async () => {
|
||||||
|
await this.plugin.resetSettings();
|
||||||
|
|
||||||
|
globalSetting = new Proxy(plugin.settings, globalSettingWatcher);
|
||||||
|
globalSettingWatcher.fireChanged(globalSetting);
|
||||||
|
|
||||||
|
changeEditExportSetting(globalSetting.lastEditName);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.setHeading();
|
||||||
|
|
||||||
|
const pandocPathSetting = new Setting(containerEl);
|
||||||
|
pandoc
|
||||||
|
.getVersion(getPlatformValue(globalSetting.pandocPath))
|
||||||
|
.then(ver => {
|
||||||
|
pandocPathSetting.setDesc(lang.settingTab.pandocVersion(ver.version));
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
pandocPathSetting.setDesc(lang.settingTab.pandocNotFound);
|
||||||
|
});
|
||||||
|
|
||||||
|
pandocPathSetting.setName(lang.settingTab.pandocPath).addText(cb => {
|
||||||
|
cb.setPlaceholder(lang.settingTab.pandocPathPlaceholder).onChange(v => {
|
||||||
|
if (globalSetting.pandocPath !== v) {
|
||||||
|
globalSetting.pandocPath = setPlatformValue(globalSetting.pandocPath, v);
|
||||||
|
pandoc
|
||||||
|
.getVersion(getPlatformValue(globalSetting.pandocPath))
|
||||||
|
.then(ver => {
|
||||||
|
pandocPathSetting.setDesc(lang.settingTab.pandocVersion(ver.version));
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
pandocPathSetting.setDesc(lang.settingTab.pandocNotFound);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
globalSettingWatcher.watchOnChanged('pandocPath', value => {
|
||||||
|
cb.setValue(getPlatformValue(value) ?? '');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(containerEl).setName(lang.settingTab.defaultFolderForExportedFile).addDropdown(cb => {
|
||||||
|
cb.addOptions({
|
||||||
|
'Auto': lang.settingTab.auto,
|
||||||
|
'Same': lang.settingTab.sameFolderWithCurrentFile,
|
||||||
|
'Custom': lang.settingTab.customLocation,
|
||||||
|
}).onChange((v: 'Auto' | 'Same' | 'Custom') => {
|
||||||
|
if (globalSetting.defaultExportDirectoryMode !== v) {
|
||||||
|
globalSetting.defaultExportDirectoryMode = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
globalSettingWatcher.watchOnChanged('defaultExportDirectoryMode', (value: 'Auto' | 'Same' | 'Custom') => {
|
||||||
|
cb.setValue(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const customDefaultExportDirectorySetting = new Setting(containerEl)
|
||||||
|
.addText(cb => {
|
||||||
|
globalSettingWatcher.watchOnChanged('customDefaultExportDirectory', (value?) => {
|
||||||
|
const val = getPlatformValue(value);
|
||||||
|
cb.setValue(val ?? '');
|
||||||
|
setTooltip(cb.inputEl, val);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.setClass('ex-setting-item')
|
||||||
|
.addExtraButton(cb => {
|
||||||
|
cb.setIcon('folder').onClick(async () => {
|
||||||
|
const retval = await ct.remote.dialog.showOpenDialog({
|
||||||
|
defaultPath: getPlatformValue(globalSetting.customDefaultExportDirectory) ?? ct.remote.app.getPath('documents'),
|
||||||
|
properties: ['createDirectory', 'openDirectory'],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!retval.canceled && retval.filePaths.length > 0) {
|
||||||
|
globalSetting.customDefaultExportDirectory = setPlatformValue(globalSetting.customDefaultExportDirectory, retval.filePaths[0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
globalSettingWatcher.watchOnChanged('customDefaultExportDirectory', value => {
|
||||||
|
const text = customDefaultExportDirectorySetting.components.first() as TextComponent;
|
||||||
|
const val = getPlatformValue(value);
|
||||||
|
text.setValue(val ?? '');
|
||||||
|
setTooltip(text.inputEl, val);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
globalSettingWatcher.watchOnChanged('defaultExportDirectoryMode', value => {
|
||||||
|
setVisible(customDefaultExportDirectorySetting.settingEl, value === 'Custom');
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(containerEl).setName(lang.settingTab.openExportedFileLocation).addToggle(cb => {
|
||||||
|
cb.onChange(v => {
|
||||||
|
if (globalSetting.openExportedFileLocation !== v) {
|
||||||
|
globalSetting.openExportedFileLocation = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
globalSettingWatcher.watchOnChanged('openExportedFileLocation', v => {
|
||||||
|
cb.setValue(v);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(containerEl).setName(lang.settingTab.openExportedFile).addToggle(cb => {
|
||||||
|
cb.onChange(v => {
|
||||||
|
if (globalSetting.openExportedFile !== v) {
|
||||||
|
globalSetting.openExportedFile = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
globalSettingWatcher.watchOnChanged('openExportedFile', v => {
|
||||||
|
cb.setValue(v);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// settings for export type
|
||||||
|
|
||||||
|
new Setting(containerEl).setName(lang.settingTab.editCommandTemplate).setHeading();
|
||||||
|
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName(lang.settingTab.chooseCommandTemplate)
|
||||||
|
.addDropdown(cb => {
|
||||||
|
cb.onChange(v => {
|
||||||
|
if (globalSetting.lastEditName !== v) {
|
||||||
|
changeEditExportSetting(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
globalSettingWatcher.watchOnChanged('items', (value: ExportSetting[]) => {
|
||||||
|
cb.selectEl.empty();
|
||||||
|
cb.addOptions(Object.fromEntries(value.map(o => [o.name, o.name])));
|
||||||
|
cb.setValue(globalSetting.lastEditName ?? globalSetting.items.first()?.name);
|
||||||
|
});
|
||||||
|
globalSettingWatcher.watchOnChanged('lastEditName', value => {
|
||||||
|
cb.setValue(value);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.addExtraButton(button => {
|
||||||
|
button.setTooltip(lang.settingTab.add);
|
||||||
|
button.setIcon('plus');
|
||||||
|
button.onClick(() => {
|
||||||
|
new AddNewModal(this.app, this, s => {
|
||||||
|
globalSetting.items = [...globalSetting.items, s];
|
||||||
|
changeEditExportSetting(s.name);
|
||||||
|
}).open();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.addExtraButton(button => {
|
||||||
|
button.setTooltip(lang.settingTab.rename);
|
||||||
|
button.setIcon('pencil');
|
||||||
|
button.onClick(() => {
|
||||||
|
new RenemeModal(this.app, this, current, n => {
|
||||||
|
current.name = n;
|
||||||
|
globalSetting.items = [...globalSetting.items];
|
||||||
|
changeEditExportSetting(n);
|
||||||
|
}).open();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.addExtraButton(button => {
|
||||||
|
button.setTooltip(lang.settingTab.remove);
|
||||||
|
button.setIcon('trash');
|
||||||
|
button.onClick(() => {
|
||||||
|
globalSetting.items = globalSetting.items.filter(o => o.name != current.name);
|
||||||
|
changeEditExportSetting(globalSetting.items.first()?.name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const commandSetting = new Setting(containerEl).setName(lang.settingTab.command).addText(cb => {
|
||||||
|
cb.setDisabled(true);
|
||||||
|
cb.onChange(v => {
|
||||||
|
if (current.type === 'custom' && current.command !== v) {
|
||||||
|
current.command = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
customSettingWatcher.watchOnChanged('command', value => {
|
||||||
|
cb.setValue(value);
|
||||||
|
});
|
||||||
|
settingWatcher.watchOnChanged('type', value => {
|
||||||
|
cb.setDisabled(value !== 'custom');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
settingWatcher.watchOnChanged('type', value => {
|
||||||
|
setVisible(commandSetting.settingEl, value === 'custom');
|
||||||
|
});
|
||||||
|
|
||||||
|
const argumentsSetting = new Setting(containerEl).setName(lang.settingTab.arguments).addText(cb => {
|
||||||
|
cb.setDisabled(true);
|
||||||
|
cb.onChange(v => {
|
||||||
|
if (current.type === 'pandoc' && current.arguments !== v) {
|
||||||
|
current.arguments = v;
|
||||||
|
setTooltip(cb.inputEl, current.arguments);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pandocSettingWatcher.watchOnChanged('arguments', value => {
|
||||||
|
cb.setValue(value ?? '');
|
||||||
|
setTooltip(cb.inputEl, value);
|
||||||
|
});
|
||||||
|
settingWatcher.watchOnChanged('type', value => {
|
||||||
|
cb.setDisabled(value !== 'custom');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
settingWatcher.watchOnChanged('type', value => {
|
||||||
|
setVisible(argumentsSetting.settingEl, value === 'pandoc');
|
||||||
|
});
|
||||||
|
|
||||||
|
const targetFileExtensionsSetting = new Setting(containerEl).setName(lang.settingTab.targetFileExtensions).addText(cb => {
|
||||||
|
cb.onChange(v => {
|
||||||
|
if (current.type === 'custom' && current.targetFileExtensions !== v) {
|
||||||
|
current.targetFileExtensions = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
customSettingWatcher.watchOnChanged('targetFileExtensions', value => {
|
||||||
|
cb.setValue(value ?? '');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
settingWatcher.watchOnChanged('type', value => {
|
||||||
|
setVisible(targetFileExtensionsSetting.settingEl, value === 'custom');
|
||||||
|
});
|
||||||
|
|
||||||
|
const customArgumentsSetting = new Setting(containerEl).setName(lang.settingTab.extraArguments).addText(cb => {
|
||||||
|
cb.onChange(v => {
|
||||||
|
if (current.type === 'pandoc' && current.customArguments !== v) {
|
||||||
|
current.customArguments = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pandocSettingWatcher.watchOnChanged('customArguments', value => {
|
||||||
|
cb.setValue(value ?? '');
|
||||||
|
setTooltip(cb.inputEl, value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
settingWatcher.watchOnChanged('type', value => {
|
||||||
|
setVisible(customArgumentsSetting.settingEl, value === 'pandoc');
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(containerEl).setName(lang.settingTab.afterExport).setHeading();
|
||||||
|
|
||||||
|
const showCommandOutputSetting = new Setting(containerEl).setName(lang.settingTab.showCommandOutput).addToggle(cb => {
|
||||||
|
if (current.type === 'custom') {
|
||||||
|
cb.setValue(current.showCommandOutput);
|
||||||
|
}
|
||||||
|
cb.onChange(v => {
|
||||||
|
if (current.type === 'custom' && current.showCommandOutput !== v) {
|
||||||
|
current.showCommandOutput = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
settingWatcher.watchOnChanged('type', value => {
|
||||||
|
setVisible(showCommandOutputSetting.settingEl, value === 'custom');
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(containerEl).setName(lang.settingTab.openExportedFileLocation).addToggle(cb => {
|
||||||
|
cb.onChange(v => {
|
||||||
|
if (current.openExportedFileLocation !== v) {
|
||||||
|
current.openExportedFileLocation = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
settingWatcher.watchOnChanged('openExportedFileLocation', value => {
|
||||||
|
cb.setValue(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
new Setting(containerEl).setName(lang.settingTab.runCommand).addToggle(cb => {
|
||||||
|
cb.onChange(v => {
|
||||||
|
if (current.type === 'pandoc' && current.runCommand !== v) {
|
||||||
|
current.runCommand = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pandocSettingWatcher.watchOnChanged('runCommand', value => {
|
||||||
|
cb.setValue(value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const commandAfterExportSetting = new Setting(containerEl).addText(cb => {
|
||||||
|
cb.onChange(v => {
|
||||||
|
if (current.command !== v) {
|
||||||
|
current.command = v;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pandocSettingWatcher.watchOnChanged('command', value => {
|
||||||
|
cb.setValue(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
pandocSettingWatcher.watchOnChanged('runCommand', (value, _, target) => {
|
||||||
|
setVisible(commandAfterExportSetting.settingEl, target.type === 'pandoc' && value);
|
||||||
|
cb.setValue(current.command);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
globalSettingWatcher.fireChanged(globalSetting);
|
||||||
|
settingWatcher.fireChanged(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AddNewModal extends Modal {
|
||||||
|
readonly settingTab: ExportSettingTab;
|
||||||
|
readonly callback: (setting: ExportSetting) => void;
|
||||||
|
get lang() {
|
||||||
|
return this.settingTab.lang;
|
||||||
|
}
|
||||||
|
constructor(app: App, settingTab: ExportSettingTab, callback: (setting: ExportSetting) => void) {
|
||||||
|
super(app);
|
||||||
|
this.settingTab = settingTab;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpen() {
|
||||||
|
const { contentEl, titleEl, lang, callback } = this;
|
||||||
|
titleEl.setText(lang.settingTab.new);
|
||||||
|
let tpl = Object.values(export_command_templates).first();
|
||||||
|
let tplName = tpl.name;
|
||||||
|
let name = tpl.name;
|
||||||
|
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
|
let nameSetting: Setting;
|
||||||
|
|
||||||
|
new Setting(contentEl).setName(lang.settingTab.template).addDropdown(cb => {
|
||||||
|
cb.addOptions(Object.fromEntries(Object.values(export_command_templates).map(o => [o.name, o.name])))
|
||||||
|
.setValue(tplName)
|
||||||
|
.onChange(v => {
|
||||||
|
tplName = v;
|
||||||
|
name = v;
|
||||||
|
|
||||||
|
(nameSetting.components.first() as TextComponent)?.setValue(name);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
nameSetting = new Setting(contentEl).setName(lang.settingTab.name).addText(cb => {
|
||||||
|
cb.setValue(name).onChange(v => (name = v));
|
||||||
|
});
|
||||||
|
|
||||||
|
contentEl.createEl('div', { cls: ['modal-button-container'], parent: contentEl }, el => {
|
||||||
|
el.createEl('button', {
|
||||||
|
text: lang.settingTab.add,
|
||||||
|
cls: ['mod-cta'],
|
||||||
|
parent: el,
|
||||||
|
}).onclick = async () => {
|
||||||
|
tpl = JSON.parse(JSON.stringify(export_command_templates[tplName]));
|
||||||
|
tpl.name = name;
|
||||||
|
callback(tpl);
|
||||||
|
this.close();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
const { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RenemeModal extends Modal {
|
||||||
|
private readonly settingTab: ExportSettingTab;
|
||||||
|
private readonly setting: ExportSetting;
|
||||||
|
private readonly callback: (name: string) => void;
|
||||||
|
get lang() {
|
||||||
|
return this.settingTab.lang;
|
||||||
|
}
|
||||||
|
constructor(app: App, settingTab: ExportSettingTab, setting: ExportSetting, callback: (name: string) => void) {
|
||||||
|
super(app);
|
||||||
|
this.settingTab = settingTab;
|
||||||
|
this.setting = setting;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpen() {
|
||||||
|
const { contentEl, titleEl, lang, setting } = this;
|
||||||
|
titleEl.setText(lang.settingTab.rename);
|
||||||
|
|
||||||
|
let name = setting.name;
|
||||||
|
|
||||||
|
new Setting(contentEl).setName(lang.settingTab.name).addText(cb => {
|
||||||
|
cb.setValue(setting.name).onChange(v => (name = v));
|
||||||
|
});
|
||||||
|
|
||||||
|
contentEl.createEl('div', { cls: ['modal-button-container'], parent: contentEl }, el => {
|
||||||
|
el.createEl('button', {
|
||||||
|
text: lang.settingTab.save,
|
||||||
|
cls: ['mod-cta'],
|
||||||
|
parent: el,
|
||||||
|
}).onclick = async () => {
|
||||||
|
// success
|
||||||
|
this.callback(name);
|
||||||
|
this.close();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
const { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TOnChangingHandler<T extends object, K extends keyof T> = (value: T[K], key: K, target: T) => boolean;
|
||||||
|
type TOnChangedHandler<T extends object, K extends keyof T> = (value: T[K], key: K, target: T) => void;
|
||||||
|
|
||||||
|
export class Watcher<T extends object> {
|
||||||
|
onChanging: { [k in keyof T]?: TOnChangingHandler<T, keyof T>[] };
|
||||||
|
onChanged: { [k in keyof T]?: TOnChangedHandler<T, keyof T>[] };
|
||||||
|
private readonly _onChangingCallback: TOnChangingHandler<T, keyof T>;
|
||||||
|
private readonly _onChangedCallback: TOnChangedHandler<T, keyof T>;
|
||||||
|
constructor(options?: { onChangingCallback?: TOnChangingHandler<T, keyof T>; onChangedCallback?: TOnChangedHandler<T, keyof T> }) {
|
||||||
|
this.onChanging = {};
|
||||||
|
this.onChanged = {};
|
||||||
|
this._onChangingCallback = options?.onChangingCallback ?? (() => true);
|
||||||
|
this._onChangedCallback = options?.onChangedCallback ?? (() => void 0);
|
||||||
|
}
|
||||||
|
as<T extends object>(): Watcher<T> {
|
||||||
|
return this as unknown as Watcher<T>;
|
||||||
|
}
|
||||||
|
watchOnChanging<K extends keyof T>(key: K, handler: TOnChangingHandler<T, K>): void {
|
||||||
|
(this.onChanging[key] ?? (this.onChanging[key] = [])).push(handler);
|
||||||
|
}
|
||||||
|
watchOnChanged<K extends keyof T>(key: K, handler: TOnChangedHandler<T, K>): void {
|
||||||
|
(this.onChanged[key] ?? (this.onChanged[key] = [])).push(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
set<K extends keyof T>(target: T, key: K, value: T[K]): boolean {
|
||||||
|
if (this._onChangingCallback && this._onChangingCallback(value, key, target) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const onChangingHandlers = this.onChanging[key];
|
||||||
|
if (onChangingHandlers) {
|
||||||
|
let invalid = false;
|
||||||
|
for (const h of onChangingHandlers) {
|
||||||
|
if (!h(value, key, target)) {
|
||||||
|
invalid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (invalid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default behavior to store the value
|
||||||
|
target[key] = value;
|
||||||
|
|
||||||
|
const onChangedHandlers = this.onChanged[key];
|
||||||
|
if (onChangedHandlers) {
|
||||||
|
for (const h of onChangedHandlers) {
|
||||||
|
try {
|
||||||
|
h(value, key, target);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._onChangedCallback) {
|
||||||
|
this._onChangedCallback(value, key, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicate success
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fireChanged(target: T) {
|
||||||
|
for (const key of Object.keys(this.onChanged)) {
|
||||||
|
const k = key as keyof T;
|
||||||
|
const onChangedHandlers = this.onChanged[k];
|
||||||
|
if (onChangedHandlers) {
|
||||||
|
for (const h of onChangedHandlers) {
|
||||||
|
try {
|
||||||
|
h(target[k], k, target);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setVisible(el: Element, visible: boolean) {
|
||||||
|
if (visible) {
|
||||||
|
el.removeAttribute('hidden');
|
||||||
|
} else {
|
||||||
|
el.setAttribute('hidden', '');
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setTooltip(el: Element, tooltip?: string) {
|
||||||
|
if (tooltip && tooltip.trim() != '') {
|
||||||
|
el.setAttribute('title', tooltip);
|
||||||
|
} else {
|
||||||
|
el.removeAttribute('title');
|
||||||
|
}
|
||||||
|
return el;
|
||||||
|
}
|
110
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/message_box.ts
vendored
Normal file
110
.obsidian/plugins/obsidian-enhancing-export-main/src/ui/message_box.ts
vendored
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import { App, Modal } from 'obsidian';
|
||||||
|
import lang, { Lang } from '../lang';
|
||||||
|
|
||||||
|
export interface MessageBoxOptions {
|
||||||
|
message: string;
|
||||||
|
title?: string;
|
||||||
|
buttons: 'Yes' | 'YesNo' | 'Ok' | 'OkCancel';
|
||||||
|
buttonsLabel?: {
|
||||||
|
yes?: string;
|
||||||
|
no?: string;
|
||||||
|
ok?: string;
|
||||||
|
cancel?: string;
|
||||||
|
};
|
||||||
|
buttonsClass?: {
|
||||||
|
yes?: string;
|
||||||
|
no?: string;
|
||||||
|
ok?: string;
|
||||||
|
cancel?: string;
|
||||||
|
};
|
||||||
|
callback?: {
|
||||||
|
yes?: () => void;
|
||||||
|
no?: () => void;
|
||||||
|
ok?: () => void;
|
||||||
|
cancel?: () => void;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MessageBox extends Modal {
|
||||||
|
readonly options: MessageBoxOptions;
|
||||||
|
readonly lang: Lang;
|
||||||
|
|
||||||
|
constructor(app: App, message: string);
|
||||||
|
constructor(app: App, message: string, title?: string);
|
||||||
|
constructor(app: App, options: MessageBoxOptions);
|
||||||
|
constructor(app: App, options: MessageBoxOptions | string, title?: string) {
|
||||||
|
super(app);
|
||||||
|
this.options = typeof options === 'string' ? { message: options, buttons: 'Ok', title } : options;
|
||||||
|
this.lang = lang.current;
|
||||||
|
}
|
||||||
|
onOpen(): void {
|
||||||
|
const {
|
||||||
|
titleEl,
|
||||||
|
contentEl,
|
||||||
|
lang,
|
||||||
|
options: { message, title, buttons, callback, buttonsLabel: label, buttonsClass },
|
||||||
|
} = this;
|
||||||
|
if (title) {
|
||||||
|
titleEl.setText(title);
|
||||||
|
}
|
||||||
|
contentEl.createDiv({ text: message });
|
||||||
|
switch (buttons) {
|
||||||
|
case 'Yes':
|
||||||
|
contentEl.createEl('div', { cls: ['modal-button-container'], parent: contentEl }, el => {
|
||||||
|
el.createEl('button', {
|
||||||
|
text: label?.yes ?? lang.messageBox.yes,
|
||||||
|
cls: ['mod-cta', buttonsClass?.yes],
|
||||||
|
parent: el,
|
||||||
|
}).onclick = () => this.call(callback?.yes);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'YesNo':
|
||||||
|
contentEl.createEl('div', { cls: ['modal-button-container'], parent: contentEl }, el => {
|
||||||
|
el.createEl('button', {
|
||||||
|
text: label?.yes ?? lang.messageBox.yes,
|
||||||
|
cls: ['mod-cta', buttonsClass?.yes],
|
||||||
|
parent: el,
|
||||||
|
}).onclick = () => this.call(callback?.yes);
|
||||||
|
el.createEl('button', {
|
||||||
|
text: label?.no ?? lang.messageBox.no,
|
||||||
|
cls: ['mod-cta', buttonsClass?.no],
|
||||||
|
parent: el,
|
||||||
|
}).onclick = () => this.call(callback?.no);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'Ok':
|
||||||
|
contentEl.createEl('div', { cls: ['modal-button-container'], parent: contentEl }, el => {
|
||||||
|
el.createEl('button', {
|
||||||
|
text: label?.ok ?? lang.messageBox.ok,
|
||||||
|
cls: ['mod-cta', buttonsClass?.no],
|
||||||
|
parent: el,
|
||||||
|
}).onclick = () => this.call(callback?.ok);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'OkCancel':
|
||||||
|
contentEl.createEl('div', { cls: ['modal-button-container'], parent: contentEl }, el => {
|
||||||
|
el.createEl('button', {
|
||||||
|
text: label?.ok ?? lang.messageBox.ok,
|
||||||
|
cls: ['mod-cta', buttonsClass?.ok],
|
||||||
|
parent: el,
|
||||||
|
}).onclick = () => this.call(callback?.ok);
|
||||||
|
el.createEl('button', {
|
||||||
|
text: label?.cancel ?? lang.messageBox.cancel,
|
||||||
|
cls: ['mod-cta', buttonsClass?.cancel],
|
||||||
|
parent: el,
|
||||||
|
}).onclick = () => this.call(callback?.cancel);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private call(callback?: () => void): void {
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
onClose() {
|
||||||
|
const { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
}
|
||||||
|
}
|
127
.obsidian/plugins/obsidian-enhancing-export-main/src/utils.ts
vendored
Normal file
127
.obsidian/plugins/obsidian-enhancing-export-main/src/utils.ts
vendored
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import { ExecOptions, exec as node_exec } from 'child_process';
|
||||||
|
import process from 'process';
|
||||||
|
|
||||||
|
export type PlatformKey = typeof process.platform | '*';
|
||||||
|
|
||||||
|
export type PlatformValue<T> = { [k in PlatformKey]?: T };
|
||||||
|
|
||||||
|
export function setPlatformValue<T>(obj: PlatformValue<T>, value: T, platform?: PlatformKey | PlatformKey[]): PlatformValue<T> {
|
||||||
|
if (typeof value === 'string' && value.trim() === '') {
|
||||||
|
value = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (platform instanceof Array) {
|
||||||
|
return platform.reduce((o, p) => setPlatformValue(o, value, p), obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
platform ??= process.platform;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...(obj ?? {}),
|
||||||
|
[platform]: value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlatformValue<T>(obj: PlatformValue<T>, platform?: PlatformKey): T {
|
||||||
|
obj ??= {};
|
||||||
|
const val = obj[platform ?? process.platform];
|
||||||
|
const all = obj['*'];
|
||||||
|
if (all && typeof all === 'object') {
|
||||||
|
return Object.assign({}, all, val);
|
||||||
|
}
|
||||||
|
return val ?? all;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
export function strTpl(strings: TemplateStringsArray, ...keys: number[]): (...values: any[]) => string {
|
||||||
|
return function (...values) {
|
||||||
|
const dict = values[values.length - 1] || {};
|
||||||
|
const result = [strings[0]];
|
||||||
|
keys.forEach(function (key, i) {
|
||||||
|
const value = Number.isInteger(key) ? values[key] : dict[key];
|
||||||
|
result.push(value, strings[i + 1]);
|
||||||
|
});
|
||||||
|
return result.join('');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function exec(cmd: string, options?: ExecOptions): Promise<string> {
|
||||||
|
options = options ?? {};
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
node_exec(cmd, options, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
console.error(stdout, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stderr && stderr !== '') {
|
||||||
|
reject(stderr);
|
||||||
|
console.error(stdout, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (stdout?.trim().length === 0 && '1' === localStorage.getItem('debug-plugin')) {
|
||||||
|
console.log(stdout);
|
||||||
|
}
|
||||||
|
resolve(stdout);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function joinEnvPath(...paths: string[]) {
|
||||||
|
switch (process.platform) {
|
||||||
|
case 'win32':
|
||||||
|
return paths.join(';');
|
||||||
|
default:
|
||||||
|
return paths.join(':');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function trimQuotes(s: string) {
|
||||||
|
return (s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'")) ? s.substring(1, s.length - 1) : s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* render template
|
||||||
|
* @example
|
||||||
|
* renderTemplate('Hi, ${name}', { name: 'John' }) // returns 'Hi, John'
|
||||||
|
* @param template
|
||||||
|
* @param variables
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function renderTemplate(template: string, variables: Record<string, unknown> = {}): string {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
const keys = Object.keys(variables).filter(isVarName) as Array<keyof typeof variables>;
|
||||||
|
const values = keys.map(k => variables[k]);
|
||||||
|
return new Function(...keys, `{ return \`${template.replaceAll('\\', '\\\\')}\` }`).bind(variables)(...values);
|
||||||
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof ReferenceError && e.message.endsWith('is not defined')) {
|
||||||
|
const name = e.message.substring(0, e.message.indexOf(' '));
|
||||||
|
const value =
|
||||||
|
Object.keys(variables)
|
||||||
|
.filter(n => n.toLowerCase() === name.toLowerCase())
|
||||||
|
.map(n => variables[n])[0] ?? `\${${name}}`;
|
||||||
|
variables[name] = value;
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isVarName = (str: string) => {
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str.trim() !== str) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
new Function(str, 'var ' + str);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
1
.obsidian/plugins/obsidian-enhancing-export-main/src/vite-env.d.ts
vendored
Normal file
1
.obsidian/plugins/obsidian-enhancing-export-main/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
38
.obsidian/plugins/obsidian-enhancing-export-main/tests/common.ts
vendored
Normal file
38
.obsidian/plugins/obsidian-enhancing-export-main/tests/common.ts
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import os from 'os';
|
||||||
|
import { exec as execSync, ExecException } from 'child_process';
|
||||||
|
import { readFile } from 'fs/promises';
|
||||||
|
|
||||||
|
|
||||||
|
export async function exec(cmd: string, options: { lineSeparator: '\n' | '\r\n' | '\r' }): Promise<string> {
|
||||||
|
function lineSeparator(s?: string, ls?: '\n' | '\r\n' | '\r') {
|
||||||
|
if (!s || os.EOL === ls || !ls) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return s.replaceAll(os.EOL, ls);
|
||||||
|
}
|
||||||
|
return await new Promise((resolve, reject) => {
|
||||||
|
execSync(cmd, { encoding: 'utf-8', cwd: module.path }, (e: ExecException, stdout: string, stderr: string) => {
|
||||||
|
if (!e) {
|
||||||
|
resolve(lineSeparator(stdout, options?.lineSeparator));
|
||||||
|
} else {
|
||||||
|
reject(lineSeparator(stderr, options?.lineSeparator));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const testConversion = async (name: string, filter?: string) => {
|
||||||
|
process.chdir(module.path);
|
||||||
|
const input_file = `./markdowns/${name}.md`;
|
||||||
|
const expect_out = `./markdowns/${name}.out`;
|
||||||
|
let pandoc: string;
|
||||||
|
if (filter) {
|
||||||
|
const lua_script = `../lua/${filter}.lua`;
|
||||||
|
pandoc = `pandoc -s -L ${lua_script} -t native -f markdown "${input_file}" -o -`;
|
||||||
|
} else {
|
||||||
|
pandoc = `pandoc -s -t native -f markdown "${input_file}" -o -`;
|
||||||
|
}
|
||||||
|
const ret = await exec(pandoc, { lineSeparator: '\n'});
|
||||||
|
expect(ret).toBe(await readFile(expect_out, { encoding: 'utf-8', flag: 'r' }));
|
||||||
|
};
|
15
.obsidian/plugins/obsidian-enhancing-export-main/tests/internalLink.spec.ts
vendored
Normal file
15
.obsidian/plugins/obsidian-enhancing-export-main/tests/internalLink.spec.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
import { testConversion } from './common';
|
||||||
|
|
||||||
|
|
||||||
|
test('test basic internal link block parsing', async () => {
|
||||||
|
await testConversion('internal-link-basic', 'markdown');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test complex internal link block parsing', async () => {
|
||||||
|
await testConversion('internal-link-bullet', 'markdown');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test basic internal link with description', async () => {
|
||||||
|
await testConversion('internal-link-described', 'markdown');
|
||||||
|
});
|
1
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-basic.md
vendored
Normal file
1
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-basic.md
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
[[#Some complex section]]
|
14
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-basic.out
vendored
Normal file
14
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-basic.out
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
Pandoc
|
||||||
|
Meta { unMeta = fromList [] }
|
||||||
|
[ Para
|
||||||
|
[ Link
|
||||||
|
( "" , [] , [] )
|
||||||
|
[ Str "Some"
|
||||||
|
, Space
|
||||||
|
, Str "complex"
|
||||||
|
, Space
|
||||||
|
, Str "section"
|
||||||
|
]
|
||||||
|
( "#Some-complex-section" , "" )
|
||||||
|
]
|
||||||
|
]
|
3
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-bullet.md
vendored
Normal file
3
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-bullet.md
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
- potato
|
||||||
|
- [[#more complex vegetables]]
|
||||||
|
- cucumber
|
19
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-bullet.out
vendored
Normal file
19
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-bullet.out
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Pandoc
|
||||||
|
Meta { unMeta = fromList [] }
|
||||||
|
[ BulletList
|
||||||
|
[ [ Plain [ Str "potato" ] ]
|
||||||
|
, [ Plain
|
||||||
|
[ Link
|
||||||
|
( "" , [] , [] )
|
||||||
|
[ Str "more"
|
||||||
|
, Space
|
||||||
|
, Str "complex"
|
||||||
|
, Space
|
||||||
|
, Str "vegetables"
|
||||||
|
]
|
||||||
|
( "#more-complex-vegetables" , "" )
|
||||||
|
]
|
||||||
|
]
|
||||||
|
, [ Plain [ Str "cucumber" ] ]
|
||||||
|
]
|
||||||
|
]
|
1
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-described.md
vendored
Normal file
1
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-described.md
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
[[#Some complex section|with custom description]]
|
14
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-described.out
vendored
Normal file
14
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/internal-link-described.out
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
Pandoc
|
||||||
|
Meta { unMeta = fromList [] }
|
||||||
|
[ Para
|
||||||
|
[ Link
|
||||||
|
( "" , [] , [] )
|
||||||
|
[ Str "with"
|
||||||
|
, Space
|
||||||
|
, Str "custom"
|
||||||
|
, Space
|
||||||
|
, Str "description"
|
||||||
|
]
|
||||||
|
( "#Some-complex-section" , "" )
|
||||||
|
]
|
||||||
|
]
|
41
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block-01-no-empty-lines.md
vendored
Normal file
41
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block-01-no-empty-lines.md
vendored
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
$$
|
||||||
|
\left(\begin{array}{c}
|
||||||
|
\hat{F}_{1,i,j}\\ \hat{F}_{2,i,j} \\ \vdots \\ \hat{F}_{C-1,i,j} \\ \hat{F}_{C,i,j}
|
||||||
|
\end{array}\right)
|
||||||
|
=
|
||||||
|
\begin{pmatrix}
|
||||||
|
\frac{\gamma_1}{\sqrt{\hat{\sigma}^2_1}+\epsilon} & 0 & \cdots & &0
|
||||||
|
\\
|
||||||
|
0 && \frac{\gamma_2}{\sqrt{\hat{\sigma}^2_2}+\epsilon} & & & &
|
||||||
|
\\
|
||||||
|
\vdots && \ddots && \vdots
|
||||||
|
\\
|
||||||
|
&&& \frac{\gamma_{C-1}}{\sqrt{\hat{\sigma}^2_{C-1}+\epsilon}} & 0
|
||||||
|
\\
|
||||||
|
0 && \cdots &0 & \frac{\gamma_C}{\sqrt{\hat{\sigma}^2_{C}+\epsilon}}
|
||||||
|
\end{pmatrix}
|
||||||
|
\cdot
|
||||||
|
\begin{pmatrix}
|
||||||
|
F_{1,i,j}
|
||||||
|
\\
|
||||||
|
F_{2,i,j}
|
||||||
|
\\
|
||||||
|
\vdots
|
||||||
|
\\
|
||||||
|
F_{C-1,i,j}
|
||||||
|
\\
|
||||||
|
F_{C,i,j}
|
||||||
|
\end{pmatrix}
|
||||||
|
+
|
||||||
|
\begin{pmatrix}
|
||||||
|
\beta_1-\gamma_1\frac{\hat{\mu}_1}{\sqrt{\hat{\sigma}^2_1+\epsilon}}
|
||||||
|
\\
|
||||||
|
\beta_2-\gamma_2\frac{\hat{\mu}_2}{\sqrt{\hat{\sigma}^2_2+\epsilon}}
|
||||||
|
\\
|
||||||
|
\vdots
|
||||||
|
\\
|
||||||
|
\beta_{C-1}-\gamma_{C-1}\frac{\hat{\mu}_{C-1}}{\sqrt{\hat{\sigma}^2_{C-1}+\epsilon}}
|
||||||
|
\\
|
||||||
|
\beta_C-\gamma_C\frac{\hat{\mu}_C}{\sqrt{\hat{\sigma}^2_C+\epsilon}}
|
||||||
|
\end{pmatrix}
|
||||||
|
$$
|
@ -0,0 +1,8 @@
|
|||||||
|
Pandoc
|
||||||
|
Meta { unMeta = fromList [] }
|
||||||
|
[ Para
|
||||||
|
[ Math
|
||||||
|
DisplayMath
|
||||||
|
"\n\\left(\\begin{array}{c}\n\\hat{F}_{1,i,j}\\\\ \\hat{F}_{2,i,j} \\\\ \\vdots \\\\ \\hat{F}_{C-1,i,j} \\\\ \\hat{F}_{C,i,j}\n\\end{array}\\right)\n=\n\\begin{pmatrix}\n\\frac{\\gamma_1}{\\sqrt{\\hat{\\sigma}^2_1}+\\epsilon} & 0 & \\cdots & &0\n\\\\\n0 && \\frac{\\gamma_2}{\\sqrt{\\hat{\\sigma}^2_2}+\\epsilon} & & & &\n\\\\\n\\vdots && \\ddots && \\vdots\n\\\\\n&&& \\frac{\\gamma_{C-1}}{\\sqrt{\\hat{\\sigma}^2_{C-1}+\\epsilon}} & 0\n\\\\\n0 && \\cdots &0 & \\frac{\\gamma_C}{\\sqrt{\\hat{\\sigma}^2_{C}+\\epsilon}}\n\\end{pmatrix}\n\\cdot\n\\begin{pmatrix}\nF_{1,i,j}\n\\\\\nF_{2,i,j}\n\\\\\n\\vdots\n\\\\\nF_{C-1,i,j}\n\\\\\nF_{C,i,j}\n\\end{pmatrix}\n+\n\\begin{pmatrix}\n\\beta_1-\\gamma_1\\frac{\\hat{\\mu}_1}{\\sqrt{\\hat{\\sigma}^2_1+\\epsilon}}\n\\\\\n\\beta_2-\\gamma_2\\frac{\\hat{\\mu}_2}{\\sqrt{\\hat{\\sigma}^2_2+\\epsilon}}\n\\\\\n\\vdots\n\\\\\n\\beta_{C-1}-\\gamma_{C-1}\\frac{\\hat{\\mu}_{C-1}}{\\sqrt{\\hat{\\sigma}^2_{C-1}+\\epsilon}}\n\\\\\n\\beta_C-\\gamma_C\\frac{\\hat{\\mu}_C}{\\sqrt{\\hat{\\sigma}^2_C+\\epsilon}}\n\\end{pmatrix} \n"
|
||||||
|
]
|
||||||
|
]
|
51
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block-01.md
vendored
Normal file
51
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block-01.md
vendored
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
$$
|
||||||
|
\left(\begin{array}{c}
|
||||||
|
\hat{F}_{1,i,j}\\ \hat{F}_{2,i,j} \\ \vdots \\ \hat{F}_{C-1,i,j} \\ \hat{F}_{C,i,j}
|
||||||
|
\end{array}\right)
|
||||||
|
|
||||||
|
=
|
||||||
|
|
||||||
|
\begin{pmatrix}
|
||||||
|
\frac{\gamma_1}{\sqrt{\hat{\sigma}^2_1}+\epsilon} & 0 & \cdots & &0
|
||||||
|
\\
|
||||||
|
0 && \frac{\gamma_2}{\sqrt{\hat{\sigma}^2_2}+\epsilon} & & & &
|
||||||
|
\\
|
||||||
|
\vdots && \ddots && \vdots
|
||||||
|
\\
|
||||||
|
&&& \frac{\gamma_{C-1}}{\sqrt{\hat{\sigma}^2_{C-1}+\epsilon}} & 0
|
||||||
|
\\
|
||||||
|
0 && \cdots &0 & \frac{\gamma_C}{\sqrt{\hat{\sigma}^2_{C}+\epsilon}}
|
||||||
|
|
||||||
|
\end{pmatrix}
|
||||||
|
|
||||||
|
|
||||||
|
\cdot
|
||||||
|
|
||||||
|
\begin{pmatrix}
|
||||||
|
F_{1,i,j}
|
||||||
|
\\
|
||||||
|
F_{2,i,j}
|
||||||
|
\\
|
||||||
|
\vdots
|
||||||
|
\\
|
||||||
|
F_{C-1,i,j}
|
||||||
|
\\
|
||||||
|
|
||||||
|
F_{C,i,j}
|
||||||
|
\end{pmatrix}
|
||||||
|
|
||||||
|
+
|
||||||
|
|
||||||
|
\begin{pmatrix}
|
||||||
|
\beta_1-\gamma_1\frac{\hat{\mu}_1}{\sqrt{\hat{\sigma}^2_1+\epsilon}}
|
||||||
|
\\
|
||||||
|
\beta_2-\gamma_2\frac{\hat{\mu}_2}{\sqrt{\hat{\sigma}^2_2+\epsilon}}
|
||||||
|
\\
|
||||||
|
\vdots
|
||||||
|
\\
|
||||||
|
\beta_{C-1}-\gamma_{C-1}\frac{\hat{\mu}_{C-1}}{\sqrt{\hat{\sigma}^2_{C-1}+\epsilon}}
|
||||||
|
\\
|
||||||
|
\beta_C-\gamma_C\frac{\hat{\mu}_C}{\sqrt{\hat{\sigma}^2_C+\epsilon}}
|
||||||
|
|
||||||
|
\end{pmatrix}
|
||||||
|
$$
|
28
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block-01.orig
vendored
Normal file
28
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block-01.orig
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
Pandoc
|
||||||
|
Meta { unMeta = fromList [] }
|
||||||
|
[ Plain
|
||||||
|
[ Str "$$"
|
||||||
|
, SoftBreak
|
||||||
|
, RawInline (Format "tex") "\\left"
|
||||||
|
, Str "("
|
||||||
|
]
|
||||||
|
, RawBlock
|
||||||
|
(Format "tex")
|
||||||
|
"\\begin{array}{c}\n\\hat{F}_{1,i,j}\\\\ \\hat{F}_{2,i,j} \\\\ \\vdots \\\\ \\hat{F}_{C-1,i,j} \\\\ \\hat{F}_{C,i,j}\n\\end{array}"
|
||||||
|
, Header
|
||||||
|
1
|
||||||
|
( "section" , [] , [] )
|
||||||
|
[ RawInline (Format "tex") "\\right" , Str ")" ]
|
||||||
|
, RawBlock
|
||||||
|
(Format "tex")
|
||||||
|
"\\begin{pmatrix}\n\\frac{\\gamma_1}{\\sqrt{\\hat{\\sigma}^2_1}+\\epsilon} & 0 & \\cdots & &0\n\\\\\n0 && \\frac{\\gamma_2}{\\sqrt{\\hat{\\sigma}^2_2}+\\epsilon} & & & &\n\\\\\n\\vdots && \\ddots && \\vdots\n\\\\\n&&& \\frac{\\gamma_{C-1}}{\\sqrt{\\hat{\\sigma}^2_{C-1}+\\epsilon}} & 0\n\\\\\n0 && \\cdots &0 & \\frac{\\gamma_C}{\\sqrt{\\hat{\\sigma}^2_{C}+\\epsilon}}\n\n\\end{pmatrix}"
|
||||||
|
, RawBlock (Format "tex") "\\cdot"
|
||||||
|
, RawBlock
|
||||||
|
(Format "tex")
|
||||||
|
"\\begin{pmatrix}\nF_{1,i,j}\n\\\\\nF_{2,i,j}\n\\\\\n\\vdots\n\\\\\nF_{C-1,i,j}\n\\\\\n\nF_{C,i,j}\n\\end{pmatrix}"
|
||||||
|
, BulletList [ [] ]
|
||||||
|
, RawBlock
|
||||||
|
(Format "tex")
|
||||||
|
"\\begin{pmatrix}\n\\beta_1-\\gamma_1\\frac{\\hat{\\mu}_1}{\\sqrt{\\hat{\\sigma}^2_1+\\epsilon}}\n\\\\\n\\beta_2-\\gamma_2\\frac{\\hat{\\mu}_2}{\\sqrt{\\hat{\\sigma}^2_2+\\epsilon}}\n\\\\\n\\vdots\n\\\\\n\\beta_{C-1}-\\gamma_{C-1}\\frac{\\hat{\\mu}_{C-1}}{\\sqrt{\\hat{\\sigma}^2_{C-1}+\\epsilon}}\n\\\\\n\\beta_C-\\gamma_C\\frac{\\hat{\\mu}_C}{\\sqrt{\\hat{\\sigma}^2_C+\\epsilon}}\n\n\\end{pmatrix}"
|
||||||
|
, Para [ Str "$$" ]
|
||||||
|
]
|
11
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block-01.out
vendored
Normal file
11
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block-01.out
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Pandoc
|
||||||
|
Meta { unMeta = fromList [] }
|
||||||
|
[ Plain []
|
||||||
|
, Para []
|
||||||
|
, Para []
|
||||||
|
, Para
|
||||||
|
[ Math
|
||||||
|
DisplayMath
|
||||||
|
"\n\\left(\\begin{array}{c}\n\\hat{F}_{1,i,j}\\\\ \\hat{F}_{2,i,j} \\\\ \\vdots \\\\ \\hat{F}_{C-1,i,j} \\\\ \\hat{F}_{C,i,j}\n\\end{array}\\right)=\\begin{pmatrix}\n\\frac{\\gamma_1}{\\sqrt{\\hat{\\sigma}^2_1}+\\epsilon} & 0 & \\cdots & &0\n\\\\\n0 && \\frac{\\gamma_2}{\\sqrt{\\hat{\\sigma}^2_2}+\\epsilon} & & & &\n\\\\\n\\vdots && \\ddots && \\vdots\n\\\\\n&&& \\frac{\\gamma_{C-1}}{\\sqrt{\\hat{\\sigma}^2_{C-1}+\\epsilon}} & 0\n\\\\\n0 && \\cdots &0 & \\frac{\\gamma_C}{\\sqrt{\\hat{\\sigma}^2_{C}+\\epsilon}}\n\n\\end{pmatrix}\\cdot\\begin{pmatrix}\nF_{1,i,j}\n\\\\\nF_{2,i,j}\n\\\\\n\\vdots\n\\\\\nF_{C-1,i,j}\n\\\\\n\nF_{C,i,j}\n\\end{pmatrix}\\begin{pmatrix}\n\\beta_1-\\gamma_1\\frac{\\hat{\\mu}_1}{\\sqrt{\\hat{\\sigma}^2_1+\\epsilon}}\n\\\\\n\\beta_2-\\gamma_2\\frac{\\hat{\\mu}_2}{\\sqrt{\\hat{\\sigma}^2_2+\\epsilon}}\n\\\\\n\\vdots\n\\\\\n\\beta_{C-1}-\\gamma_{C-1}\\frac{\\hat{\\mu}_{C-1}}{\\sqrt{\\hat{\\sigma}^2_{C-1}+\\epsilon}}\n\\\\\n\\beta_C-\\gamma_C\\frac{\\hat{\\mu}_C}{\\sqrt{\\hat{\\sigma}^2_C+\\epsilon}}\n\n\\end{pmatrix}\n"
|
||||||
|
]
|
||||||
|
]
|
13
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block.md
vendored
Normal file
13
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block.md
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
$$
|
||||||
|
\begin{align*}
|
||||||
|
\begin{rcases}
|
||||||
|
\lambda_{1}(a_{11}^{*}, \ldots, a_{22}^{*}) < 0 \\
|
||||||
|
\lambda_{2}(a_{11}^{*}, \ldots, a_{22}^{*}) < 0 \\
|
||||||
|
\end{rcases} & \Rightarrow \text{stable knot} \\
|
||||||
|
|
||||||
|
\begin{rcases}
|
||||||
|
\lambda_{1}(a_{11}^{*}, \ldots, a_{22}^{*}) > 0 \\
|
||||||
|
\lambda_{2}(a_{11}^{*}, \ldots, a_{22}^{*}) < 0 \\
|
||||||
|
\end{rcases} & \Rightarrow \text{saddle}
|
||||||
|
\end{align*}
|
||||||
|
$$
|
12
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block.orig
vendored
Normal file
12
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block.orig
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Pandoc
|
||||||
|
Meta { unMeta = fromList [] }
|
||||||
|
[ Para
|
||||||
|
[ Str "$$"
|
||||||
|
, SoftBreak
|
||||||
|
, RawInline
|
||||||
|
(Format "tex")
|
||||||
|
"\\begin{align*}\n\\begin{rcases}\n\\lambda_{1}(a_{11}^{*}, \\ldots, a_{22}^{*}) < 0 \\\\\n\\lambda_{2}(a_{11}^{*}, \\ldots, a_{22}^{*}) < 0 \\\\\n\\end{rcases} & \\Rightarrow \\text{stable knot} \\\\\n\n \\begin{rcases}\n \\lambda_{1}(a_{11}^{*}, \\ldots, a_{22}^{*}) > 0 \\\\\n \\lambda_{2}(a_{11}^{*}, \\ldots, a_{22}^{*}) < 0 \\\\\n \\end{rcases} & \\Rightarrow \\text{saddle}\n\\end{align*}"
|
||||||
|
, SoftBreak
|
||||||
|
, Str "$$"
|
||||||
|
]
|
||||||
|
]
|
8
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block.out
vendored
Normal file
8
.obsidian/plugins/obsidian-enhancing-export-main/tests/markdowns/math-block.out
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
Pandoc
|
||||||
|
Meta { unMeta = fromList [] }
|
||||||
|
[ Para
|
||||||
|
[ Math
|
||||||
|
DisplayMath
|
||||||
|
"\n\\begin{align*}\n\\begin{rcases}\n\\lambda_{1}(a_{11}^{*}, \\ldots, a_{22}^{*}) < 0 \\\\\n\\lambda_{2}(a_{11}^{*}, \\ldots, a_{22}^{*}) < 0 \\\\\n\\end{rcases} & \\Rightarrow \\text{stable knot} \\\\\n\n \\begin{rcases}\n \\lambda_{1}(a_{11}^{*}, \\ldots, a_{22}^{*}) > 0 \\\\\n \\lambda_{2}(a_{11}^{*}, \\ldots, a_{22}^{*}) < 0 \\\\\n \\end{rcases} & \\Rightarrow \\text{saddle}\n\\end{align*}\n"
|
||||||
|
]
|
||||||
|
]
|
23
.obsidian/plugins/obsidian-enhancing-export-main/tests/mathBlock.spec.ts
vendored
Normal file
23
.obsidian/plugins/obsidian-enhancing-export-main/tests/mathBlock.spec.ts
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { testConversion } from './common';
|
||||||
|
|
||||||
|
|
||||||
|
test('test math-block parsing', async () => {
|
||||||
|
await testConversion('math-block', 'math_block');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('test math-block-01 parsing', async () => {
|
||||||
|
await testConversion('math-block-01', 'math_block');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test math-block-01-no-empty-lines parsing', async () => {
|
||||||
|
await testConversion('math-block-01-no-empty-lines', 'math_block');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test math-block-01-no-empty-lines parsing filter off', async () => {
|
||||||
|
await testConversion('math-block-01-no-empty-lines');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
7
.obsidian/plugins/obsidian-enhancing-export-main/tests/pandocVersion.spec.ts
vendored
Normal file
7
.obsidian/plugins/obsidian-enhancing-export-main/tests/pandocVersion.spec.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import pandoc from '../src/pandoc';
|
||||||
|
|
||||||
|
|
||||||
|
test('test get pandoc version', async () => {
|
||||||
|
const out = await pandoc.getVersion();
|
||||||
|
expect(out.compare('3.1.5')).toBe(1);
|
||||||
|
});
|
26
.obsidian/plugins/obsidian-enhancing-export-main/tests/platformValue.spec.ts
vendored
Normal file
26
.obsidian/plugins/obsidian-enhancing-export-main/tests/platformValue.spec.ts
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { getPlatformValue, setPlatformValue } from '../src/utils';
|
||||||
|
|
||||||
|
|
||||||
|
test('test get set platformValue 1', async () => {
|
||||||
|
const val = setPlatformValue({}, 'abc');
|
||||||
|
expect(getPlatformValue(val)).toBe('abc');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test get set platformValue 2', async () => {
|
||||||
|
const val = setPlatformValue({}, 'abc', '*');
|
||||||
|
expect(getPlatformValue(val)).toBe('abc');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test get set platformValue 3', async () => {
|
||||||
|
let val = setPlatformValue<Record<string, string>>({}, { 'a': 'x' }, '*');
|
||||||
|
val = setPlatformValue(val, { 'b': 'y' });
|
||||||
|
expect(getPlatformValue(val)).toStrictEqual({ 'a': 'x', 'b': 'y' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test set platformValue on multi platform', async () => {
|
||||||
|
const val = setPlatformValue<Record<string, string>>({}, { 'a': 'x' }, ['win32', 'darwin']);
|
||||||
|
expect(val).toStrictEqual({
|
||||||
|
'win32': { 'a': 'x' },
|
||||||
|
'darwin': { 'a': 'x' }
|
||||||
|
});
|
||||||
|
});
|
32
.obsidian/plugins/obsidian-enhancing-export-main/tests/renderTemplate.spec.ts
vendored
Normal file
32
.obsidian/plugins/obsidian-enhancing-export-main/tests/renderTemplate.spec.ts
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { renderTemplate } from '../src/utils';
|
||||||
|
|
||||||
|
|
||||||
|
test('test Template rendering', async () => {
|
||||||
|
const out = renderTemplate('s${luaDir}e', { luaDir: 'w123' });
|
||||||
|
expect(out).toBe('sw123e');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test Template rendering 2', async () => {
|
||||||
|
const out = renderTemplate('${HOME}', {
|
||||||
|
'HOME': 'C:\\Users\\Admin',
|
||||||
|
'CommonProgramFiles(x86)': 'C:\\Program Files (x86)\\Common Files',
|
||||||
|
});
|
||||||
|
expect(out).toBe('C:\\Users\\Admin');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('test Template rendering options.textemplate', async () => {
|
||||||
|
expect(renderTemplate('pandoc ${ options.textemplate ? `--template="${options.textemplate}"` : `` }',
|
||||||
|
{ options: { textemplate: 'dissertation.tex' } }))
|
||||||
|
.toBe('pandoc --template="dissertation.tex"');
|
||||||
|
|
||||||
|
expect(renderTemplate('pandoc ${ options.textemplate ? `--template="${options.textemplate}"` : `` }',
|
||||||
|
{ options: { textemplate: null } }))
|
||||||
|
.toBe('pandoc ');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
test('test Template rendering with undefined variable', async () => {
|
||||||
|
expect(renderTemplate('Hi ${user}', { }))
|
||||||
|
.toBe('Hi ${user}');
|
||||||
|
});
|
1210
.obsidian/plugins/obsidian-enhancing-export-main/textemplate/dissertation.tex
vendored
Normal file
1210
.obsidian/plugins/obsidian-enhancing-export-main/textemplate/dissertation.tex
vendored
Normal file
File diff suppressed because it is too large
Load Diff
373
.obsidian/plugins/obsidian-enhancing-export-main/textemplate/neurips.sty
vendored
Normal file
373
.obsidian/plugins/obsidian-enhancing-export-main/textemplate/neurips.sty
vendored
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
% partial rewrite of the LaTeX2e package for submissions to the
|
||||||
|
% Conference on Neural Information Processing Systems (NeurIPS):
|
||||||
|
%
|
||||||
|
% - uses more LaTeX conventions
|
||||||
|
% - line numbers at submission time replaced with aligned numbers from
|
||||||
|
% lineno package
|
||||||
|
% - \nipsfinalcopy replaced with [final] package option
|
||||||
|
% - automatically loads times package for authors
|
||||||
|
% - loads natbib automatically; this can be suppressed with the
|
||||||
|
% [nonatbib] package option
|
||||||
|
% - adds foot line to first page identifying the conference
|
||||||
|
% - adds preprint option for submission to e.g. arXiv
|
||||||
|
% - conference acronym modified
|
||||||
|
%
|
||||||
|
% Roman Garnett (garnett@wustl.edu) and the many authors of
|
||||||
|
% nips15submit_e.sty, including MK and drstrip@sandia
|
||||||
|
%
|
||||||
|
% last revision: March 2023
|
||||||
|
|
||||||
|
\NeedsTeXFormat{LaTeX2e}
|
||||||
|
\ProvidesPackage{neurips}[2023/03/31 NeurIPS 2023 submission/camera-ready style file]
|
||||||
|
|
||||||
|
% declare final option, which creates camera-ready copy
|
||||||
|
\newif\if@neuripsfinal\@neuripsfinalfalse
|
||||||
|
\DeclareOption{final}{
|
||||||
|
\@neuripsfinaltrue
|
||||||
|
}
|
||||||
|
|
||||||
|
% declare nonatbib option, which does not load natbib in case of
|
||||||
|
% package clash (users can pass options to natbib via
|
||||||
|
% \PassOptionsToPackage)
|
||||||
|
\newif\if@natbib\@natbibtrue
|
||||||
|
\DeclareOption{nonatbib}{
|
||||||
|
\@natbibfalse
|
||||||
|
}
|
||||||
|
|
||||||
|
% declare preprint option, which creates a preprint version ready for
|
||||||
|
% upload to, e.g., arXiv
|
||||||
|
\newif\if@preprint\@preprintfalse
|
||||||
|
\DeclareOption{preprint}{
|
||||||
|
\@preprinttrue
|
||||||
|
}
|
||||||
|
|
||||||
|
\ProcessOptions\relax
|
||||||
|
|
||||||
|
% determine whether this is an anonymized submission
|
||||||
|
\newif\if@submission\@submissiontrue
|
||||||
|
\if@neuripsfinal\@submissionfalse\fi
|
||||||
|
\if@preprint\@submissionfalse\fi
|
||||||
|
|
||||||
|
% fonts
|
||||||
|
\renewcommand{\rmdefault}{ptm}
|
||||||
|
\renewcommand{\sfdefault}{phv}
|
||||||
|
|
||||||
|
% change this every year for notice string at bottom
|
||||||
|
\newcommand{\@neuripsordinal}{}
|
||||||
|
\newcommand{\@neuripsyear}{\the\year}
|
||||||
|
\newcommand{\@neuripslocation}{}
|
||||||
|
|
||||||
|
% acknowledgments
|
||||||
|
\usepackage{environ}
|
||||||
|
\newcommand{\acksection}{\section*{Acknowledgments and Disclosure of Funding}}
|
||||||
|
\NewEnviron{ack}{%
|
||||||
|
\acksection
|
||||||
|
\BODY
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
% load natbib unless told otherwise
|
||||||
|
\if@natbib
|
||||||
|
\RequirePackage{natbib}
|
||||||
|
\fi
|
||||||
|
|
||||||
|
% set page geometry
|
||||||
|
\usepackage[verbose=true,letterpaper]{geometry}
|
||||||
|
\AtBeginDocument{
|
||||||
|
\newgeometry{
|
||||||
|
textheight=9in,
|
||||||
|
textwidth=5.5in,
|
||||||
|
top=1in,
|
||||||
|
headheight=12pt,
|
||||||
|
headsep=25pt,
|
||||||
|
footskip=30pt
|
||||||
|
}
|
||||||
|
\@ifpackageloaded{fullpage}
|
||||||
|
{\PackageWarning{neurips_2023}{fullpage package not allowed! Overwriting formatting.}}
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
|
\widowpenalty=10000
|
||||||
|
\clubpenalty=10000
|
||||||
|
\flushbottom
|
||||||
|
\sloppy
|
||||||
|
|
||||||
|
|
||||||
|
% font sizes with reduced leading
|
||||||
|
\renewcommand{\normalsize}{%
|
||||||
|
\@setfontsize\normalsize\@xpt\@xipt
|
||||||
|
\abovedisplayskip 7\p@ \@plus 2\p@ \@minus 5\p@
|
||||||
|
\abovedisplayshortskip \z@ \@plus 3\p@
|
||||||
|
\belowdisplayskip \abovedisplayskip
|
||||||
|
\belowdisplayshortskip 4\p@ \@plus 3\p@ \@minus 3\p@
|
||||||
|
}
|
||||||
|
\normalsize
|
||||||
|
\renewcommand{\small}{%
|
||||||
|
\@setfontsize\small\@ixpt\@xpt
|
||||||
|
\abovedisplayskip 6\p@ \@plus 1.5\p@ \@minus 4\p@
|
||||||
|
\abovedisplayshortskip \z@ \@plus 2\p@
|
||||||
|
\belowdisplayskip \abovedisplayskip
|
||||||
|
\belowdisplayshortskip 3\p@ \@plus 2\p@ \@minus 2\p@
|
||||||
|
}
|
||||||
|
\renewcommand{\footnotesize}{\@setfontsize\footnotesize\@ixpt\@xpt}
|
||||||
|
\renewcommand{\scriptsize}{\@setfontsize\scriptsize\@viipt\@viiipt}
|
||||||
|
\renewcommand{\tiny}{\@setfontsize\tiny\@vipt\@viipt}
|
||||||
|
\renewcommand{\large}{\@setfontsize\large\@xiipt{14}}
|
||||||
|
\renewcommand{\Large}{\@setfontsize\Large\@xivpt{16}}
|
||||||
|
\renewcommand{\LARGE}{\@setfontsize\LARGE\@xviipt{20}}
|
||||||
|
\renewcommand{\huge}{\@setfontsize\huge\@xxpt{23}}
|
||||||
|
\renewcommand{\Huge}{\@setfontsize\Huge\@xxvpt{28}}
|
||||||
|
|
||||||
|
% sections with less space
|
||||||
|
\providecommand{\section}{}
|
||||||
|
\renewcommand{\section}{%
|
||||||
|
\@startsection{section}{1}{\z@}%
|
||||||
|
{-2.0ex \@plus -0.5ex \@minus -0.2ex}%
|
||||||
|
{ 1.5ex \@plus 0.3ex \@minus 0.2ex}%
|
||||||
|
{\large\bf\raggedright}%
|
||||||
|
}
|
||||||
|
\providecommand{\subsection}{}
|
||||||
|
\renewcommand{\subsection}{%
|
||||||
|
\@startsection{subsection}{2}{\z@}%
|
||||||
|
{-1.8ex \@plus -0.5ex \@minus -0.2ex}%
|
||||||
|
{ 0.8ex \@plus 0.2ex}%
|
||||||
|
{\normalsize\bf\raggedright}%
|
||||||
|
}
|
||||||
|
\providecommand{\subsubsection}{}
|
||||||
|
\renewcommand{\subsubsection}{%
|
||||||
|
\@startsection{subsubsection}{3}{\z@}%
|
||||||
|
{-1.5ex \@plus -0.5ex \@minus -0.2ex}%
|
||||||
|
{ 0.5ex \@plus 0.2ex}%
|
||||||
|
{\normalsize\bf\raggedright}%
|
||||||
|
}
|
||||||
|
\providecommand{\paragraph}{}
|
||||||
|
\renewcommand{\paragraph}{%
|
||||||
|
\@startsection{paragraph}{4}{\z@}%
|
||||||
|
{1.5ex \@plus 0.5ex \@minus 0.2ex}%
|
||||||
|
{-1em}%
|
||||||
|
{\normalsize\bf}%
|
||||||
|
}
|
||||||
|
\providecommand{\subparagraph}{}
|
||||||
|
\renewcommand{\subparagraph}{%
|
||||||
|
\@startsection{subparagraph}{5}{\z@}%
|
||||||
|
{1.5ex \@plus 0.5ex \@minus 0.2ex}%
|
||||||
|
{-1em}%
|
||||||
|
{\normalsize\bf}%
|
||||||
|
}
|
||||||
|
\providecommand{\subsubsubsection}{}
|
||||||
|
\renewcommand{\subsubsubsection}{%
|
||||||
|
\vskip5pt{\noindent\normalsize\rm\raggedright}%
|
||||||
|
}
|
||||||
|
|
||||||
|
% float placement
|
||||||
|
\renewcommand{\topfraction }{0.85}
|
||||||
|
\renewcommand{\bottomfraction }{0.4}
|
||||||
|
\renewcommand{\textfraction }{0.1}
|
||||||
|
\renewcommand{\floatpagefraction}{0.7}
|
||||||
|
|
||||||
|
\newlength{\@neuripsabovecaptionskip}\setlength{\@neuripsabovecaptionskip}{7\p@}
|
||||||
|
\newlength{\@neuripsbelowcaptionskip}\setlength{\@neuripsbelowcaptionskip}{\z@}
|
||||||
|
|
||||||
|
\setlength{\abovecaptionskip}{\@neuripsabovecaptionskip}
|
||||||
|
\setlength{\belowcaptionskip}{\@neuripsbelowcaptionskip}
|
||||||
|
|
||||||
|
% swap above/belowcaptionskip lengths for tables
|
||||||
|
\renewenvironment{table}
|
||||||
|
{\setlength{\abovecaptionskip}{\@neuripsbelowcaptionskip}%
|
||||||
|
\setlength{\belowcaptionskip}{\@neuripsabovecaptionskip}%
|
||||||
|
\@float{table}}
|
||||||
|
{\end@float}
|
||||||
|
|
||||||
|
% footnote formatting
|
||||||
|
\setlength{\footnotesep }{6.65\p@}
|
||||||
|
\setlength{\skip\footins}{9\p@ \@plus 4\p@ \@minus 2\p@}
|
||||||
|
\renewcommand{\footnoterule}{\kern-3\p@ \hrule width 12pc \kern 2.6\p@}
|
||||||
|
\setcounter{footnote}{0}
|
||||||
|
|
||||||
|
% paragraph formatting
|
||||||
|
\setlength{\parindent}{\z@}
|
||||||
|
\setlength{\parskip }{5.5\p@}
|
||||||
|
|
||||||
|
% list formatting
|
||||||
|
\setlength{\topsep }{4\p@ \@plus 1\p@ \@minus 2\p@}
|
||||||
|
\setlength{\partopsep }{1\p@ \@plus 0.5\p@ \@minus 0.5\p@}
|
||||||
|
\setlength{\itemsep }{2\p@ \@plus 1\p@ \@minus 0.5\p@}
|
||||||
|
\setlength{\parsep }{2\p@ \@plus 1\p@ \@minus 0.5\p@}
|
||||||
|
\setlength{\leftmargin }{3pc}
|
||||||
|
\setlength{\leftmargini }{\leftmargin}
|
||||||
|
\setlength{\leftmarginii }{2em}
|
||||||
|
\setlength{\leftmarginiii}{1.5em}
|
||||||
|
\setlength{\leftmarginiv }{1.0em}
|
||||||
|
\setlength{\leftmarginv }{0.5em}
|
||||||
|
\def\@listi {\leftmargin\leftmargini}
|
||||||
|
\def\@listii {\leftmargin\leftmarginii
|
||||||
|
\labelwidth\leftmarginii
|
||||||
|
\advance\labelwidth-\labelsep
|
||||||
|
\topsep 2\p@ \@plus 1\p@ \@minus 0.5\p@
|
||||||
|
\parsep 1\p@ \@plus 0.5\p@ \@minus 0.5\p@
|
||||||
|
\itemsep \parsep}
|
||||||
|
\def\@listiii{\leftmargin\leftmarginiii
|
||||||
|
\labelwidth\leftmarginiii
|
||||||
|
\advance\labelwidth-\labelsep
|
||||||
|
\topsep 1\p@ \@plus 0.5\p@ \@minus 0.5\p@
|
||||||
|
\parsep \z@
|
||||||
|
\partopsep 0.5\p@ \@plus 0\p@ \@minus 0.5\p@
|
||||||
|
\itemsep \topsep}
|
||||||
|
\def\@listiv {\leftmargin\leftmarginiv
|
||||||
|
\labelwidth\leftmarginiv
|
||||||
|
\advance\labelwidth-\labelsep}
|
||||||
|
\def\@listv {\leftmargin\leftmarginv
|
||||||
|
\labelwidth\leftmarginv
|
||||||
|
\advance\labelwidth-\labelsep}
|
||||||
|
\def\@listvi {\leftmargin\leftmarginvi
|
||||||
|
\labelwidth\leftmarginvi
|
||||||
|
\advance\labelwidth-\labelsep}
|
||||||
|
|
||||||
|
% create title
|
||||||
|
\providecommand{\maketitle}{}
|
||||||
|
\renewcommand{\maketitle}{%
|
||||||
|
\par
|
||||||
|
\begingroup
|
||||||
|
\renewcommand{\thefootnote}{\fnsymbol{footnote}}
|
||||||
|
% for perfect author name centering
|
||||||
|
\renewcommand{\@makefnmark}{\hbox to \z@{$^{\@thefnmark}$\hss}}
|
||||||
|
% The footnote-mark was overlapping the footnote-text,
|
||||||
|
% added the following to fix this problem (MK)
|
||||||
|
\long\def\@makefntext##1{%
|
||||||
|
\parindent 1em\noindent
|
||||||
|
\hbox to 1.8em{\hss $\m@th ^{\@thefnmark}$}##1
|
||||||
|
}
|
||||||
|
\thispagestyle{empty}
|
||||||
|
\@maketitle
|
||||||
|
\@thanks
|
||||||
|
\@notice
|
||||||
|
\endgroup
|
||||||
|
\let\maketitle\relax
|
||||||
|
\let\thanks\relax
|
||||||
|
}
|
||||||
|
|
||||||
|
% rules for title box at top of first page
|
||||||
|
\newcommand{\@toptitlebar}{
|
||||||
|
\hrule height 4\p@
|
||||||
|
\vskip 0.25in
|
||||||
|
\vskip -\parskip%
|
||||||
|
}
|
||||||
|
\newcommand{\@bottomtitlebar}{
|
||||||
|
\vskip 0.29in
|
||||||
|
\vskip -\parskip
|
||||||
|
\hrule height 1\p@
|
||||||
|
\vskip 0.09in%
|
||||||
|
}
|
||||||
|
|
||||||
|
% create title (includes both anonymized and non-anonymized versions)
|
||||||
|
\providecommand{\@maketitle}{}
|
||||||
|
\renewcommand{\@maketitle}{%
|
||||||
|
\vbox{%
|
||||||
|
\hsize\textwidth
|
||||||
|
\linewidth\hsize
|
||||||
|
\vskip 0.1in
|
||||||
|
\@toptitlebar
|
||||||
|
\centering
|
||||||
|
{\LARGE\bf \@title\par}
|
||||||
|
\@bottomtitlebar
|
||||||
|
\if@submission
|
||||||
|
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}
|
||||||
|
Anonymous Author(s) \\
|
||||||
|
Affiliation \\
|
||||||
|
Address \\
|
||||||
|
\texttt{email} \\
|
||||||
|
\end{tabular}%
|
||||||
|
\else
|
||||||
|
\def\And{%
|
||||||
|
\end{tabular}\hfil\linebreak[0]\hfil%
|
||||||
|
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\ignorespaces%
|
||||||
|
}
|
||||||
|
\def\AND{%
|
||||||
|
\end{tabular}\hfil\linebreak[4]\hfil%
|
||||||
|
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\ignorespaces%
|
||||||
|
}
|
||||||
|
\begin{tabular}[t]{c}\bf\rule{\z@}{24\p@}\@author\end{tabular}%
|
||||||
|
\fi
|
||||||
|
\vskip 0.3in \@minus 0.1in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
% add conference notice to bottom of first page
|
||||||
|
\newcommand{\ftype@noticebox}{8}
|
||||||
|
\newcommand{\@notice}{%
|
||||||
|
% give a bit of extra room back to authors on first page
|
||||||
|
\enlargethispage{2\baselineskip}%
|
||||||
|
\@float{noticebox}[b]%
|
||||||
|
\footnotesize\@noticestring%
|
||||||
|
\end@float%
|
||||||
|
}
|
||||||
|
|
||||||
|
% abstract styling
|
||||||
|
\renewenvironment{abstract}%
|
||||||
|
{%
|
||||||
|
\vskip 0.075in%
|
||||||
|
\centerline%
|
||||||
|
{\large\bf Abstract}%
|
||||||
|
\vspace{0.5ex}%
|
||||||
|
\begin{quote}%
|
||||||
|
}
|
||||||
|
{
|
||||||
|
\par%
|
||||||
|
\end{quote}%
|
||||||
|
\vskip 1ex%
|
||||||
|
}
|
||||||
|
|
||||||
|
% handle tweaks for camera-ready copy vs. submission copy
|
||||||
|
\if@preprint
|
||||||
|
\newcommand{\@noticestring}{%
|
||||||
|
Preprint. Under review.%
|
||||||
|
}
|
||||||
|
\else
|
||||||
|
\if@neuripsfinal
|
||||||
|
\newcommand{\@noticestring}{%
|
||||||
|
(\@neuripsyear) \@title
|
||||||
|
}
|
||||||
|
\else
|
||||||
|
\newcommand{\@noticestring}{%
|
||||||
|
(\@neuripsyear) \@title %
|
||||||
|
}
|
||||||
|
|
||||||
|
% hide the acknowledgements
|
||||||
|
\NewEnviron{hide}{}
|
||||||
|
\let\ack\hide
|
||||||
|
\let\endack\endhide
|
||||||
|
|
||||||
|
% line numbers for submission
|
||||||
|
\RequirePackage{lineno}
|
||||||
|
\linenumbers
|
||||||
|
|
||||||
|
% fix incompatibilities between lineno and amsmath, if required, by
|
||||||
|
% transparently wrapping linenomath environments around amsmath
|
||||||
|
% environments
|
||||||
|
\AtBeginDocument{%
|
||||||
|
\@ifpackageloaded{amsmath}{%
|
||||||
|
\newcommand*\patchAmsMathEnvironmentForLineno[1]{%
|
||||||
|
\expandafter\let\csname old#1\expandafter\endcsname\csname #1\endcsname
|
||||||
|
\expandafter\let\csname oldend#1\expandafter\endcsname\csname end#1\endcsname
|
||||||
|
\renewenvironment{#1}%
|
||||||
|
{\linenomath\csname old#1\endcsname}%
|
||||||
|
{\csname oldend#1\endcsname\endlinenomath}%
|
||||||
|
}%
|
||||||
|
\newcommand*\patchBothAmsMathEnvironmentsForLineno[1]{%
|
||||||
|
\patchAmsMathEnvironmentForLineno{#1}%
|
||||||
|
\patchAmsMathEnvironmentForLineno{#1*}%
|
||||||
|
}%
|
||||||
|
\patchBothAmsMathEnvironmentsForLineno{equation}%
|
||||||
|
\patchBothAmsMathEnvironmentsForLineno{align}%
|
||||||
|
\patchBothAmsMathEnvironmentsForLineno{flalign}%
|
||||||
|
\patchBothAmsMathEnvironmentsForLineno{alignat}%
|
||||||
|
\patchBothAmsMathEnvironmentsForLineno{gather}%
|
||||||
|
\patchBothAmsMathEnvironmentsForLineno{multline}%
|
||||||
|
}
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
\fi
|
||||||
|
\fi
|
||||||
|
|
||||||
|
|
||||||
|
\endinput
|
159
.obsidian/plugins/obsidian-enhancing-export-main/textemplate/neurips.tex
vendored
Normal file
159
.obsidian/plugins/obsidian-enhancing-export-main/textemplate/neurips.tex
vendored
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
\documentclass{article}
|
||||||
|
|
||||||
|
|
||||||
|
% if you need to pass options to natbib, use, e.g.:
|
||||||
|
% \PassOptionsToPackage{numbers, compress}{natbib}
|
||||||
|
% before loading neurips_2023
|
||||||
|
|
||||||
|
|
||||||
|
% ready for submission
|
||||||
|
\usepackage[final]{neurips}
|
||||||
|
|
||||||
|
|
||||||
|
% to compile a preprint version, e.g., for submission to arXiv, add add the
|
||||||
|
% [preprint] option:
|
||||||
|
% \usepackage[preprint]{neurips_2023}
|
||||||
|
|
||||||
|
|
||||||
|
% to compile a camera-ready version, add the [final] option, e.g.:
|
||||||
|
% \usepackage[final]{neurips_2023}
|
||||||
|
|
||||||
|
|
||||||
|
% to avoid loading the natbib package, add option nonatbib:
|
||||||
|
% \usepackage[nonatbib]{neurips_2023}
|
||||||
|
|
||||||
|
|
||||||
|
\usepackage[utf8]{inputenc} % allow utf-8 input
|
||||||
|
\usepackage[T1]{fontenc} % use 8-bit T1 fonts
|
||||||
|
\usepackage{hyperref} % hyperlinks
|
||||||
|
\usepackage{url} % simple URL typesetting
|
||||||
|
\usepackage{booktabs} % professional-quality tables
|
||||||
|
\usepackage{amsfonts} % blackboard math symbols
|
||||||
|
\usepackage{nicefrac} % compact symbols for 1/2, etc.
|
||||||
|
\usepackage{microtype} % microtypography
|
||||||
|
\usepackage{xcolor} % colors
|
||||||
|
\usepackage{graphicx}
|
||||||
|
|
||||||
|
\makeatletter
|
||||||
|
\def\maxwidth{\ifdim\Gin@nat@width>\linewidth\linewidth\else\Gin@nat@width\fi}
|
||||||
|
\def\maxheight{\ifdim\Gin@nat@height>\textheight\textheight\else\Gin@nat@height\fi}
|
||||||
|
\makeatother
|
||||||
|
% Scale images if necessary, so that they will not overflow the page
|
||||||
|
% margins by default, and it is still possible to overwrite the defaults
|
||||||
|
% using explicit options in \includegraphics[width, height, ...]{}
|
||||||
|
\setkeys{Gin}{width=\maxwidth,height=\maxheight,keepaspectratio}
|
||||||
|
% Set default figure placement to htbp
|
||||||
|
\makeatletter
|
||||||
|
\def\fps@figure{htbp}
|
||||||
|
\makeatother
|
||||||
|
|
||||||
|
$if(csl-refs)$
|
||||||
|
\newlength{\cslhangindent}
|
||||||
|
\setlength{\cslhangindent}{1.5em}
|
||||||
|
\newlength{\csllabelwidth}
|
||||||
|
\setlength{\csllabelwidth}{3em}
|
||||||
|
\newlength{\cslentryspacingunit} % times entry-spacing
|
||||||
|
\setlength{\cslentryspacingunit}{\parskip}
|
||||||
|
\newenvironment{CSLReferences}[2] % #1 hanging-ident, #2 entry spacing
|
||||||
|
{% don't indent paragraphs
|
||||||
|
\setlength{\parindent}{0pt}
|
||||||
|
% turn on hanging indent if param 1 is 1
|
||||||
|
\ifodd #1
|
||||||
|
\let\oldpar\par
|
||||||
|
\def\par{\hangindent=\cslhangindent\oldpar}
|
||||||
|
\fi
|
||||||
|
% set entry spacing
|
||||||
|
\setlength{\parskip}{#2\cslentryspacingunit}
|
||||||
|
}%
|
||||||
|
{}
|
||||||
|
\usepackage{calc}
|
||||||
|
\newcommand{\CSLBlock}[1]{#1\hfill\break}
|
||||||
|
\newcommand{\CSLLeftMargin}[1]{\parbox[t]{\csllabelwidth}{#1}}
|
||||||
|
\newcommand{\CSLRightInline}[1]{\parbox[t]{\linewidth - \csllabelwidth}{#1}\break}
|
||||||
|
\newcommand{\CSLIndent}[1]{\hspace{\cslhangindent}#1}
|
||||||
|
$endif$
|
||||||
|
\providecommand{\tightlist}{%
|
||||||
|
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
|
||||||
|
\title{$title$}
|
||||||
|
|
||||||
|
|
||||||
|
% Iterate through the authors except last to add \And.
|
||||||
|
|
||||||
|
\author{%
|
||||||
|
$for(authors/allbutlast)$
|
||||||
|
$authors.name$\\$authors.affiliation$\\$authors.institution$\\$authors.email$\\$authors.address$ \And
|
||||||
|
$endfor$
|
||||||
|
$for(authors/last)$
|
||||||
|
$authors.name$\\$authors.affiliation$\\$authors.institution$\\$authors.email$\\$authors.address$
|
||||||
|
$endfor$
|
||||||
|
}
|
||||||
|
|
||||||
|
% \author{%
|
||||||
|
% David S.~Hippocampus \\
|
||||||
|
% Department of Computer Science\\
|
||||||
|
% Cranberry-Lemon University\\
|
||||||
|
% Pittsburgh, PA 15213 \\
|
||||||
|
% \texttt{hippo@cs.cranberry-lemon.edu} \\
|
||||||
|
% % examples of more authors
|
||||||
|
% % \And
|
||||||
|
% % Coauthor \\
|
||||||
|
% % Affiliation \\
|
||||||
|
% % Address \\
|
||||||
|
% % \texttt{email} \\
|
||||||
|
% % \AND
|
||||||
|
% % Coauthor \\
|
||||||
|
% % Affiliation \\
|
||||||
|
% % Address \\
|
||||||
|
% % \texttt{email} \\
|
||||||
|
% % \And
|
||||||
|
% % Coauthor \\
|
||||||
|
% % Affiliation \\
|
||||||
|
% % Address \\
|
||||||
|
% % \texttt{email} \\
|
||||||
|
% % \And
|
||||||
|
% % Coauthor \\
|
||||||
|
% % Affiliation \\
|
||||||
|
% % Address \\
|
||||||
|
% % \texttt{email} \\
|
||||||
|
% }
|
||||||
|
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
|
||||||
|
|
||||||
|
\maketitle
|
||||||
|
|
||||||
|
|
||||||
|
\begin{abstract}
|
||||||
|
$if(abstract)$
|
||||||
|
$abstract$
|
||||||
|
$else$
|
||||||
|
Add your abstract at the beginning of your markdown file like this
|
||||||
|
\begin{verbatim}
|
||||||
|
---
|
||||||
|
title: "Your Title"
|
||||||
|
abstract: "your abstract here"
|
||||||
|
authors:
|
||||||
|
- name: Leonardo V. Castorina
|
||||||
|
affiliation: School of Informatics
|
||||||
|
institution: University of Edinburgh
|
||||||
|
email: justanemail@domain.ext
|
||||||
|
address: Edinburgh
|
||||||
|
- name: Coauthor
|
||||||
|
affiliation: Affiliation
|
||||||
|
institution: Institution
|
||||||
|
email: coauthor@example.com
|
||||||
|
address: Address
|
||||||
|
---
|
||||||
|
\end{verbatim}
|
||||||
|
This is called YAML frontmatter. If you set your abstract correctly you should not see this message.
|
||||||
|
$endif$
|
||||||
|
\end{abstract}
|
||||||
|
|
||||||
|
|
||||||
|
$body$
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
|
||||||
|
\end{document}
|
29
.obsidian/plugins/obsidian-enhancing-export-main/tsconfig.json
vendored
Normal file
29
.obsidian/plugins/obsidian-enhancing-export-main/tsconfig.json
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"inlineSourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"target": "ES2021",
|
||||||
|
"allowJs": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"importHelpers": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"jsxImportSource": "solid-js",
|
||||||
|
"noEmit": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"lib": [
|
||||||
|
"DOM",
|
||||||
|
"ES5",
|
||||||
|
"ES6",
|
||||||
|
"ES7",
|
||||||
|
"ES2021"
|
||||||
|
],
|
||||||
|
"typeRoots": [ "./typings", "./node_modules/@types"]
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.ts", "**/*.tsx"
|
||||||
|
]
|
||||||
|
}
|
16204
.obsidian/plugins/obsidian-enhancing-export-main/typings/electron.d.ts
vendored
Normal file
16204
.obsidian/plugins/obsidian-enhancing-export-main/typings/electron.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
34
.obsidian/plugins/obsidian-enhancing-export-main/typings/obsidian.d.ts
vendored
Normal file
34
.obsidian/plugins/obsidian-enhancing-export-main/typings/obsidian.d.ts
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
import 'obsidian';
|
||||||
|
import type { EventRef } from 'obsidian';
|
||||||
|
|
||||||
|
|
||||||
|
declare module 'obsidian' {
|
||||||
|
|
||||||
|
export interface DataAdapter {
|
||||||
|
getBasePath(): string;
|
||||||
|
getFullPath(path: string): string;
|
||||||
|
startWatchPath(path: string): void;
|
||||||
|
stopWatchPath(path: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PluginSettingTab {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface App {
|
||||||
|
readonly loadProgress: { show(): void; hide(): void; setMessage(msg: string): void; };
|
||||||
|
plugins: {
|
||||||
|
enablePlugin(id: string): Promise<void>;
|
||||||
|
disablePlugin(id: string): Promise<void>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Vault {
|
||||||
|
config: {
|
||||||
|
attachmentFolderPath: string,
|
||||||
|
useMarkdownLinks: boolean,
|
||||||
|
}
|
||||||
|
on(name: 'raw', callback: (file: string) => void, ctx?: any): EventRef;
|
||||||
|
}
|
||||||
|
}
|
29
.obsidian/plugins/obsidian-enhancing-export-main/version-bump.mjs
vendored
Normal file
29
.obsidian/plugins/obsidian-enhancing-export-main/version-bump.mjs
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { readFileSync, writeFileSync } from 'fs';
|
||||||
|
import { exec } from 'child_process';
|
||||||
|
import process from 'process';
|
||||||
|
|
||||||
|
|
||||||
|
const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
|
||||||
|
|
||||||
|
const version = process.argv.at(2) ?? pkg.version;
|
||||||
|
|
||||||
|
if (version != pkg.version) {
|
||||||
|
pkg.version = version;
|
||||||
|
writeFileSync('package.json', `${JSON.stringify(pkg, null, 2)}\n`);
|
||||||
|
exec('git add package.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// read minAppVersion from manifest.json and bump version to target version
|
||||||
|
let manifest = JSON.parse(readFileSync('manifest.json', 'utf8'));
|
||||||
|
const { minAppVersion } = manifest;
|
||||||
|
manifest.version = pkg.version;
|
||||||
|
manifest.description = pkg.description;
|
||||||
|
writeFileSync('manifest.json', JSON.stringify(manifest, null, 2));
|
||||||
|
|
||||||
|
// update versions.json with target version and minAppVersion from manifest.json
|
||||||
|
let versions = JSON.parse(readFileSync('versions.json', 'utf8'));
|
||||||
|
versions[pkg.version] = minAppVersion;
|
||||||
|
writeFileSync('versions.json', JSON.stringify(versions, null, '\t'));
|
||||||
|
|
||||||
|
exec('git add manifest.json versions.json');
|
53
.obsidian/plugins/obsidian-enhancing-export-main/versions.json
vendored
Normal file
53
.obsidian/plugins/obsidian-enhancing-export-main/versions.json
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"1.0.4": "0.12.0",
|
||||||
|
"1.0.5": "0.12.0",
|
||||||
|
"1.0.6": "0.12.0",
|
||||||
|
"1.0.7": "0.12.0",
|
||||||
|
"1.0.8": "0.12.0",
|
||||||
|
"1.0.9": "0.12.0",
|
||||||
|
"1.0.10": "0.12.0",
|
||||||
|
"1.0.11": "0.12.0",
|
||||||
|
"1.1.0": "0.12.0",
|
||||||
|
"1.1.1": "0.12.0",
|
||||||
|
"1.1.2": "0.12.0",
|
||||||
|
"1.1.3": "0.12.0",
|
||||||
|
"1.1.4": "0.12.0",
|
||||||
|
"1.1.5": "0.12.0",
|
||||||
|
"1.1.6": "0.12.0",
|
||||||
|
"1.1.7": "0.12.0",
|
||||||
|
"1.1.8": "0.12.0",
|
||||||
|
"1.1.9": "0.12.0",
|
||||||
|
"1.1.10": "0.12.0",
|
||||||
|
"1.2.0": "0.12.0",
|
||||||
|
"1.2.1": "0.12.0",
|
||||||
|
"1.3.0": "0.12.0",
|
||||||
|
"1.3.1": "0.12.0",
|
||||||
|
"1.4.1": "0.12.0",
|
||||||
|
"1.5.2": "0.12.0",
|
||||||
|
"1.5.3": "0.12.0",
|
||||||
|
"1.5.4": "0.12.0",
|
||||||
|
"1.6.1": "0.12.0",
|
||||||
|
"1.7.1": "0.12.0",
|
||||||
|
"1.7.2": "0.12.0",
|
||||||
|
"1.8.1": "0.12.0",
|
||||||
|
"1.8.2": "0.12.0",
|
||||||
|
"1.8.3": "0.12.0",
|
||||||
|
"1.8.4": "0.12.0",
|
||||||
|
"1.8.5": "0.12.0",
|
||||||
|
"1.8.6": "0.12.0",
|
||||||
|
"1.8.7": "0.12.0",
|
||||||
|
"1.8.8": "0.12.0",
|
||||||
|
"1.9.1": "0.12.0",
|
||||||
|
"1.9.2": "0.12.0",
|
||||||
|
"1.9.3": "0.12.0",
|
||||||
|
"1.9.4": "0.12.0",
|
||||||
|
"1.9.5": "0.12.0",
|
||||||
|
"1.9.6": "0.12.0",
|
||||||
|
"1.10.1": "0.12.0",
|
||||||
|
"1.10.2": "0.12.0",
|
||||||
|
"1.10.3": "0.12.0",
|
||||||
|
"1.10.4": "0.12.0",
|
||||||
|
"1.10.5": "0.12.0",
|
||||||
|
"1.10.6": "1.6.3",
|
||||||
|
"1.10.7": "1.6.3"
|
||||||
|
}
|
143
.obsidian/plugins/obsidian-enhancing-export-main/vite.config.ts
vendored
Normal file
143
.obsidian/plugins/obsidian-enhancing-export-main/vite.config.ts
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import { exec } from 'child_process';
|
||||||
|
import { defineConfig, loadEnv, Plugin, UserConfig } from 'vite';
|
||||||
|
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
||||||
|
import solidPlugin from 'vite-plugin-solid';
|
||||||
|
import builtins from 'builtin-modules';
|
||||||
|
import path from 'path';
|
||||||
|
import * as fsp from 'fs/promises';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const banner = `
|
||||||
|
/*!
|
||||||
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||||
|
if you want to view the source, please visit the github repository https://github.com/mokeyish/obsidian-enhancing-export .
|
||||||
|
*/
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
export default defineConfig(async ({ mode }) => {
|
||||||
|
const { normalize } = path;
|
||||||
|
const { rm } = fsp;
|
||||||
|
const prod = mode === 'production';
|
||||||
|
|
||||||
|
let { OUT_DIR } = loadEnv(mode, process.cwd(), ['OUT_']);
|
||||||
|
|
||||||
|
OUT_DIR = normalize(OUT_DIR);
|
||||||
|
if (OUT_DIR != 'dist' && OUT_DIR != path.join(process.cwd(), 'dist')) {
|
||||||
|
await rm('dist', { recursive: true });
|
||||||
|
exec(process.platform === 'win32' ? `mklink /J dist ${OUT_DIR}` : `ln -s ${OUT_DIR} dist`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
plugins: [
|
||||||
|
solidPlugin(),
|
||||||
|
viteStaticCopy({
|
||||||
|
targets: [{
|
||||||
|
src: 'manifest.json',
|
||||||
|
dest: '.'
|
||||||
|
}]
|
||||||
|
}),
|
||||||
|
loader({
|
||||||
|
'.lua': 'binary',
|
||||||
|
'.tex': 'binary',
|
||||||
|
'.sty': 'binary'
|
||||||
|
}), // src/resources.ts
|
||||||
|
prod ? undefined : inject(['src/hmr.ts']),
|
||||||
|
],
|
||||||
|
build: {
|
||||||
|
lib: {
|
||||||
|
entry: 'src/main.ts',
|
||||||
|
name: 'main',
|
||||||
|
fileName: () => 'main.js',
|
||||||
|
formats: ['cjs'],
|
||||||
|
},
|
||||||
|
minify: prod,
|
||||||
|
sourcemap: prod ? false : 'inline',
|
||||||
|
cssCodeSplit: false,
|
||||||
|
emptyOutDir: false,
|
||||||
|
// outDir: '',
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
exports: 'named',
|
||||||
|
assetFileNames: (v) => v.name === 'style.css' ? 'styles.css' : v.name,
|
||||||
|
banner,
|
||||||
|
},
|
||||||
|
external: [
|
||||||
|
'obsidian',
|
||||||
|
'electron',
|
||||||
|
'@codemirror/autocomplete',
|
||||||
|
'@codemirror/closebrackets',
|
||||||
|
'@codemirror/collab',
|
||||||
|
'@codemirror/commands',
|
||||||
|
'@codemirror/comment',
|
||||||
|
'@codemirror/fold',
|
||||||
|
'@codemirror/gutter',
|
||||||
|
'@codemirror/highlight',
|
||||||
|
'@codemirror/history',
|
||||||
|
'@codemirror/language',
|
||||||
|
'@codemirror/lint',
|
||||||
|
'@codemirror/matchbrackets',
|
||||||
|
'@codemirror/panel',
|
||||||
|
'@codemirror/rangeset',
|
||||||
|
'@codemirror/rectangular-selection',
|
||||||
|
'@codemirror/search',
|
||||||
|
'@codemirror/state',
|
||||||
|
'@codemirror/stream-parser',
|
||||||
|
'@codemirror/text',
|
||||||
|
'@codemirror/tooltip',
|
||||||
|
'@codemirror/view',
|
||||||
|
'@lezer/common',
|
||||||
|
'@lezer/highlight',
|
||||||
|
'@lezer/lr',
|
||||||
|
...builtins
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} as UserConfig;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const loader = (config: { [extention: string]: 'binary' }): Plugin => {
|
||||||
|
const { extname } = path;
|
||||||
|
return {
|
||||||
|
name: 'binary-boader',
|
||||||
|
enforce: 'pre',
|
||||||
|
async load(id) {
|
||||||
|
const format = config[extname(id)];
|
||||||
|
if (format) {
|
||||||
|
if (format === 'binary') {
|
||||||
|
const buffer = await fsp.readFile(id);
|
||||||
|
return {
|
||||||
|
code: `export default Uint8Array.from(atob('${buffer.toString('base64')}'), c => c.charCodeAt(0));`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const inject = (files: string[]): Plugin => {
|
||||||
|
if (files && files.length > 0) {
|
||||||
|
return {
|
||||||
|
name: 'inject-code',
|
||||||
|
async load(this, id) {
|
||||||
|
const info = this.getModuleInfo(id);
|
||||||
|
if (info.isEntry) {
|
||||||
|
const code = await fsp.readFile(id, 'utf-8');
|
||||||
|
const { relative, dirname, basename, extname, join } = path;
|
||||||
|
const dir = dirname(id);
|
||||||
|
const inject_code = files
|
||||||
|
.map(v => relative(dir, v))
|
||||||
|
.map(p => join('./', basename(p, extname(p))))
|
||||||
|
.map(p => `import './${p}'`).join(';');
|
||||||
|
return `
|
||||||
|
${inject_code};
|
||||||
|
${code}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
Reference in New Issue
Block a user