diff --git a/.eslintrc.js b/.eslintrc.js
index df31ee5..249a7fa 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -11,6 +11,7 @@ const TypescriptRules = {
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/no-unsafe-member-access': 'off',
+ '@typescript-eslint/no-unsafe-return': 'off',
'@typescript-eslint/explicit-member-accessibility': [
'error',
{
@@ -70,6 +71,8 @@ const TypescriptRules = {
'@typescript-eslint/no-magic-numbers': ['error', { ignore: [-1, 0, 1] }],
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
+ '@typescript-eslint/parameter-properties': 'off',
+ '@typescript-eslint/restrict-template-expressions': 'off',
};
module.exports = {
@@ -99,6 +102,12 @@ module.exports = {
project: './tsconfig.eslint.json',
},
overrides: [
+ {
+ files: ['crates/**/*.js'],
+ parserOptions: {
+ project: null,
+ },
+ },
{
files: ['**/*.{js,jsx}'],
rules: {
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..f6e229a
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,216 @@
+name: Build
+
+on:
+ push:
+ branches: [ main, master ]
+ tags:
+ - 'v*'
+ pull_request:
+ branches: [ main, master ]
+ workflow_dispatch:
+
+jobs:
+ test:
+ name: Test on ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [macos-latest, windows-latest]
+ runs-on: ${{ matrix.os }}
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Rust
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: stable
+ override: true
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2
+ with:
+ version: 8
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Install cargo-make
+ run: cargo install cargo-make
+
+ - name: Run tests
+ run: pnpm ts:check && pnpm lint && cd ./crates && cargo make ci
+
+ build-macos:
+ name: Build macOS
+ runs-on: macos-latest
+ if: startsWith(github.ref, 'refs/tags/')
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Rust
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: stable
+ override: true
+ targets: x86_64-apple-darwin,aarch64-apple-darwin
+
+ - name: Install Rust targets
+ run: |
+ rustup target add x86_64-apple-darwin
+ rustup target add aarch64-apple-darwin
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2
+ with:
+ version: 8
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Build macOS
+ run: pnpm build:macos
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: mds-macos
+ path: dist/mds-macos.zip
+ retention-days: 90
+
+ build-windows:
+ name: Build Windows
+ runs-on: windows-latest
+ if: startsWith(github.ref, 'refs/tags/')
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Rust
+ uses: actions-rs/toolchain@v1
+ with:
+ toolchain: stable
+ override: true
+ target: x86_64-pc-windows-msvc
+
+ - name: Install Rust target
+ run: rustup target add x86_64-pc-windows-msvc
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2
+ with:
+ version: 8
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Build Windows
+ run: |
+ cd crates
+ node build.js windows --msvc
+
+ - name: Upload artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: mds-windows
+ path: dist/mds-windows.zip
+ retention-days: 90
+
+ deploy-pages:
+ name: Deploy to GitHub Pages
+ needs: [create-release]
+ runs-on: ubuntu-latest
+ if: startsWith(github.ref, 'refs/tags/')
+ permissions:
+ contents: read
+ pages: write
+ id-token: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2
+ with:
+ version: 8
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Build client
+ env:
+ GITHUB_PAGES_BASE_PATH: /${{ github.event.repository.name }}/
+ run: |
+ cd client
+ pnpm build
+
+ - name: Create .nojekyll file
+ run: touch dist/.nojekyll
+
+ - name: Setup Pages
+ uses: actions/configure-pages@v4
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: dist
+
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
+
+ create-release:
+ name: Create Release
+ needs: [build-macos, build-windows]
+ if: startsWith(github.ref, 'refs/tags/')
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Download macOS artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: mds-macos
+ path: dist/
+
+ - name: Download Windows artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: mds-windows
+ path: dist/
+
+ - name: Get version
+ id: version
+ run: |
+ VERSION=${GITHUB_REF#refs/tags/}
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+
+ - name: Create Release
+ uses: softprops/action-gh-release@v1
+ with:
+ files: |
+ dist/mds-macos.zip
+ dist/mds-windows.zip
+ tag_name: ${{ steps.version.outputs.version }}
+ name: Release ${{ steps.version.outputs.version }}
+ draft: false
+ prerelease: false
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
diff --git a/.gitignore b/.gitignore
index cecfee9..43f6850 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,15 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
-/node_modules
-/.pnp
+node_modules
+.pnp
.pnp.js
# testing
-/coverage
+coverage
# production
-/build
+build
# misc
.DS_Store
@@ -27,3 +27,64 @@ dist
config.json
.vscode
+
+# compiled output
+/dist
+/node_modules
+/build
+
+# Logs
+logs
+*.log
+npm-debug.log*
+pnpm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# OS
+.DS_Store
+
+# Tests
+/coverage
+/.nyc_output
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+# dotenv environment variable files
+.env
+.env.development.local
+.env.test.local
+.env.production.local
+.env.local
+
+# temp directory
+.temp
+.tmp
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+editor-settings.json
+MDS-Server.app
+mds-macos.zip
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 0000000..a2483cb
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+pnpm ts:check && pnpm lint && cd ./crates && cargo make commit
diff --git a/README.md b/README.md
index df3da6a..71a11fd 100644
--- a/README.md
+++ b/README.md
@@ -1,95 +1,74 @@
-# Local Markdown Editor
+# Markdown Editor
-- This is a web-based WYSIWYG markdown Editor without the need of database to store the markdown files, since it only needs to access the local file system by specifying the root path of the documents.
+[](https://github.com/s-elo/Markdown-editor/actions/workflows/build.yml)
-- It uses [milkdown](https://milkdown.dev/getting-started) and [react-codemirror](https://uiwjs.github.io/react-codemirror/) to edit and display the local markdown files. It is featured by react with ts and less for the client UI and a node server with ts.
+A web-based WYSIWYG markdown editor that works with your local files. Simply specify the root path of your documents to start editing.
-- [How to use](#set-up)
+**🌐 [Try it online](https://s-elo.github.io/Markdown-editor)**
-## Features
+Built with [Milkdown](https://milkdown.dev/getting-started) and [React CodeMirror](https://uiwjs.github.io/react-codemirror/) for editing and displaying local markdown files.
-Apart from some common features of milkdown and react-codemirror:
+## Key Features
-- `Saving`: synchronizing with the local file system after editing
+- **WYSIWYG Editing**: Rich markdown editing experience with Milkdown
+- **Dual Editor Mode**: Switch between WYSIWYG and raw markdown (CodeMirror) with real-time sync
+- **File Operations**: Create, rename, copy, move, and delete files and folders directly from the editor
+- **Git Integration**: Commit, push, pull, and manage git changes if your document root is a git repository
+- **Search**: Fast file name and content search across your documents based on [ripgrep
+ ](https://github.com/BurntSushi/ripgrep)
+- **Image Management**: Upload and manage images stored locally.
-- `Keyboard shortcuts`: shortcut for saving and read-edit mode switch
-
-- `Code mirror sync`: you can edit and sync in the milkdown or the code mirror with pure markdown syntax
-
-- `Sync position`: you can sync the position at the code mirror by double clicking the milkdown editor
-
-- `File Operations`: you can do some common file operaitons that will be sync to the local file system currently including adding new files and folders, renaming, copying, cutting, pasting and deleting
+## Development
-- `Github Sync`: if the local root document path has a git repo, it should be able to sync the files from the editor page
+### Prerequisites
-- `Navigation`: it has a menu for navigation
+- [Rust](https://www.rust-lang.org/tools/install)
+- [Node.js](https://nodejs.org/) (v20+)
+- [pnpm](https://pnpm.io/)
-- `Decent Search`: it should be able to search the docs quickly via some defined tags and the docs content
+### Setup
-- `Image storage`: currently using aliyun OSS as image storage, you might need to config your aliyun account
+```bash
+# Install dependencies
+pnpm install
+```
-
+### Run Development Server
-
+The project consists of two main components:
-## Set up
+- **Rust Server** (`crates/server`): Handles file operations, git sync, and search
+- **React Client** (`client`): Web UI built with React and TypeScript
-### 1. install deps
+Start both components:
```bash
-yarn
+pnpm dev
```
-### 2. configs(optional)
+This will automatically start the Rust server and React client with hot-reload.
-Add a config.json at the root path
+### Build
-```json
-{
- "docRootPath": "the doc root path",
- "ignoreDirs": [".git", "imgs"],
- // (for aliyun OSS)
- "region": "oss-cn-shenzhen",
- "accessKeyId": "your accessKeyId",
- "accessKeySecret": "your accessKeySecret",
- "bucket": "your bucket name"
-}
+```bash
+# Build both server and client
+pnpm build
```
-> or you can just set the configs at the setting
+### Release
-### 3. compile and bundle the code
+Formal release:
```bash
-yarn build
+pnpm release patch
+pnpm release minor
+pnpm release major
```
-### 4. open the document page
-
-> Before opening the page, make sure the code is bundled.
-
-- run the server at terminal
-
- ```bash
- yarn open
- ```
-
-- or create a shortcut link
-
- 1. for window
-
- > After the bundling, you can just click the run.bat to open the documents. The bat file is actually for window shortcut so that you can open from your desktop.
- > you can create a desktop shortcut by linking the run.bat or run.vbs file.
- > The run.vbs is to hide the command window when you click the shortchut from your desktop.
-
- 2. for mac
- > make sure the project path in run.scpt file is corrent, defualt path is ~/Markdown-editor. you can change to your own clone path.
- > then save the run.scpt file as application file so that you can just double click it to open the editor.
-
-## Development
-
-There are two main components. One is the `node server` for doc file operations; another is the `client` for documentation UI. They are developed mainly using react and typescripts. Once start, the node server and client will be auto run.
+Pre-release:
```bash
-yarn start
+pnpm release patch --alpha
+pnpm release minor --beta
+pnpm release major --rc
```
diff --git a/client/.gitignore b/client/.gitignore
deleted file mode 100644
index 4d29575..0000000
--- a/client/.gitignore
+++ /dev/null
@@ -1,23 +0,0 @@
-# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
-
-# dependencies
-/node_modules
-/.pnp
-.pnp.js
-
-# testing
-/coverage
-
-# production
-/build
-
-# misc
-.DS_Store
-.env.local
-.env.development.local
-.env.test.local
-.env.production.local
-
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
diff --git a/client/config-overrides.js b/client/config-overrides.js
deleted file mode 100644
index 9ec355a..0000000
--- a/client/config-overrides.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/* eslint-disable @typescript-eslint/no-var-requires */
-const path = require('path');
-const { override, addWebpackAlias } = require('customize-cra');
-const addLessLoader = require('customize-cra-less-loader');
-
-module.exports = override(
- addLessLoader(),
- addWebpackAlias({
- ['@']: path.resolve(__dirname, './src'),
- }),
-);
diff --git a/client/package.json b/client/package.json
index b4679ff..9b42c61 100644
--- a/client/package.json
+++ b/client/package.json
@@ -1,71 +1,61 @@
{
"name": "client",
- "version": "0.1.0",
+ "version": "1.0.0",
"private": true,
+ "scripts": {
+ "dev": "cross-env SERVER_PORT=3024 rsbuild dev --port 4000",
+ "build": "cross-env SERVER_PORT=7024 rsbuild build",
+ "preview": "rsbuild preview",
+ "ts:check": "tsc --noEmit"
+ },
"dependencies": {
- "@codemirror/lang-markdown": "^0.19.6",
- "@codemirror/language-data": "^0.19.2",
- "@milkdown/core": "6.1.3",
- "@milkdown/plugin-diagram": "6.1.3",
- "@milkdown/plugin-emoji": "^6.1.3",
- "@milkdown/plugin-history": "^6.1.3",
- "@milkdown/plugin-indent": "^6.1.3",
- "@milkdown/plugin-listener": "^6.1.3",
- "@milkdown/plugin-menu": "^6.1.3",
- "@milkdown/plugin-prism": "^6.1.3",
- "@milkdown/plugin-slash": "^6.1.3",
- "@milkdown/plugin-tooltip": "^6.1.3",
- "@milkdown/plugin-upload": "^6.1.3",
- "@milkdown/preset-commonmark": "^6.1.3",
- "@milkdown/preset-gfm": "^6.1.3",
- "@milkdown/prose": "^6.1.3",
- "@milkdown/react": "^6.1.3",
- "@milkdown/theme-nord": "^6.1.3",
- "@milkdown/theme-tokyo": "^6.1.3",
- "@milkdown/utils": "6.1.3",
+ "@codemirror/lang-markdown": "0.19.6",
+ "@codemirror/language": "^6.11.3",
+ "@codemirror/language-data": "0.19.2",
+ "@emotion/css": "11.13.5",
+ "@emotion/react": "^11.14.0",
+ "@emotion/styled": "^11.14.1",
+ "@milkdown/crepe": "7.18.0",
+ "@milkdown/kit": "7.18.0",
+ "@milkdown/react": "7.18.0",
+ "@milkdown/utils": "7.18.0",
+ "@mui/icons-material": "^7.2.0",
"@reduxjs/toolkit": "^1.7.1",
- "@types/react": "^17.0.30",
- "@types/react-dom": "^17.0.9",
- "@types/react-router-dom": "^5.3.1",
+ "@uiw/codemirror-theme-github": "^4.24.2",
"@uiw/react-codemirror": "4.5.3",
+ "@uiw/react-split": "^5.9.3",
"clipboard": "^2.0.11",
- "cross-env": "^7.0.3",
- "react": "17.0.2",
- "react-dom": "17.0.2",
- "react-redux": "^7.2.6",
- "react-router-dom": "^5.3.0",
- "react-scripts": "^5.0.1",
- "remark-directive": "^2.0.1"
- },
- "scripts": {
- "start": "cross-env PORT=4000 react-app-rewired start",
- "build": "react-app-rewired build",
- "test": "react-app-rewired test",
- "eject": "react-scripts eject"
- },
- "browserslist": {
- "production": [
- ">0.2%",
- "not dead",
- "not op_mini all"
- ],
- "development": [
- "last 1 chrome version",
- "last 1 firefox version",
- "last 1 safari version"
- ]
+ "mdast-util-from-markdown": "^2.0.2",
+ "mdast-util-to-markdown": "^2.1.2",
+ "mermaid": "^11.11.0",
+ "micromark-extension-highlight-mark": "^1.2.0",
+ "micromark-util-types": "^2.0.2",
+ "primeicons": "^7.0.0",
+ "primereact": "^10.9.6",
+ "prosemirror-inputrules": "1.5.1",
+ "react": "19.1.0",
+ "react-complex-tree": "^2.6.0",
+ "react-dom": "19.1.0",
+ "react-redux": "^9.2.0",
+ "react-router-dom": "^7.6.1",
+ "refractor": "4.9.0",
+ "remark-directive": "^4.0.0",
+ "remark-highlight-mark": "^1.4.0",
+ "unified": "^11.0.5",
+ "unist-util-add": "^1.2.0",
+ "unist-util-visit": "^5.0.0",
+ "uuid": "^13.0.0"
},
"devDependencies": {
- "customize-cra": "^1.0.0",
- "customize-cra-less-loader": "^2.0.0",
- "less": "^4.1.2",
- "less-loader": "^11.0.0",
- "react-app-rewired": "^2.2.1",
- "react-error-overlay": "6.0.9",
- "typescript": "^4.4.4"
- },
- "resolutions": {
- "react-error-overlay": "6.0.9"
- },
- "proxy": "http://localhost:3024"
-}
+ "@rsbuild/core": "^1.3.21",
+ "@rsbuild/plugin-less": "^1.2.4",
+ "@rsbuild/plugin-react": "^1.3.1",
+ "@rsbuild/plugin-sass": "^1.3.1",
+ "@types/mdast": "^4.0.4",
+ "@types/node": "^24.10.1",
+ "@types/react": "^19.1.6",
+ "@types/react-dom": "^19.1.5",
+ "@types/react-redux": "7.1.34",
+ "typescript": "^5.8.3"
+ }
+}
\ No newline at end of file
diff --git a/client/public/404.html b/client/public/404.html
new file mode 100644
index 0000000..98cec74
--- /dev/null
+++ b/client/public/404.html
@@ -0,0 +1,45 @@
+
+
+
+
+ Markdown Editor
+
+
+
+
diff --git a/client/public/favicon.ico b/client/public/favicon.ico
deleted file mode 100644
index c2c86b8..0000000
Binary files a/client/public/favicon.ico and /dev/null differ
diff --git a/client/public/fonts/Rubik/Rubik-Black.ttf b/client/public/fonts/Rubik/Rubik-Black.ttf
new file mode 100644
index 0000000..a135092
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-Black.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-BlackItalic.ttf b/client/public/fonts/Rubik/Rubik-BlackItalic.ttf
new file mode 100644
index 0000000..fd1f869
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-BlackItalic.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-Bold.ttf b/client/public/fonts/Rubik/Rubik-Bold.ttf
new file mode 100644
index 0000000..f9c4221
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-Bold.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-BoldItalic.ttf b/client/public/fonts/Rubik/Rubik-BoldItalic.ttf
new file mode 100644
index 0000000..1a7b205
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-BoldItalic.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-ExtraBold.ttf b/client/public/fonts/Rubik/Rubik-ExtraBold.ttf
new file mode 100644
index 0000000..07d11ef
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-ExtraBold.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-ExtraBoldItalic.ttf b/client/public/fonts/Rubik/Rubik-ExtraBoldItalic.ttf
new file mode 100644
index 0000000..871a0d4
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-ExtraBoldItalic.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-Italic.ttf b/client/public/fonts/Rubik/Rubik-Italic.ttf
new file mode 100644
index 0000000..2bb3a87
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-Italic.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-Light.ttf b/client/public/fonts/Rubik/Rubik-Light.ttf
new file mode 100644
index 0000000..974f35a
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-Light.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-LightItalic.ttf b/client/public/fonts/Rubik/Rubik-LightItalic.ttf
new file mode 100644
index 0000000..0bb5ae8
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-LightItalic.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-Medium.ttf b/client/public/fonts/Rubik/Rubik-Medium.ttf
new file mode 100644
index 0000000..a82ab78
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-Medium.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-MediumItalic.ttf b/client/public/fonts/Rubik/Rubik-MediumItalic.ttf
new file mode 100644
index 0000000..7205c56
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-MediumItalic.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-Regular.ttf b/client/public/fonts/Rubik/Rubik-Regular.ttf
new file mode 100644
index 0000000..f596412
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-Regular.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-SemiBold.ttf b/client/public/fonts/Rubik/Rubik-SemiBold.ttf
new file mode 100644
index 0000000..8a95c22
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-SemiBold.ttf differ
diff --git a/client/public/fonts/Rubik/Rubik-SemiBoldItalic.ttf b/client/public/fonts/Rubik/Rubik-SemiBoldItalic.ttf
new file mode 100644
index 0000000..07adbfa
Binary files /dev/null and b/client/public/fonts/Rubik/Rubik-SemiBoldItalic.ttf differ
diff --git a/client/public/fonts/Rubik/font.css b/client/public/fonts/Rubik/font.css
new file mode 100644
index 0000000..23ecb41
--- /dev/null
+++ b/client/public/fonts/Rubik/font.css
@@ -0,0 +1,97 @@
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-Regular.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-Light.ttf') format('truetype');
+ font-weight: 300;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-Medium.ttf') format('truetype');
+ font-weight: 500;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-SemiBold.ttf') format('truetype');
+ font-weight: 600;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-Bold.ttf') format('truetype');
+ font-weight: bold;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-ExtraBold.ttf') format('truetype');
+ font-weight: 800;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-Black.ttf') format('truetype');
+ font-weight: 900;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-Italic.ttf') format('truetype');
+ font-weight: normal;
+ font-style: italic;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-LightItalic.ttf') format('truetype');
+ font-weight: 300;
+ font-style: italic;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-MediumItalic.ttf') format('truetype');
+ font-weight: 500;
+ font-style: italic;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-SemiBoldItalic.ttf') format('truetype');
+ font-weight: 600;
+ font-style: italic;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-BoldItalic.ttf') format('truetype');
+ font-weight: bold;
+ font-style: italic;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-ExtraBoldItalic.ttf') format('truetype');
+ font-weight: 800;
+ font-style: italic;
+}
+
+@font-face {
+ font-family: 'Rubik';
+ src: url('./Rubik-BlackItalic.ttf') format('truetype');
+ font-weight: 900;
+ font-style: italic;
+}
diff --git a/client/public/index.html b/client/public/index.html
index 5ea9760..142eff3 100644
--- a/client/public/index.html
+++ b/client/public/index.html
@@ -2,34 +2,50 @@
-
+
-
-
-
+
-
+
-
-
-
-
-
- DOC
+
+ Markdown Editor
+
+
You need to enable JavaScript to run this app.
diff --git a/client/public/logo.svg b/client/public/logo.svg
new file mode 100644
index 0000000..2192b35
--- /dev/null
+++ b/client/public/logo.svg
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/client/public/logo192.png b/client/public/logo192.png
deleted file mode 100644
index fa313ab..0000000
Binary files a/client/public/logo192.png and /dev/null differ
diff --git a/client/public/logo512.png b/client/public/logo512.png
deleted file mode 100644
index bd5d4b5..0000000
Binary files a/client/public/logo512.png and /dev/null differ
diff --git a/client/public/manifest.json b/client/public/manifest.json
index 080d6c7..38bf2ae 100644
--- a/client/public/manifest.json
+++ b/client/public/manifest.json
@@ -1,20 +1,20 @@
{
- "short_name": "React App",
- "name": "Create React App Sample",
+ "short_name": "Markdown Editor",
+ "name": "Markdown Editor",
"icons": [
{
- "src": "favicon.ico",
+ "src": "logo.svg",
"sizes": "64x64 32x32 24x24 16x16",
- "type": "image/x-icon"
+ "type": "image/svg+xml"
},
{
- "src": "logo192.png",
- "type": "image/png",
+ "src": "logo.svg",
+ "type": "image/svg+xml",
"sizes": "192x192"
},
{
- "src": "logo512.png",
- "type": "image/png",
+ "src": "logo.svg",
+ "type": "image/svg+xml",
"sizes": "512x512"
}
],
diff --git a/client/public/prism.css b/client/public/prism.css
deleted file mode 100644
index 3cad71f..0000000
--- a/client/public/prism.css
+++ /dev/null
@@ -1,446 +0,0 @@
-/**
- * One Dark theme for prism.js
- * Based on Atom's One Dark theme: https://github.com/atom/atom/tree/master/packages/one-dark-syntax
- */
-
-/**
- * One Dark colours (accurate as of commit 8ae45ca on 6 Sep 2018)
- * From colors.less
- * --mono-1: hsl(220, 14%, 71%);
- * --mono-2: hsl(220, 9%, 55%);
- * --mono-3: hsl(220, 10%, 40%);
- * --hue-1: hsl(187, 47%, 55%);
- * --hue-2: hsl(207, 82%, 66%);
- * --hue-3: hsl(286, 60%, 67%);
- * --hue-4: hsl(95, 38%, 62%);
- * --hue-5: hsl(355, 65%, 65%);
- * --hue-5-2: hsl(5, 48%, 51%);
- * --hue-6: hsl(29, 54%, 61%);
- * --hue-6-2: hsl(39, 67%, 69%);
- * --syntax-fg: hsl(220, 14%, 71%);
- * --syntax-bg: hsl(220, 13%, 18%);
- * --syntax-gutter: hsl(220, 14%, 45%);
- * --syntax-guide: hsla(220, 14%, 71%, 0.15);
- * --syntax-accent: hsl(220, 100%, 66%);
- * From syntax-variables.less
- * --syntax-selection-color: hsl(220, 13%, 28%);
- * --syntax-gutter-background-color-selected: hsl(220, 13%, 26%);
- * --syntax-cursor-line: hsla(220, 100%, 80%, 0.04);
- */
-
-code[class*="language-"],
-pre[class*="language-"] {
- background: hsl(220, 13%, 18%);
- color: hsl(220, 14%, 71%);
- text-shadow: 0 1px rgba(0, 0, 0, 0.3);
- font-family: "Fira Code", "Fira Mono", Menlo, Consolas, "DejaVu Sans Mono",
- monospace;
- direction: ltr;
- text-align: left;
- white-space: pre;
- word-spacing: normal;
- word-break: normal;
- line-height: 1.5;
- -moz-tab-size: 2;
- -o-tab-size: 2;
- tab-size: 2;
- -webkit-hyphens: none;
- -moz-hyphens: none;
- -ms-hyphens: none;
- hyphens: none;
-}
-
-/* Selection */
-code[class*="language-"]::-moz-selection,
-code[class*="language-"] *::-moz-selection,
-pre[class*="language-"] *::-moz-selection {
- background: hsl(220, 13%, 28%);
- color: inherit;
- text-shadow: none;
-}
-
-code[class*="language-"]::selection,
-code[class*="language-"] *::selection,
-pre[class*="language-"] *::selection {
- background: hsl(220, 13%, 28%);
- color: inherit;
- text-shadow: none;
-}
-
-/* Code blocks */
-pre[class*="language-"] {
- padding: 1em;
- margin: 0.5em 0;
- overflow: auto;
- border-radius: 0.3em;
-}
-
-/* Inline code */
-:not(pre) > code[class*="language-"] {
- padding: 0.2em 0.3em;
- border-radius: 0.3em;
- white-space: normal;
-}
-
-/* Print */
-@media print {
- code[class*="language-"],
- pre[class*="language-"] {
- text-shadow: none;
- }
-}
-
-.token.comment,
-.token.prolog,
-.token.cdata {
- color: hsl(220, 10%, 40%);
-}
-
-.token.property,
-.token.tag,
-.token.symbol,
-.token.deleted,
-.token.important {
- color: hsl(355, 65%, 65%);
-}
-
-.token.doctype,
-.token.punctuation,
-.token.entity {
- color: hsl(220, 14%, 71%);
-}
-
-.token.attr-name,
-.token.class-name,
-.token.boolean,
-.token.constant,
-.token.number,
-.token.atrule {
- color: hsl(29, 54%, 61%);
-}
-
-.token.keyword {
- color: hsl(286, 60%, 67%);
-}
-
-.token.selector,
-.token.string,
-.token.char,
-.token.builtin,
-.token.inserted,
-.token.regex,
-.token.attr-value,
-.token.attr-value > .token.punctuation {
- color: hsl(95, 38%, 62%);
-}
-
-.token.variable,
-.token.operator,
-.token.function {
- color: hsl(207, 82%, 66%);
-}
-
-.token.url {
- color: hsl(187, 47%, 55%);
-}
-
-/* HTML overrides */
-.token.attr-value > .token.punctuation.attr-equals,
-.token.special-attr > .token.attr-value > .token.value.css {
- color: hsl(220, 14%, 71%);
-}
-
-/* CSS overrides */
-.language-css .token.selector {
- color: hsl(355, 65%, 65%);
-}
-
-.language-css .token.property {
- color: hsl(220, 14%, 71%);
-}
-
-.language-css .token.function,
-.language-css .token.url > .token.function {
- color: hsl(187, 47%, 55%);
-}
-
-.language-css .token.url > .token.string.url {
- color: hsl(95, 38%, 62%);
-}
-
-.language-css .token.important,
-.language-css .token.atrule .token.rule {
- color: hsl(286, 60%, 67%);
-}
-
-/* JS overrides */
-.language-javascript .token.operator {
- color: hsl(286, 60%, 67%);
-}
-
-.language-javascript
- .token.template-string
- > .token.interpolation
- > .token.interpolation-punctuation.punctuation {
- color: hsl(5, 48%, 51%);
-}
-
-/* JSON overrides */
-.language-json .token.operator {
- color: hsl(220, 14%, 71%);
-}
-
-.language-json .token.null.keyword {
- color: hsl(29, 54%, 61%);
-}
-
-/* MD overrides */
-.language-markdown .token.url,
-.language-markdown .token.url > .token.operator,
-.language-markdown .token.url-reference.url > .token.string {
- color: hsl(220, 14%, 71%);
-}
-
-.language-markdown .token.url > .token.content {
- color: hsl(207, 82%, 66%);
-}
-
-.language-markdown .token.url > .token.url,
-.language-markdown .token.url-reference.url {
- color: hsl(187, 47%, 55%);
-}
-
-.language-markdown .token.blockquote.punctuation,
-.language-markdown .token.hr.punctuation {
- color: hsl(220, 10%, 40%);
- font-style: italic;
-}
-
-.language-markdown .token.code-snippet {
- color: hsl(95, 38%, 62%);
-}
-
-.language-markdown .token.bold .token.content {
- color: hsl(29, 54%, 61%);
-}
-
-.language-markdown .token.italic .token.content {
- color: hsl(286, 60%, 67%);
-}
-
-.language-markdown .token.strike .token.content,
-.language-markdown .token.strike .token.punctuation,
-.language-markdown .token.list.punctuation,
-.language-markdown .token.title.important > .token.punctuation {
- color: hsl(355, 65%, 65%);
-}
-
-/* General */
-.token.bold {
- font-weight: bold;
-}
-
-.token.comment,
-.token.italic {
- font-style: italic;
-}
-
-.token.entity {
- cursor: help;
-}
-
-.token.namespace {
- opacity: 0.8;
-}
-
-/* Plugin overrides */
-/* Selectors should have higher specificity than those in the plugins' default stylesheets */
-
-/* Show Invisibles plugin overrides */
-.token.token.tab:not(:empty):before,
-.token.token.cr:before,
-.token.token.lf:before,
-.token.token.space:before {
- color: hsla(220, 14%, 71%, 0.15);
- text-shadow: none;
-}
-
-/* Toolbar plugin overrides */
-/* Space out all buttons and move them away from the right edge of the code block */
-div.code-toolbar > .toolbar.toolbar > .toolbar-item {
- margin-right: 0.4em;
-}
-
-/* Styling the buttons */
-div.code-toolbar > .toolbar.toolbar > .toolbar-item > button,
-div.code-toolbar > .toolbar.toolbar > .toolbar-item > a,
-div.code-toolbar > .toolbar.toolbar > .toolbar-item > span {
- background: hsl(220, 13%, 26%);
- color: hsl(220, 9%, 55%);
- padding: 0.1em 0.4em;
- border-radius: 0.3em;
-}
-
-div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:hover,
-div.code-toolbar > .toolbar.toolbar > .toolbar-item > button:focus,
-div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:hover,
-div.code-toolbar > .toolbar.toolbar > .toolbar-item > a:focus,
-div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:hover,
-div.code-toolbar > .toolbar.toolbar > .toolbar-item > span:focus {
- background: hsl(220, 13%, 28%);
- color: hsl(220, 14%, 71%);
-}
-
-/* Line Highlight plugin overrides */
-/* The highlighted line itself */
-.line-highlight.line-highlight {
- background: hsla(220, 100%, 80%, 0.04);
-}
-
-/* Default line numbers in Line Highlight plugin */
-.line-highlight.line-highlight:before,
-.line-highlight.line-highlight[data-end]:after {
- background: hsl(220, 13%, 26%);
- color: hsl(220, 14%, 71%);
- padding: 0.1em 0.6em;
- border-radius: 0.3em;
- box-shadow: 0 2px 0 0 rgba(0, 0, 0, 0.2); /* same as Toolbar plugin default */
-}
-
-/* Hovering over a linkable line number (in the gutter area) */
-/* Requires Line Numbers plugin as well */
-pre[id].linkable-line-numbers.linkable-line-numbers
- span.line-numbers-rows
- > span:hover:before {
- background-color: hsla(220, 100%, 80%, 0.04);
-}
-
-/* Line Numbers and Command Line plugins overrides */
-/* Line separating gutter from coding area */
-.line-numbers.line-numbers .line-numbers-rows,
-.command-line .command-line-prompt {
- border-right-color: hsla(220, 14%, 71%, 0.15);
-}
-
-/* Stuff in the gutter */
-.line-numbers .line-numbers-rows > span:before,
-.command-line .command-line-prompt > span:before {
- color: hsl(220, 14%, 45%);
-}
-
-/* Match Braces plugin overrides */
-/* Note: Outline colour is inherited from the braces */
-.rainbow-braces .token.token.punctuation.brace-level-1,
-.rainbow-braces .token.token.punctuation.brace-level-5,
-.rainbow-braces .token.token.punctuation.brace-level-9 {
- color: hsl(355, 65%, 65%);
-}
-
-.rainbow-braces .token.token.punctuation.brace-level-2,
-.rainbow-braces .token.token.punctuation.brace-level-6,
-.rainbow-braces .token.token.punctuation.brace-level-10 {
- color: hsl(95, 38%, 62%);
-}
-
-.rainbow-braces .token.token.punctuation.brace-level-3,
-.rainbow-braces .token.token.punctuation.brace-level-7,
-.rainbow-braces .token.token.punctuation.brace-level-11 {
- color: hsl(207, 82%, 66%);
-}
-
-.rainbow-braces .token.token.punctuation.brace-level-4,
-.rainbow-braces .token.token.punctuation.brace-level-8,
-.rainbow-braces .token.token.punctuation.brace-level-12 {
- color: hsl(286, 60%, 67%);
-}
-
-/* Diff Highlight plugin overrides */
-/* Taken from https://github.com/atom/github/blob/master/styles/variables.less */
-pre.diff-highlight > code .token.token.deleted:not(.prefix),
-pre > code.diff-highlight .token.token.deleted:not(.prefix) {
- background-color: hsla(353, 100%, 66%, 0.15);
-}
-
-pre.diff-highlight > code .token.token.deleted:not(.prefix)::-moz-selection,
-pre.diff-highlight > code .token.token.deleted:not(.prefix) *::-moz-selection,
-pre > code.diff-highlight .token.token.deleted:not(.prefix)::-moz-selection,
-pre > code.diff-highlight .token.token.deleted:not(.prefix) *::-moz-selection {
- background-color: hsla(353, 95%, 66%, 0.25);
-}
-
-pre.diff-highlight > code .token.token.deleted:not(.prefix)::selection,
-pre.diff-highlight > code .token.token.deleted:not(.prefix) *::selection,
-pre > code.diff-highlight .token.token.deleted:not(.prefix)::selection,
-pre > code.diff-highlight .token.token.deleted:not(.prefix) *::selection {
- background-color: hsla(353, 95%, 66%, 0.25);
-}
-
-pre.diff-highlight > code .token.token.inserted:not(.prefix),
-pre > code.diff-highlight .token.token.inserted:not(.prefix) {
- background-color: hsla(137, 100%, 55%, 0.15);
-}
-
-pre.diff-highlight > code .token.token.inserted:not(.prefix)::-moz-selection,
-pre.diff-highlight > code .token.token.inserted:not(.prefix) *::-moz-selection,
-pre > code.diff-highlight .token.token.inserted:not(.prefix)::-moz-selection,
-pre > code.diff-highlight .token.token.inserted:not(.prefix) *::-moz-selection {
- background-color: hsla(135, 73%, 55%, 0.25);
-}
-
-pre.diff-highlight > code .token.token.inserted:not(.prefix)::selection,
-pre.diff-highlight > code .token.token.inserted:not(.prefix) *::selection,
-pre > code.diff-highlight .token.token.inserted:not(.prefix)::selection,
-pre > code.diff-highlight .token.token.inserted:not(.prefix) *::selection {
- background-color: hsla(135, 73%, 55%, 0.25);
-}
-
-/* Previewers plugin overrides */
-/* Based on https://github.com/atom-community/atom-ide-datatip/blob/master/styles/atom-ide-datatips.less and https://github.com/atom/atom/blob/master/packages/one-dark-ui */
-/* Border around popup */
-.prism-previewer.prism-previewer:before,
-.prism-previewer-gradient.prism-previewer-gradient div {
- border-color: hsl(224, 13%, 17%);
-}
-
-/* Angle and time should remain as circles and are hence not included */
-.prism-previewer-color.prism-previewer-color:before,
-.prism-previewer-gradient.prism-previewer-gradient div,
-.prism-previewer-easing.prism-previewer-easing:before {
- border-radius: 0.3em;
-}
-
-/* Triangles pointing to the code */
-.prism-previewer.prism-previewer:after {
- border-top-color: hsl(224, 13%, 17%);
-}
-
-.prism-previewer-flipped.prism-previewer-flipped.after {
- border-bottom-color: hsl(224, 13%, 17%);
-}
-
-/* Background colour within the popup */
-.prism-previewer-angle.prism-previewer-angle:before,
-.prism-previewer-time.prism-previewer-time:before,
-.prism-previewer-easing.prism-previewer-easing {
- background: hsl(219, 13%, 22%);
-}
-
-/* For angle, this is the positive area (eg. 90deg will display one quadrant in this colour) */
-/* For time, this is the alternate colour */
-.prism-previewer-angle.prism-previewer-angle circle,
-.prism-previewer-time.prism-previewer-time circle {
- stroke: hsl(220, 14%, 71%);
- stroke-opacity: 1;
-}
-
-/* Stroke colours of the handle, direction point, and vector itself */
-.prism-previewer-easing.prism-previewer-easing circle,
-.prism-previewer-easing.prism-previewer-easing path,
-.prism-previewer-easing.prism-previewer-easing line {
- stroke: hsl(220, 14%, 71%);
-}
-
-/* Fill colour of the handle */
-.prism-previewer-easing.prism-previewer-easing circle {
- fill: transparent;
-}
diff --git a/client/public/robots.txt b/client/public/robots.txt
deleted file mode 100644
index 01b0f9a..0000000
--- a/client/public/robots.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-# https://www.robotstxt.org/robotstxt.html
-User-agent: *
diff --git a/client/rsbuild-plugins/plugin-prime-themes.ts b/client/rsbuild-plugins/plugin-prime-themes.ts
new file mode 100644
index 0000000..ef37d89
--- /dev/null
+++ b/client/rsbuild-plugins/plugin-prime-themes.ts
@@ -0,0 +1,102 @@
+import fs from 'fs';
+import path from 'path';
+
+import type { RsbuildPlugin } from '@rsbuild/core';
+
+/** Theme names to copy from primereact/resources/themes (only these are used at runtime) */
+const PRIME_THEMES = ['lara-dark-blue', 'lara-light-blue'] as const;
+
+const PRIME_THEMES_SOURCE = 'node_modules/primereact/resources/themes';
+
+/* eslint-disable @typescript-eslint/naming-convention -- file extensions for MIME */
+const MIME: Record = {
+ '.css': 'text/css',
+ '.woff2': 'font/woff2',
+ '.woff': 'font/woff',
+};
+/* eslint-enable @typescript-eslint/naming-convention */
+
+/**
+ * Rsbuild plugin: PrimeReact themes as build-time assets only.
+ * - Build: copies theme files from node_modules to dist/themes/ (no themes in public or git).
+ * - Dev: serves /themes/* from node_modules via middleware.
+ */
+export function pluginPrimeThemes(options?: { themes?: string[] }): RsbuildPlugin {
+ const themes = options?.themes ?? [...PRIME_THEMES];
+
+ return {
+ name: 'plugin-prime-themes',
+ setup(api) {
+ api.modifyRsbuildConfig((config) => {
+ const root = config.root ?? process.cwd();
+
+ // Build: copy PrimeReact themes from node_modules to dist/themes/
+ const existingCopy = config.output?.copy;
+ const copyPatterns = themes.map((name) => ({
+ from: path.join(root, PRIME_THEMES_SOURCE, name),
+ to: `themes/${name}`,
+ }));
+ const mergedCopy = existingCopy
+ ? Array.isArray(existingCopy)
+ ? [...existingCopy, ...copyPatterns]
+ : [existingCopy, ...copyPatterns]
+ : copyPatterns;
+ const output = config.output ?? {};
+ config.output = { ...output, copy: mergedCopy as typeof output.copy };
+
+ // Dev: serve /themes/* from node_modules (no public/themes needed)
+ const themesDir = path.resolve(root, PRIME_THEMES_SOURCE);
+ const existingMiddlewares = config.dev?.setupMiddlewares;
+ const setup = (middlewares: { push: (...handlers: unknown[]) => void }, context: unknown) => {
+ if (existingMiddlewares) {
+ const fns = Array.isArray(existingMiddlewares) ? existingMiddlewares : [existingMiddlewares];
+ fns.forEach((fn) => {
+ (fn as (m: typeof middlewares, c: typeof context) => void)(middlewares, context);
+ });
+ }
+ middlewares.push(
+ (
+ req: { url?: string },
+ res: { setHeader: (a: string, b: string) => void; end: (data?: Buffer) => void },
+ next: () => void,
+ ) => {
+ const url = req.url?.split('?')[0] ?? '';
+ if (!url.startsWith('/themes/')) {
+ next();
+ return;
+ }
+ const subpath = url.slice('/themes/'.length);
+ if (!subpath || subpath.includes('..')) {
+ next();
+ return;
+ }
+ const file = path.join(themesDir, subpath);
+ const resolved = path.normalize(path.resolve(file));
+ const allowed = path.normalize(path.resolve(themesDir));
+ if (!resolved.startsWith(allowed) || !fs.existsSync(resolved)) {
+ next();
+ return;
+ }
+ const stat = fs.statSync(resolved);
+ if (!stat.isFile()) {
+ next();
+ return;
+ }
+ const ext = path.extname(resolved);
+ const mime = MIME[ext] ?? 'application/octet-stream';
+ res.setHeader('Content-Type', mime);
+ res.end(fs.readFileSync(resolved));
+ },
+ );
+ };
+ const dev = config.dev ?? {};
+ config.dev = {
+ ...dev,
+ setupMiddlewares: (existingMiddlewares
+ ? [...(Array.isArray(existingMiddlewares) ? existingMiddlewares : [existingMiddlewares]), setup]
+ : [setup]) as typeof dev.setupMiddlewares,
+ };
+ });
+ },
+ };
+}
diff --git a/client/rsbuild.config.ts b/client/rsbuild.config.ts
new file mode 100644
index 0000000..3dd17b3
--- /dev/null
+++ b/client/rsbuild.config.ts
@@ -0,0 +1,49 @@
+import { defineConfig } from '@rsbuild/core';
+import { pluginReact } from '@rsbuild/plugin-react';
+import { pluginSass } from '@rsbuild/plugin-sass';
+
+import pkgJson from './package.json';
+import { pluginPrimeThemes } from './rsbuild-plugins/plugin-prime-themes';
+
+const defaultPort = 3024;
+const SERVER_PORT = process.env.SERVER_PORT ?? defaultPort;
+
+// Base path for GitHub Pages (set via GITHUB_PAGES_BASE_PATH env var)
+// For project pages: /repo-name/
+// For user/organization pages: /
+const basePath = process.env.GITHUB_PAGES_BASE_PATH ?? '/';
+console.log(`Using base path: "${basePath}"`);
+
+const version = pkgJson.version;
+
+export default defineConfig({
+ plugins: [pluginReact(), pluginSass(), pluginPrimeThemes()],
+ html: {
+ template: './public/index.html',
+ },
+ output: {
+ distPath: {
+ root: '../dist',
+ },
+ assetPrefix: basePath,
+ },
+ source: {
+ define: {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ __GITHUB_PAGES_BASE_PATH__: JSON.stringify(basePath),
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ __VERSION__: JSON.stringify(version),
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ __SERVER_PORT__: JSON.stringify(SERVER_PORT),
+ },
+ },
+ server: {
+ proxy: {
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ '/api': {
+ target: `http://127.0.0.1:${SERVER_PORT}`,
+ },
+ },
+ open: true,
+ },
+});
diff --git a/client/src/App.less b/client/src/App.less
deleted file mode 100644
index fd7a9a5..0000000
--- a/client/src/App.less
+++ /dev/null
@@ -1,81 +0,0 @@
-@import url('./utils/utils.less');
-
-* {
- margin: 0;
- padding: 0;
- // font-size: 16px;
- box-sizing: border-box;
-}
-body {
- font-family: 'LXGW WenKai Screen R', Calibri, Arial, sans-serif;
-}
-*::-webkit-scrollbar {
- width: 10px;
- min-height: 10px;
- border-radius: 15px;
- background-color: transparent;
- &:hover {
- opacity: 0.7;
- }
-}
-*::-webkit-scrollbar-corner {
- background-color: transparent;
-}
-*::-webkit-scrollbar-track {
- border-radius: 15px;
- background-color: transparent;
-}
-*::-webkit-scrollbar-thumb {
- box-shadow: inset 0 0 2px rgb(114, 166, 226);
- border-radius: 15px;
- background-color: transparent;
- &:hover {
- background-color: rgb(149, 146, 146);
- }
-}
-
-.input {
- // width: 100%;
- // height: 4rem;
- padding: 0 10px;
- outline: none;
- // border-radius: 5px;
- border: 0.5px solid #e7e0e0;
- // margin-bottom: 10px;
- // font-size: 1.5rem;
- position: relative;
- &:focus {
- // border: 1px solid #4cade6;
- // box-shadow: 0 0 2px 2px #62b5ec;
- border-color: #62b5ec;
- }
-}
-.btn {
- width: fit-content;
- height: fit-content;
- padding: 10px;
- outline: none;
- border: none;
- border-radius: 5px;
- cursor: pointer;
- text-decoration: none;
- display: block;
-}
-
-.container {
- width: 100vw;
- height: 100vh;
- background-color: @backgroundColor;
- // #ECEFF4
- // #252932
- position: relative;
- display: flex;
- justify-content: flex-start;
- align-items: flex-start;
- transition: @transition;
- // overflow: auto;
- .content-area {
- width: 60rem;
- height: fit-content;
- }
-}
diff --git a/client/src/App.scss b/client/src/App.scss
new file mode 100644
index 0000000..f85e101
--- /dev/null
+++ b/client/src/App.scss
@@ -0,0 +1,83 @@
+@use './utils/utils.scss' as *;
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+}
+
+.input {
+ // width: 100%;
+ // height: 4rem;
+ padding: 0 10px;
+ outline: none;
+ // border-radius: 5px;
+ border: 0.5px solid #e7e0e0;
+ // margin-bottom: 10px;
+ // font-size: 1.5rem;
+ position: relative;
+ &:focus {
+ // border: 1px solid #4cade6;
+ // box-shadow: 0 0 2px 2px #62b5ec;
+ border-color: #62b5ec;
+ }
+}
+.btn {
+ width: fit-content;
+ height: fit-content;
+ padding: 10px;
+ outline: none;
+ border: none;
+ border-radius: 5px;
+ cursor: pointer;
+ text-decoration: none;
+ display: block;
+}
+
+.app-container {
+ display: flex;
+ height: calc(100vh - 26px);
+}
+
+.app-loading-container {
+ @include flex-center();
+ height: 100vh;
+ width: 100vw;
+}
+
+#container {
+ width: calc(100vw - 45px);
+ background-color: $backgroundColor;
+ .content-area {
+ width: 60rem;
+ height: fit-content;
+ }
+ .split-bar {
+ &:hover {
+ background-color: rgba(255, 255, 255, 0.3);
+ }
+ }
+}
+
+.tool-tip {
+ .p-tooltip-text {
+ font-size: 14px;
+ }
+}
+
+.p-scrollpanel-content {
+ padding: 0;
+}
+.p-scrollpanel-bar {
+ background-color: rgba(#f3f5f6, 0.7);
+}
+
+.p-inputtext {
+ box-shadow: none;
+}
+
+.p-confirm-dialog-message {
+ margin: 0;
+}
diff --git a/client/src/App.tsx b/client/src/App.tsx
index cedcaa3..fd9fded 100644
--- a/client/src/App.tsx
+++ b/client/src/App.tsx
@@ -1,19 +1,74 @@
-import React from 'react';
+import Split from '@uiw/react-split';
+import { PrimeReactProvider } from 'primereact/api';
+import { ConfirmDialog } from 'primereact/confirmdialog';
+import { FC, useEffect } from 'react';
+import { useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
-import EditorContainer from './components/EditorContainer/EditorContainer';
-import Menu from './components/Menu/MenuContainer';
-import { useShortCut } from './utils/hooks/tools';
+import { EditorContainer } from './components/EditorContainer/EditorContainer';
+import { Footer } from './components/Footer/Footer';
+import { Menu } from './components/Menu/Menu';
+import { Sidebar } from './components/Sidebar/Sidebar';
+import { SplitBar } from './components/SplitBar';
+import { APP_VERSION } from './constants';
+import { selectMenuCollapse } from './redux-feature/globalOptsSlice';
+import { useCheckServer, useWarnUnsavedOnUnload } from './utils/hooks/reduxHooks';
-import './App.less';
+import './App.scss';
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function App() {
- useShortCut();
+export const App: FC = () => {
+ useWarnUnsavedOnUnload();
+
+ const { isLoading, isError, isSuccess, data: serverCheckRes } = useCheckServer();
+ const menuCollapse = useSelector(selectMenuCollapse);
+ const navigate = useNavigate();
+
+ const showMenu = !isError && !menuCollapse;
+
+ useEffect(() => {
+ if (isError) {
+ void navigate('/internal/guide');
+ return;
+ }
+
+ if (!isLoading && serverCheckRes?.version !== APP_VERSION) {
+ void navigate('/internal/version-mismatch');
+ }
+ }, [isError, isSuccess, serverCheckRes]);
+
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
return (
-
-
-
-
+
+
+
+
+
+ {/* re-rendering menu to select current doc */}
+ {showMenu && (
+
+
+
+ )}
+
+
+
+
+
+
+
);
-}
+};
diff --git a/client/src/components/Cascader/Cascader.scss b/client/src/components/Cascader/Cascader.scss
new file mode 100644
index 0000000..235b942
--- /dev/null
+++ b/client/src/components/Cascader/Cascader.scss
@@ -0,0 +1,33 @@
+@use '@/utils/utils.scss' as *;
+
+.cascader-container {
+ width: 600px;
+ height: 600px;
+ overflow: auto;
+ border: 1px solid $shadowColor;
+ border-radius: 6px;
+ .cascader-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ }
+ .box-list {
+ height: 100%;
+ width: fit-content;
+ @include flex-center();
+ .list-item-wrapper {
+ width: 210px;
+ height: 100%;
+ overflow: auto;
+ @include flex-center();
+ &:nth-child(n + 1) {
+ border-right: 1px solid $shadowColor;
+ }
+ }
+ .p-listbox {
+ width: 100%;
+ border: none;
+ height: 100%;
+ }
+ }
+}
diff --git a/client/src/components/Cascader/Cascader.tsx b/client/src/components/Cascader/Cascader.tsx
new file mode 100644
index 0000000..b2c5fe2
--- /dev/null
+++ b/client/src/components/Cascader/Cascader.tsx
@@ -0,0 +1,69 @@
+import { ListBox, ListBoxChangeEvent, ListBoxProps } from 'primereact/listbox';
+import { FC, useState } from 'react';
+
+import './Cascader.scss';
+
+export interface CascaderItemValue {
+ path: string[];
+}
+
+export interface CascaderListItem {
+ label: string;
+ value: CascaderItemValue;
+}
+
+interface CascaderProps {
+ /** An array of list, represented as levels */
+ data: CascaderListItem[][];
+ onSelectItem?: (value: CascaderItemValue) => void;
+}
+
+const ListItemWrapper: FC = (props) => {
+ const [selectValue, setSelectValue] = useState(null);
+ return (
+ {
+ if (!e.value) {
+ props.onChange?.({ ...e, value: selectValue });
+ return;
+ }
+
+ setSelectValue(e.value);
+ props.onChange?.(e);
+ }}
+ />
+ );
+};
+
+export const Cascader: FC = ({ data, onSelectItem }) => {
+ const itemTemplate = (item: CascaderListItem) => {
+ return (
+
+ );
+ };
+
+ const handleSelectItem = (event: ListBoxChangeEvent) => {
+ onSelectItem?.(event.value);
+ };
+
+ return (
+
+
+ {data.map((list, level) => (
+
+ {list.length ? (
+
+ ) : (
+ 'Empty'
+ )}
+
+ ))}
+
+
+ );
+};
diff --git a/client/src/components/ConfigBox/ConfigBox.less b/client/src/components/ConfigBox/ConfigBox.less
deleted file mode 100644
index 7527f49..0000000
--- a/client/src/components/ConfigBox/ConfigBox.less
+++ /dev/null
@@ -1,86 +0,0 @@
-@import url("@/utils/utils.less");
-
-.config-form {
- width: 42rem;
- max-height: 30rem;
- overflow: auto;
- padding: 1rem 0;
- .config-label {
- font-weight: bold;
- display: block;
- margin-bottom: 0.5rem;
- font-size: 20px;
- }
- .config-input {
- width: 25rem;
- height: 2rem;
- padding: 0 0.5rem;
- font-size: 20px;
- outline: none;
- border-radius: 5px;
- border: 0.5px solid rgb(117, 114, 114);
- &:focus {
- border: 0.5px solid #62b5ec;
- }
- margin-bottom: 1rem;
- }
- .arr-config {
- display: flex;
- width: 40rem;
- flex-wrap: wrap;
- justify-content: flex-start;
- .arr-config-item {
- list-style: none;
- width: fit-content;
- padding: 0.5rem;
- background-color: #e6e6e6;
- margin: 0.3rem 0.3rem;
- position: relative;
- border-radius: 5px;
- input {
- width: 10rem;
- height: 1.5rem;
- padding: 0.5rem;
- font-size: 18px;
- margin: 0;
- }
- .close-tag {
- position: absolute;
- width: 1rem;
- height: 1rem;
- border-radius: 50%;
- background-color: rgb(163, 157, 157);
- text-align: center;
- line-height: 1rem;
- right: -0.5rem;
- top: -0.5rem;
- cursor: pointer;
- display: none;
- }
- &:hover {
- .close-tag {
- display: block;
- }
- }
- }
- }
- .add-ignore-dir {
- margin-left: 0.5rem;
- width: 1.5rem;
- height: 1.5rem;
- cursor: pointer;
- position: relative;
- &:hover {
- &::after {
- content: "";
- position: absolute;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.2);
- border-radius: 5px;
- }
- }
- }
-}
diff --git a/client/src/components/ConfigBox/ConfigBox.tsx b/client/src/components/ConfigBox/ConfigBox.tsx
deleted file mode 100644
index b071d60..0000000
--- a/client/src/components/ConfigBox/ConfigBox.tsx
+++ /dev/null
@@ -1,173 +0,0 @@
-/* eslint-disable @typescript-eslint/no-magic-numbers */
-import React, { useEffect, useRef, useState } from 'react';
-
-import Modal from '../../utils/Modal/Modal';
-
-import { useGetConfigsQuery, useUpdateConfigsMutation, ConfigType } from '@/redux-api/configApi';
-import Toast from '@/utils/Toast';
-import { isEqual } from '@/utils/utils';
-import './ConfigBox.less';
-export interface ConfigBoxProps {
- setShow: React.Dispatch>;
-}
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function ConfigBox({ setShow }: ConfigBoxProps) {
- const {
- data: { configs, err } = {
- configs: null,
- err: 1,
- },
- isSuccess,
- } = useGetConfigsQuery();
-
- const [ignoreDirs, setIgnoreDirs] = useState([]);
- const formRef = useRef(null);
- const ignoreDirsRef = useRef([]);
-
- const [updateConfigsMutation] = useUpdateConfigsMutation();
-
- const updateConfigs = async () => {
- if (!formRef.current) return;
-
- const newConfigs: Record = {};
-
- const formData = new FormData(formRef.current);
-
- for (const [key, value] of formData.entries()) {
- newConfigs[key] = value;
- }
-
- newConfigs.ignoreDirs = [
- ...new Set(ignoreDirsRef.current.map((ref) => ref.value).filter((dir) => dir.trim() !== '')),
- ];
-
- // check if it is changed
- if (isEqual(newConfigs, configs as unknown as Record)) {
- Toast('no changes', 'WARNING');
- return;
- }
-
- try {
- const resp = await updateConfigsMutation(newConfigs as unknown as ConfigType).unwrap();
-
- if (resp.err === 1) {
- Toast(resp.message, 'ERROR', 2000);
- } else {
- Toast(resp.message, 'SUCCESS');
- }
- } catch (e) {
- Toast(String(e), 'ERROR', 10000);
- }
- };
-
- const addIgDir = () => {
- setIgnoreDirs(
- // sync from the ref
- ignoreDirsRef.current.map((ref) => ref.value).concat(''),
- );
-
- // clear, get the refs again when rendering again due to setIgnoreDirs
- ignoreDirsRef.current = [];
- };
-
- const deleteIgDir = (deleteIdx: number) => {
- // sync from the ref
- setIgnoreDirs(ignoreDirsRef.current.map((ref) => ref.value).filter((_, idx) => idx !== deleteIdx));
-
- // clear, get the refs again when rendering again due to setIgnoreDirs
- ignoreDirsRef.current = [];
- };
-
- useEffect(() => {
- if (!isSuccess || err === 1) return;
-
- if (configs?.ignoreDirs) {
- setIgnoreDirs(configs.ignoreDirs);
- }
- }, [configs, isSuccess, err]);
-
- const confirmCallback = async (setLoading: React.Dispatch>) => {
- setLoading(true);
- await updateConfigs();
- setLoading(false);
- };
-
- return (
- void confirmCallback(set)}>
-
-
- );
-}
diff --git a/client/src/components/DocMirror/DocMirror.less b/client/src/components/DocMirror/DocMirror.less
deleted file mode 100644
index 49296bd..0000000
--- a/client/src/components/DocMirror/DocMirror.less
+++ /dev/null
@@ -1,27 +0,0 @@
-@import url("@/utils/utils.less");
-
-.code-mirror-container {
- overflow: auto;
- height: 100%;
- // border-radius: 15px;
- .cm-editor {
- transition: @transition;
- height: 90vh;
- overflow-x: hidden;
- // border-radius: 15px;
- .cm-gutters {
- transition: @transition;
- }
- .cm-activeLine {
- transition: @transition;
- }
- .cm-activeLineGutter {
- transition: @transition;
- }
- .cm-content {
- transition: @transition;
- font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
- monospace;
- }
- }
-}
diff --git a/client/src/components/DocMirror/DocMirror.scss b/client/src/components/DocMirror/DocMirror.scss
new file mode 100644
index 0000000..c877ad8
--- /dev/null
+++ b/client/src/components/DocMirror/DocMirror.scss
@@ -0,0 +1,33 @@
+@use "@/utils/utils.scss" as *;
+@use "@/theme.scss" as *;
+
+@mixin code-mirror-style {
+ .cm-editor {
+ transition: $transition;
+ overflow-x: hidden;
+ // border-radius: 15px;
+ // .cm-gutters {
+ // transition: $transition;
+ // }
+ // .cm-activeLine {
+ // transition: $transition;
+ // }
+ .cm-activeLineGutter {
+ transition: $transition;
+ background-color: var(--crepe-color-surface-low);
+ }
+ .cm-content {
+ transition: $transition;
+ font-family: var(--code-font-family);
+ }
+ }
+}
+
+.code-mirror-container {
+ overflow: auto;
+ height: 100%;
+ @include code-mirror-style;
+ .cm-editor {
+ height: $editorHeight;
+ }
+}
diff --git a/client/src/components/DocMirror/DocMirror.tsx b/client/src/components/DocMirror/DocMirror.tsx
index f185872..2a876a4 100644
--- a/client/src/components/DocMirror/DocMirror.tsx
+++ b/client/src/components/DocMirror/DocMirror.tsx
@@ -4,61 +4,48 @@ import CodeMirror from '@uiw/react-codemirror';
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
-import { EditorWrappedRef } from '../EditorContainer/EditorContainer';
-
-import { selectCurContent, selectCurPath } from '@/redux-feature/curDocSlice';
+import { selectCurContent, selectCurDocIdent } from '@/redux-feature/curDocSlice';
import { selectGlobalOpts } from '@/redux-feature/globalOptsSlice';
import ErrorBoundary from '@/utils/ErrorBoundary/ErrorBoundary';
-import './DocMirror.less';
+import './DocMirror.scss';
export interface DocMirrorProps {
- unmount: boolean;
- editorRef: React.RefObject;
+ onChange: (value: string) => void;
}
-const MirrorWrapper = ({ editorRef }: { editorRef: React.RefObject }) => {
- const { isDarkMode, isEditorBlur } = useSelector(selectGlobalOpts);
+export const DocMirror: React.FC = ({ onChange }) => {
+ const { theme, isEditorBlur, readonly } = useSelector(selectGlobalOpts);
const globalContent = useSelector(selectCurContent);
- const contentPath = useSelector(selectCurPath);
+ const curDocIdent = useSelector(selectCurDocIdent);
const [mirrorVal, setMirrorVal] = useState('');
useEffect(() => {
- // only when editing the editor, sync the code at mirror
- if (!isEditorBlur) setMirrorVal(globalContent);
+ // set the new value for mirror when switch to new doc
+ setMirrorVal(globalContent);
// eslint-disable-next-line
- }, [globalContent]);
+ }, [curDocIdent]);
useEffect(() => {
- // set the new value for mirror when switch to new doc
- setMirrorVal(globalContent);
+ // only when editing the editor, sync the code at mirror
+ if (!isEditorBlur) {
+ setMirrorVal(globalContent);
+ }
// eslint-disable-next-line
- }, [contentPath]);
+ }, [globalContent]);
return (
- {
- if (isEditorBlur && editorRef.current && value !== globalContent) {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- editorRef.current.update(value);
- }
- }}
- />
+
+
+
);
};
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function DocMirror({ unmount, editorRef }: DocMirrorProps) {
- return (
-
- {/* doesn't need to render when it is at the backend */}
- {!unmount && }
-
- );
-}
diff --git a/client/src/components/DocSearch/DocSearch.less b/client/src/components/DocSearch/DocSearch.less
deleted file mode 100644
index 8e46b52..0000000
--- a/client/src/components/DocSearch/DocSearch.less
+++ /dev/null
@@ -1,98 +0,0 @@
-@import url("@/utils/utils.less");
-
-.search-box {
- @width: 20rem;
- @height: 2rem;
- margin: 0 1rem;
- height: @height;
- position: relative;
- .search-input {
- width: @width;
- height: 100%;
- line-height: @height;
- font-size: 16px;
- border-radius: 15px;
- }
- .result-wrapper {
- position: absolute;
- left: 0;
- top: @height + 0.5rem;
- background-color: #e6e6e6;
- border-radius: 5px;
- padding: 0.5rem;
- font-weight: bold;
- box-shadow: @shadow;
- .result-info {
- margin-bottom: 1rem;
- }
- }
- .search-results-box {
- min-width: @width + 15rem;
- max-height: @width + 15rem;
- overflow: auto;
- display: flex;
- flex-direction: column;
- justify-content: flex-start;
- .result-item {
- width: 100%;
- height: fit-content;
- display: flex;
- flex-direction: column;
- padding: 0.5rem;
- border-bottom: 1px solid rgb(153, 151, 151);
- position: relative;
- color: black;
- .path-show {
- width: fit-content;
- height: fit-content;
- background-color: #60b6ef;
- padding: 0.3rem;
- font-size: 14px;
- border-radius: 5px;
- cursor: pointer;
- &:hover {
- background-color: #3aa1e5;
- }
- }
- .keyword-show {
- width: 100%;
- display: flex;
- justify-content: flex-start;
- align-items: center;
- flex-wrap: wrap;
- margin-top: 1rem;
- .keyword-item {
- padding: 0.5rem;
- margin-right: 0.3rem;
- background-color: #fff;
- font-size: 12px;
- font-weight: bold;
- border-radius: 5px;
- cursor: pointer;
- color: black;
- &:hover {
- background-color: #c5bbbb;
- }
- }
- }
- .heading-show {
- width: 100%;
- padding: 0.3rem 0;
- border-radius: 5px;
- background-color: #e6e6e6;
- margin-top: 0.3rem;
- .heading-item {
- width: 100%;
- cursor: pointer;
- background-color: #fff;
- margin: 0.3rem 0;
- padding: 0.3rem;
- border-radius: 5px;
- &:hover {
- background-color: #c5bbbb;
- }
- }
- }
- }
- }
-}
diff --git a/client/src/components/DocSearch/DocSearch.scss b/client/src/components/DocSearch/DocSearch.scss
new file mode 100644
index 0000000..5af6121
--- /dev/null
+++ b/client/src/components/DocSearch/DocSearch.scss
@@ -0,0 +1,336 @@
+@use "@/utils/utils.scss" as *;
+
+.doc-search-container {
+ margin: 0 1rem;
+ .search-trigger {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.35rem 0.6rem;
+ border: 1px solid var(--surface-border, #dee2e6);
+ border-radius: $borderRadius;
+ background: transparent;
+ color: $shallowTextColor;
+ cursor: pointer;
+ font-size: 0.85rem;
+ white-space: nowrap;
+ transition: $transition;
+
+ &:hover {
+ color: $headerTextColor;
+ border-color: $hoverBorderColor;
+ }
+
+ .search-trigger-icon {
+ font-size: 0.85rem;
+ }
+
+ .search-trigger-text {
+ margin-right: 0.25rem;
+ }
+
+ .search-trigger-shortcut {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.15rem;
+ font-family: inherit;
+ font-size: 0.75rem;
+ padding: 0.1rem 0.35rem;
+ border: 1px solid var(--surface-border, #dee2e6);
+ border-radius: 3px;
+ background-color: $hoverBgcColor;
+ line-height: 1.3;
+ }
+ }
+}
+
+.doc-search-dialog {
+ width: 600px;
+ background-color: $backgroundColor;
+
+ .search-panel {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ }
+
+ .search-mode-toggle.p-selectbutton {
+ display: flex;
+ width: 100%;
+
+ .p-button {
+ flex: 1;
+ justify-content: center;
+ font-size: 0.8rem;
+ padding: 0.4rem 0.75rem;
+ &.p-focus {
+ box-shadow: none;
+ }
+ }
+ }
+
+ .search-input-row {
+ display: flex;
+ align-items: center;
+ gap: 0.35rem;
+
+ .search-input-field {
+ flex: 1;
+ min-width: 0;
+
+ .search-input {
+ width: 100%;
+ }
+ }
+
+ .search-options {
+ display: flex;
+ gap: 2px;
+ flex-shrink: 0;
+
+ .search-option-btn {
+ width: 28px;
+ height: 28px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px solid transparent;
+ border-radius: 3px;
+ background: transparent;
+ color: $shallowTextColor;
+ cursor: pointer;
+ font-size: 0.8rem;
+ font-weight: 600;
+ transition: $transition;
+
+ &:hover {
+ background-color: $hoverBgcColor;
+ }
+
+ &.active {
+ color: $anchorColor;
+ border-color: $anchorColor;
+ }
+ }
+
+ .filter-toggle-btn {
+ display: flex;
+ align-items: center;
+ gap: 0.35rem;
+ padding: 0.25rem 0.5rem;
+ border: none;
+ background: transparent;
+ color: $shallowTextColor;
+ cursor: pointer;
+ font-size: 0.8rem;
+ border-radius: $borderRadius;
+ transition: $transition;
+
+ &:hover {
+ color: $headerTextColor;
+ background-color: $hoverBgcColor;
+ }
+
+ i {
+ font-size: 0.65rem;
+ }
+ }
+ }
+ }
+
+ .search-filters-section {
+ .search-filters {
+ display: flex;
+ flex-direction: column;
+ gap: 0.4rem;
+ padding: 0.35rem 0 0 1rem;
+
+ .filter-input {
+ width: 100%;
+ font-size: 0.85rem;
+ }
+ }
+ }
+
+ .search-results {
+ max-height: 400px;
+ overflow-y: auto;
+
+ .results-toolbar {
+ display: flex;
+ justify-content: flex-end;
+ padding: 0.25rem 0.75rem;
+ border-bottom: 1px solid var(--surface-border, #dee2e6);
+ margin-bottom: 0.25rem;
+ position: sticky;
+ top: 0;
+ background-color: $backgroundColor;
+ z-index: 2;
+ .collapse-all-btn {
+ display: flex;
+ align-items: center;
+ gap: 0.3rem;
+ padding: 0.2rem 0.5rem;
+ border: none;
+ background: transparent;
+ color: $shallowTextColor;
+ cursor: pointer;
+ font-size: 0.78rem;
+ border-radius: $borderRadius;
+ transition: $transition;
+
+ &:hover {
+ color: $headerTextColor;
+ background-color: $hoverBgcColor;
+ }
+
+ i {
+ font-size: 0.6rem;
+ }
+ }
+ }
+
+ .empty-results {
+ color: $shallowTextColor;
+ padding: 2rem 0;
+ @include flex-center();
+ }
+ }
+
+ .result-item {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.5rem 0.75rem;
+ cursor: pointer;
+ border-radius: $borderRadius;
+
+ &:hover {
+ background-color: $hoverBgcColor;
+ }
+
+ .result-icon {
+ flex-shrink: 0;
+ font-size: 1rem;
+ opacity: 0.6;
+ }
+
+ .result-info {
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+ gap: 0.15rem;
+
+ .result-name {
+ font-weight: 600;
+ @include text-overflow-omit();
+ }
+
+ .result-path {
+ font-size: 0.8rem;
+ color: $shallowTextColor;
+ @include text-overflow-omit();
+ }
+ }
+ }
+
+ .content-file-group {
+ margin-bottom: 0.25rem;
+
+ .content-file-header {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.5rem 0.75rem;
+ cursor: pointer;
+ border-radius: $borderRadius;
+ user-select: none;
+
+ &:hover {
+ background-color: $hoverBgcColor;
+ }
+
+ .collapse-icon {
+ flex-shrink: 0;
+ font-size: 0.7rem;
+ opacity: 0.5;
+ transition: $transition;
+ }
+
+ .result-icon {
+ flex-shrink: 0;
+ font-size: 1rem;
+ opacity: 0.6;
+ }
+
+ .result-name {
+ font-weight: 600;
+ white-space: nowrap;
+
+ &:hover {
+ text-decoration: underline;
+ color: $anchorColor;
+ }
+ }
+
+ .result-path {
+ font-size: 0.8rem;
+ color: $shallowTextColor;
+ flex: 1;
+ @include text-overflow-omit();
+ }
+
+ .match-count {
+ flex-shrink: 0;
+ font-size: 0.75rem;
+ background-color: $hoverBgcColor;
+ padding: 0.1rem 0.4rem;
+ border-radius: 10px;
+ min-width: 1.2rem;
+ text-align: center;
+ }
+ }
+
+ .content-matches {
+ .match-line {
+ display: flex;
+ align-items: baseline;
+ gap: 0.75rem;
+ padding: 0.25rem 0.75rem 0.25rem 2.75rem;
+ cursor: pointer;
+ border-radius: $borderRadius;
+ font-size: 0.85rem;
+
+ &:hover {
+ background-color: $hoverBgcColor;
+ }
+
+ .line-number {
+ flex-shrink: 0;
+ color: $shallowTextColor;
+ font-size: 0.8rem;
+ min-width: 2rem;
+ text-align: right;
+ }
+
+ .line-content {
+ flex: 1;
+ min-width: 0;
+ @include text-overflow-omit();
+
+ .match-highlight {
+ background-color: rgba(255, 213, 0, 0.35);
+ color: inherit;
+ border-radius: 2px;
+ padding: 0.05rem 0.1rem;
+ }
+
+ .match-ellipsis {
+ color: $shallowTextColor;
+ margin: 0 0.15rem;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/client/src/components/DocSearch/DocSearch.tsx b/client/src/components/DocSearch/DocSearch.tsx
index c606af5..5016d5d 100644
--- a/client/src/components/DocSearch/DocSearch.tsx
+++ b/client/src/components/DocSearch/DocSearch.tsx
@@ -1,217 +1,295 @@
-import React, { useCallback, useEffect, useRef, useState } from 'react';
+import { Dialog } from 'primereact/dialog';
+import { IconField } from 'primereact/iconfield';
+import { InputIcon } from 'primereact/inputicon';
+import { InputText } from 'primereact/inputtext';
+import { SelectButton } from 'primereact/selectbutton';
+import { FC, useCallback, useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
-import { useGetNorDocsQuery } from '@/redux-api/docsApi';
-import { useEditorScrollToAnchor } from '@/utils/hooks/docHooks';
+import { SearchResults } from './SearchResults';
+
+import {
+ FileContentMatches,
+ FileNameMatch,
+ useLazySearchContentQuery,
+ useLazySearchFilesQuery,
+} from '@/redux-api/searchApi';
import { useDebounce } from '@/utils/hooks/tools';
-import { hightLight, scrollToBottomListener } from '@/utils/utils';
-
-import './DocSearch.less';
-
-export interface SearchResult {
- path: string;
- keywords: string[];
- headings: string[];
-}
-
-const loadNum = 13;
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function SearchBar() {
- const { data: norDocs = {} } = useGetNorDocsQuery();
-
- const searchInputRef = useRef(null);
- const [resultShow, setResultShow] = useState(false);
- const [results, setResults] = useState([]);
- const [showNum, setShowNum] = useState(loadNum);
-
- const resultBoxRef = useRef(null);
-
- const scrollToAnchor = useEditorScrollToAnchor();
-
- const search = useCallback(
- (searchContent: string): { path: string; keywords: string[]; headings: string[] }[] => {
- const transformResults = Object.keys(norDocs)
- .filter((path) => norDocs[path].doc.isFile)
- .map((path) => ({
- path,
- keywords: norDocs[path].doc.keywords,
- headings: norDocs[path].doc.headings,
- }));
-
- // filtering based on previous searching keywords
- return searchContent.split(' ').reduce((rets, word) => {
- return rets.filter((ret) => {
- const { path } = ret;
- const { keywords, headings } = norDocs[path].doc;
-
- // if path is matched, then return directly (show all the headings and keywords)
- if (path.toLowerCase().includes(word.toLowerCase())) {
- return true;
- }
-
- const filteredKeywords: string[] = [];
- const filteredHeadings: string[] = [];
-
- // see if the keywords match the search content
- for (const keyword of keywords) {
- if (keyword.toLowerCase().includes(word.toLowerCase())) {
- filteredKeywords.push(keyword);
- }
- }
-
- // see if the headings match the search content
- for (const heading of headings) {
- if (heading.toLowerCase().includes(word.toLowerCase())) {
- filteredHeadings.push(heading);
- }
- }
-
- if (filteredKeywords.length !== 0 || filteredHeadings.length !== 0) {
- ret.keywords = filteredKeywords;
- ret.headings = filteredHeadings;
- return true;
- }
-
- return false;
- });
- }, transformResults);
+import { normalizePath } from '@/utils/utils';
+
+import './DocSearch.scss';
+
+const DEBOUNCE_MS = 350;
+
+type SearchMode = 'content' | 'files';
+
+const MODE_OPTIONS = [
+ { label: 'Files', value: 'files' },
+ { label: 'Content', value: 'content' },
+];
+
+export const DocSearch: FC = () => {
+ const [visible, setVisible] = useState(false);
+ const [searchMode, setSearchMode] = useState('files');
+ const [query, setQuery] = useState('');
+ const [matchCase, setMatchCase] = useState(false);
+ const [includeFiles, setIncludeFiles] = useState('');
+ const [excludeFiles, setExcludeFiles] = useState('');
+ const [showFilters, setShowFilters] = useState(false);
+
+ const [fileResults, setFileResults] = useState([]);
+ const [contentResults, setContentResults] = useState([]);
+ const [collapsedFiles, setCollapsedFiles] = useState>(new Set());
+
+ const [searchFiles] = useLazySearchFilesQuery();
+ const [searchContent] = useLazySearchContentQuery();
+ const navigate = useNavigate();
+
+ const performSearch = useCallback(
+ (q: string, mode: SearchMode, mc: boolean, inc: string, exc: string) => {
+ const trimmed = q.trim();
+ if (trimmed === '') {
+ if (mode === 'files') setFileResults([]);
+ else setContentResults([]);
+ return;
+ }
+ if (mode === 'files') {
+ searchFiles(trimmed)
+ .unwrap()
+ .then((data) => {
+ setFileResults(data ?? []);
+ })
+ .catch(() => {
+ setFileResults([]);
+ });
+ } else {
+ searchContent({
+ q: trimmed,
+ caseSensitive: mc,
+ includeFiles: inc.trim() || undefined,
+ excludeFiles: exc.trim() || undefined,
+ })
+ .unwrap()
+ .then((data) => {
+ setContentResults(data ?? []);
+ })
+ .catch(() => {
+ setContentResults([]);
+ })
+ .finally(() => {
+ setCollapsedFiles(new Set());
+ });
+ }
},
- [norDocs],
+ [searchFiles, searchContent, setCollapsedFiles],
);
- const handleSearch = useDebounce((e: React.ChangeEvent) => {
- setResults(search(e.target.value));
- // eslint-disable-next-line @typescript-eslint/no-magic-numbers
- }, 500);
+ const debouncedSearch = useDebounce(performSearch, DEBOUNCE_MS, [], false);
- const toResult = useCallback(
- (path: string, anchor: string) => {
- scrollToAnchor(anchor, path);
+ const handleQueryChange = useCallback(
+ (value: string) => {
+ setQuery(value);
+ debouncedSearch(value, searchMode, matchCase, includeFiles, excludeFiles);
},
- [scrollToAnchor],
+ [searchMode, matchCase, includeFiles, excludeFiles, debouncedSearch],
);
- // update when the norDoc changed (headings and keywords changed)
- useEffect(() => {
- if (searchInputRef.current && searchInputRef.current.value.trim() !== '') {
- setResults(search(searchInputRef.current.value));
+ const handleModeChange = useCallback(
+ (mode: SearchMode) => {
+ setSearchMode(mode);
+ performSearch(query, mode, matchCase, includeFiles, excludeFiles);
+ },
+ [query, matchCase, includeFiles, excludeFiles, performSearch],
+ );
+
+ const handleMatchCaseToggle = useCallback(() => {
+ const next = !matchCase;
+ setMatchCase(next);
+ if (searchMode === 'content') {
+ performSearch(query, searchMode, next, includeFiles, excludeFiles);
}
- // norDocs changes will lead to the change of the search function ref
- }, [search]);
+ }, [matchCase, searchMode, query, includeFiles, excludeFiles, performSearch]);
- /**
- * scroll down to the bottom to show more images
- */
- useEffect(() => {
- if (!resultBoxRef.current) return;
+ const handleIncludeChange = useCallback(
+ (value: string) => {
+ setIncludeFiles(value);
+ if (searchMode === 'content') {
+ debouncedSearch(query, searchMode, matchCase, value, excludeFiles);
+ }
+ },
+ [searchMode, query, matchCase, excludeFiles, debouncedSearch],
+ );
- // every time when the results changed, reset to only show loadNum results
- setShowNum(loadNum);
+ const handleExcludeChange = useCallback(
+ (value: string) => {
+ setExcludeFiles(value);
+ if (searchMode === 'content') {
+ debouncedSearch(query, searchMode, matchCase, includeFiles, value);
+ }
+ },
+ [searchMode, query, matchCase, includeFiles, debouncedSearch],
+ );
+
+ const navigateToDoc = useCallback(
+ (path: string[], searchContext?: { query: string; lineContent: string }) => {
+ const normalized = normalizePath(path);
+ void navigate(`/article/${normalized}`, {
+ state: searchContext ? { searchQuery: searchContext.query, lineContent: searchContext.lineContent } : undefined,
+ });
+ setVisible(false);
+ },
+ [navigate],
+ );
- const remover = scrollToBottomListener(resultBoxRef.current, () => {
- setShowNum((num) => (num + loadNum > results.length ? results.length : num + loadNum));
+ const toggleFileCollapse = useCallback((filePath: string) => {
+ setCollapsedFiles((prev) => {
+ const next = new Set(prev);
+ if (next.has(filePath)) {
+ next.delete(filePath);
+ } else {
+ next.add(filePath);
+ }
+ return next;
});
+ }, []);
+
+ const collapseAll = useCallback(() => {
+ setCollapsedFiles(new Set(contentResults.map((f) => normalizePath(f.path))));
+ }, [contentResults]);
+
+ const expandAll = useCallback(() => {
+ setCollapsedFiles(new Set());
+ }, []);
+
+ const openSearch = useCallback(() => {
+ setVisible(true);
+ }, []);
- return remover as () => void;
- }, [results]);
+ const onHide = useCallback(() => {
+ setVisible(false);
+ }, []);
+
+ useEffect(() => {
+ const handler = (e: KeyboardEvent) => {
+ if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
+ e.preventDefault();
+ if (visible) {
+ onHide();
+ } else {
+ openSearch();
+ }
+ }
+ };
+ document.addEventListener('keydown', handler);
+ return () => {
+ document.removeEventListener('keydown', handler);
+ };
+ }, [openSearch, onHide, visible]);
+
+ const currentQuery = query.trim();
+ const isFileMode = searchMode === 'files';
return (
-
-
{
- if (searchInputRef.current && searchInputRef.current.value.trim() === '') {
- setResults(search(''));
- }
- setResultShow(true);
- }}
- onBlur={() => {
- setResultShow(false);
- }}
- />
-
+
+
+ Search
+
+ ⌘ K
+
+
+ 🔍 Search
}
+ visible={visible}
+ onHide={onHide}
+ className="doc-search-dialog"
>
-
{`found ${results.length} related article`}
-
{
- e.preventDefault();
- }}
- ref={resultBoxRef}
- >
- {results.length !== 0 &&
- results.slice(0, showNum).map((result) => {
- const { path, keywords, headings } = result;
- const showPath = path.replace(/-/g, '->');
-
- return (
-
-
+
{
+ if (e.value != null) handleModeChange(e.value as SearchMode);
+ }}
+ options={MODE_OPTIONS}
+ className="search-mode-toggle"
+ />
+
+
+
+
+ {
+ handleQueryChange(e.target.value);
+ }}
+ placeholder={isFileMode ? 'Search file name...' : 'Search in files...'}
+ className="search-input"
+ autoFocus
+ />
+
+ {!isFileMode && (
+
+
+ Aa
+
+ {!isFileMode && (
+ {
- toResult(path, '');
+ setShowFilters((v) => !v);
}}
- >
- {searchInputRef.current?.value.trim() !== '' && keywords.length !== 0 && (
-
- {keywords.map((keyword) => (
-
{
- toResult(path, keyword);
- }}
- >
- ))}
-
- )}
- {searchInputRef.current?.value.trim() !== '' && headings.length !== 0 && (
-
- {headings.map((heading) => (
-
{
- toResult(path, heading.replace(/#+\s/g, ''));
- }}
- >
- ))}
-
- )}
-
- );
- })}
+ >
+
+ Filters
+
+ )}
+
+ )}
+
+
+ {!isFileMode && showFilters && (
+
+
+ {
+ handleIncludeChange(e.target.value);
+ }}
+ placeholder="Files to include (e.g. docs, notes)"
+ className="filter-input"
+ />
+ {
+ handleExcludeChange(e.target.value);
+ }}
+ placeholder="Files to exclude (e.g. drafts, archive)"
+ className="filter-input"
+ />
+
+
+ )}
+
+
-
+
);
-}
+};
diff --git a/client/src/components/DocSearch/SearchResults.tsx b/client/src/components/DocSearch/SearchResults.tsx
new file mode 100644
index 0000000..603aa21
--- /dev/null
+++ b/client/src/components/DocSearch/SearchResults.tsx
@@ -0,0 +1,190 @@
+import { FC, ReactNode, useMemo } from 'react';
+
+import { FileContentMatches, FileNameMatch } from '@/redux-api/searchApi';
+import { normalizePath } from '@/utils/utils';
+
+const CONTEXT_CHARS = 30;
+
+function renderHighlightedLine(lineContent: string, query: string, matchCase: boolean): ReactNode {
+ const trimmedQuery = query.trim();
+ if (!trimmedQuery) return lineContent;
+
+ const escaped = trimmedQuery.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ const flags = matchCase ? 'g' : 'gi';
+
+ let regex: RegExp | null = null;
+ try {
+ regex = new RegExp(escaped, flags);
+ } catch {
+ return lineContent;
+ }
+
+ if (!regex) return lineContent;
+
+ const matches: { start: number; end: number }[] = [];
+ let m: RegExpExecArray | null = null;
+ while ((m = regex.exec(lineContent)) !== null) {
+ matches.push({ start: m.index, end: m.index + m[0].length });
+ if (m[0].length === 0) break;
+ }
+
+ if (matches.length === 0) return lineContent;
+
+ const windowStart = Math.max(0, matches[0].start - CONTEXT_CHARS);
+ const windowEnd = Math.min(lineContent.length, matches[matches.length - 1].end + CONTEXT_CHARS);
+ const prefix = windowStart > 0;
+ const suffix = windowEnd < lineContent.length;
+
+ const parts: ReactNode[] = [];
+ let cursor = windowStart;
+ let key = 0;
+
+ for (const match of matches) {
+ if (match.end <= windowStart || match.start >= windowEnd) continue;
+ const mStart = Math.max(match.start, windowStart);
+ const mEnd = Math.min(match.end, windowEnd);
+ if (mStart > cursor) {
+ parts.push(lineContent.substring(cursor, mStart));
+ }
+ parts.push(
+
+ {lineContent.substring(mStart, mEnd)}
+ ,
+ );
+ cursor = mEnd;
+ }
+
+ if (cursor < windowEnd) {
+ parts.push(lineContent.substring(cursor, windowEnd));
+ }
+
+ return (
+ <>
+ {prefix && … }
+ {parts}
+ {suffix && … }
+ >
+ );
+}
+
+interface SearchResultsProps {
+ isFileMode: boolean;
+ currentQuery: string;
+ query: string;
+ matchCase: boolean;
+ fileResults: FileNameMatch[];
+ contentResults: FileContentMatches[];
+ collapsedFiles: Set;
+ onNavigate: (path: string[], searchContext?: { query: string; lineContent: string }) => void;
+ onToggleCollapse: (filePath: string) => void;
+ onCollapseAll: () => void;
+ onExpandAll: () => void;
+}
+
+const renderFilePath = (path: string[]) => path.join(' / ');
+
+export const SearchResults: FC = ({
+ isFileMode,
+ currentQuery,
+ query,
+ matchCase,
+ fileResults,
+ contentResults,
+ collapsedFiles,
+ onNavigate,
+ onToggleCollapse,
+ onCollapseAll,
+ onExpandAll,
+}) => {
+ const allCollapsed = useMemo(
+ () => contentResults.length > 0 && contentResults.every((f) => collapsedFiles.has(normalizePath(f.path))),
+ [contentResults, collapsedFiles],
+ );
+
+ return (
+
+ {isFileMode ? (
+ <>
+ {currentQuery !== '' && fileResults.length === 0 &&
No files found
}
+ {fileResults.map((item) => (
+
{
+ onNavigate(item.path);
+ }}
+ >
+
+
+ {item.name}
+ {renderFilePath(item.path)}
+
+
+ ))}
+ >
+ ) : (
+ <>
+ {contentResults.length > 0 && (
+
+
+
+ {allCollapsed ? 'Expand All' : 'Collapse All'}
+
+
+ )}
+ {currentQuery !== '' && contentResults.length === 0 &&
No matches found
}
+ {contentResults.map((file) => {
+ const fileKey = normalizePath(file.path);
+ const isCollapsed = collapsedFiles.has(fileKey);
+ return (
+
+
{
+ onToggleCollapse(fileKey);
+ }}
+ >
+
+
+ {
+ e.stopPropagation();
+ onNavigate(file.path);
+ }}
+ >
+ {file.name}
+
+ {renderFilePath(file.path)}
+ {file.matches.length}
+
+ {!isCollapsed && (
+
+ {file.matches.map((match) => (
+
{
+ onNavigate(file.path, { query: query.trim(), lineContent: match.lineContent });
+ }}
+ >
+ {match.lineNumber}
+
+ {renderHighlightedLine(match.lineContent, query, matchCase)}
+
+
+ ))}
+
+ )}
+
+ );
+ })}
+ >
+ )}
+
+ );
+};
diff --git a/client/src/components/Editor/DraftEditor.tsx b/client/src/components/Editor/DraftEditor.tsx
new file mode 100644
index 0000000..2f1e3bf
--- /dev/null
+++ b/client/src/components/Editor/DraftEditor.tsx
@@ -0,0 +1,157 @@
+import { Ctx } from '@milkdown/kit/ctx';
+import { outline } from '@milkdown/utils';
+import { ScrollPanel } from 'primereact/scrollpanel';
+import React, { useEffect, useId, useRef } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useParams } from 'react-router-dom';
+
+import { getGuideDoc } from './internalDocs/guide';
+import { getVersionMismatchDoc } from './internalDocs/versionMismatch';
+import { CrepeEditor, CrepeEditorRef } from './MilkdownEditor';
+import { EditorRef } from './type';
+
+import { updateCurDoc, selectCurTabs, DocType, selectCurDoc, clearCurDoc } from '@/redux-feature/curDocSlice';
+import { selectNarrowMode, selectReadonly, selectTheme, updateGlobalOpts } from '@/redux-feature/globalOptsSlice';
+
+import '@milkdown/crepe/theme/common/style.css';
+import '@milkdown/crepe/theme/frame.css';
+import './Editor.scss';
+
+const getDoc = (docId: string, type: DocType) => {
+ if (type === 'internal') {
+ if (docId === 'guide') {
+ return {
+ id: docId,
+ title: `Guide`,
+ content: getGuideDoc(),
+ };
+ }
+ if (docId === 'version-mismatch') {
+ return {
+ id: docId,
+ title: `Version Mismatch`,
+ content: getVersionMismatchDoc(),
+ };
+ }
+ }
+
+ return {
+ id: docId,
+ title: `Draft ${docId}`,
+ content: `draft-${docId}`,
+ };
+};
+
+export interface DraftEditorProps {
+ type: DocType;
+ ref: React.RefObject;
+}
+
+export const DraftEditor: React.FC = ({ ref: editorWrappedRef, type }) => {
+ const uniqueId = useId();
+
+ const { docId = uniqueId } = useParams<{
+ docId: string;
+ }>();
+
+ const doc = getDoc(docId, type);
+
+ const { content: storedContent, contentIdent } = useSelector(selectCurDoc);
+
+ const theme = useSelector(selectTheme);
+ const readonly = useSelector(selectReadonly);
+ const narrowMode = useSelector(selectNarrowMode);
+
+ const dispatch = useDispatch();
+
+ const crepeEditorRef = useRef(null);
+
+ // for update the editor using a wrapped ref
+ React.useImperativeHandle(editorWrappedRef, () => ({
+ update: (markdown: string) => {
+ crepeEditorRef.current?.update(markdown);
+ },
+ }));
+
+ const curTabs = useSelector(selectCurTabs);
+
+ useEffect(() => {
+ dispatch(
+ updateGlobalOpts({
+ keys: ['readonly'],
+ values: [true],
+ }),
+ );
+
+ return () => {
+ dispatch(clearCurDoc());
+ // void crepeEditorRef.current?.get()?.destroy();
+ };
+ }, []);
+
+ // when switching the doc (or same doc refetched)
+ useEffect(() => {
+ if (doc?.id === contentIdent) return;
+
+ const tab = curTabs.find(({ ident }) => ident === contentIdent);
+ const ctx = crepeEditorRef.current?.get()?.ctx;
+ // const contentToUse = draft?.content ?? fetchedDoc?.content;
+ // const headingsToUse = draft?.headings ?? (ctx ? outline()(ctx) : []);
+
+ const contentToUse = doc?.content;
+ const headingsToUse = ctx ? outline()(ctx) : [];
+
+ dispatch(
+ updateCurDoc({
+ content: contentToUse,
+ // isDirty: Boolean(draft),
+ isDirty: false,
+ contentIdent: doc?.id,
+ headings: headingsToUse,
+ scrollTop: tab ? tab.scroll : 0,
+ title: doc?.title,
+ type,
+ }),
+ );
+
+ crepeEditorRef.current?.reRender();
+ }, [doc?.content, type]);
+
+ // only for draft doc, since internal doc should not be edited and saved
+ const onUpdated = (ctx: Ctx, markdown: string) => {
+ const isDirty = markdown !== doc?.content;
+
+ const headings = outline()(ctx);
+
+ dispatch(
+ updateCurDoc({
+ content: markdown,
+ isDirty,
+ contentIdent: docId,
+ title: doc?.title,
+ headings,
+ type,
+ }),
+ );
+
+ // if (isDirty) {
+ // dispatch(setDraft({ path: draftKey, content: markdown, headings }));
+ // } else {
+ // dispatch(clearDraft(draftKey));
+ // }
+ };
+
+ return (
+
+
+
+
+
+ );
+};
diff --git a/client/src/components/Editor/Editor.less b/client/src/components/Editor/Editor.less
deleted file mode 100644
index 70213ca..0000000
--- a/client/src/components/Editor/Editor.less
+++ /dev/null
@@ -1,131 +0,0 @@
-@import url('../../utils/utils.less');
-
-.editor-box {
- min-width: 0%;
- height: 100%;
- display: flex;
- flex-direction: column;
- align-items: stretch;
- justify-content: flex-start;
- .shadow-box();
- .milkdown-menu-wrapper {
- height: 90vh;
- display: flex;
- flex-direction: column;
- align-items: stretch;
- justify-content: flex-start;
- }
- .milkdown-menu {
- border: none;
- }
- .milkdown {
- width: 100%;
- height: 90vh;
- overflow: auto;
- transition: @transition;
- font-family: 'LXGW WenKai Screen R', Calibri, Arial, sans-serif;
- .editor {
- max-width: 100%;
- padding: 1rem 3rem;
- .blockquote {
- background-color: @blockquoteColor;
- border-top-right-radius: 15px;
- border-bottom-right-radius: 15px;
- }
- .paragraph {
- font-size: 24px;
- }
- .heading {
- display: flex;
- justify-content: flex-start;
- align-items: center;
- font-weight: bold;
- // position: relative;
- .heading-outline {
- margin-left: 1rem;
- display: flex;
- align-items: center;
- .material-icons-outlined {
- font-size: 2.5rem;
- }
- // position: relative;
- .show-outline-icon {
- // z-index: 1000;
- opacity: 0.5;
- cursor: pointer;
- &:hover {
- opacity: 1;
- }
- }
- }
- }
- .bullet-list {
- padding: 0 2rem;
- }
- .ordered-list {
- padding: 0 2rem;
- }
- .list-item {
- &::marker {
- font-size: 24px;
- }
- }
- .task-list-item {
- display: flex;
- transform: translateX(-1rem);
- label {
- position: static;
- margin-right: 0.5rem;
- }
- }
- .image-container {
- img {
- width: auto;
- max-height: 35rem;
- object-fit: cover;
- }
- }
- .code-fence {
- background-color: rgb(46, 52, 64);
- border-radius: 15px;
- position: relative;
- .code-fence_selector {
- border: 1px solid rgb(37, 41, 50);
- background-color: rgb(37, 41, 50);
- span {
- background-color: rgb(37, 41, 50);
- color: rgba(236, 239, 244, 0.87);
- border: 1px solid rgb(37, 41, 50);
- font-size: 16px;
- }
- }
- .code-fence_selector-list {
- background-color: rgb(37, 41, 50);
- color: rgba(236, 239, 244, 0.87);
- border: 1px solid rgb(37, 41, 50);
- }
- pre {
- background-color: rgb(46, 52, 64);
- color: rgb(236, 239, 244);
- code {
- font-size: 24px;
- }
- }
- .code-fence-copy-btn {
- position: absolute;
- top: 1rem;
- right: 1rem;
- .btn(fit-content, fit-content, #24283b, rgb(68, 70, 72));
- color: #e6e6e6;
- font-weight: bold;
- }
- }
- .tooltip {
- border: none;
- .icon {
- font-size: 16px;
- }
- }
- }
- }
-}
diff --git a/client/src/components/Editor/Editor.scss b/client/src/components/Editor/Editor.scss
new file mode 100644
index 0000000..49f535d
--- /dev/null
+++ b/client/src/components/Editor/Editor.scss
@@ -0,0 +1,139 @@
+@use '../../utils/utils.scss' as *;
+@use '../DocMirror/DocMirror.scss' as DocMirror;
+@use './plugins/plugin-iframe/index.scss';
+@use './plugins/plugin-heading/heading.scss';
+@use './plugins/plugin-container';
+
+.editor-box {
+ overflow: hidden;
+ min-width: 0%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: stretch;
+ justify-content: flex-start;
+ border-right: 1px solid $shadowColor;
+ &.narrow {
+ .milkdown {
+ .editor {
+ max-width: 56rem;
+ margin: 0 auto;
+ }
+ }
+ }
+
+ .p-scrollpanel-content {
+ height: $editorHeight;
+ }
+
+ .milkdown {
+ height: $editorHeight;
+ transition: $transition;
+ .ProseMirror {
+ code {
+ padding: .1em .4em;
+ border-radius: 6px;
+ font-size: 80%;
+ line-height: 1.5;
+ font-family: var(--code-font-family);
+ }
+ }
+ .editor {
+ max-width: 95%;
+ margin: 0 auto;
+ padding: 1rem 3rem;
+ blockquote {
+ border-top-right-radius: 15px;
+ border-bottom-right-radius: 15px;
+ }
+ p {
+ font-size: 16px;
+ margin: 1rem 0;
+ padding: 0;
+ strong {
+ font-weight: 500;
+ }
+ }
+ .image-container {
+ img {
+ width: auto;
+ max-height: 35rem;
+ object-fit: cover;
+ }
+ }
+ .milkdown-code-block {
+ position: relative;
+ @include DocMirror.code-mirror-style;
+ border-radius: $borderRadius;
+ margin-top: 0;
+ margin-bottom: 1rem;
+ }
+ .milkdown-image-block {
+ border-radius: $borderRadius;
+ img {
+ border-radius: $borderRadius;
+ }
+ }
+ blockquote {
+ padding: 0 1em;
+ margin-top: 0;
+ margin-bottom: 1rem;
+ &::before {
+ height: 100%;
+ top: 50%;
+ transform: translateY(-50%);
+ }
+ p {
+ margin: 0;
+ }
+ }
+ ul, ol {
+ margin-bottom: 16px;
+ }
+ .milkdown-list-item-block {
+ & + .milkdown-list-item-block {
+ margin-top: .25em;
+ }
+ .list-item {
+ .label-wrapper {
+ height: 24px;
+ .milkdown-icon.label {
+ height: 24px;
+ padding: 0;
+ }
+ // avoid checked type list style overwrite
+ .milkdown-icon.label.readonly {
+ &.bullet, &.ordered {
+ cursor: unset;
+ }
+ }
+ }
+ .children p {
+ margin: 0;
+ }
+ }
+ }
+ .milkdown-code-block {
+ p {
+ font-size: unset;
+ margin-bottom: unset;
+ line-height: unset;
+ }
+ }
+ }
+ }
+}
+
+@keyframes search-highlight-flash {
+ 0% { background-color: rgba(255, 235, 59, 0.6); }
+ 25% { background-color: transparent; }
+ 50% { background-color: rgba(255, 235, 59, 0.6); }
+ 75% { background-color: transparent; }
+ 100% { background-color: rgba(255, 235, 59, 0.4); }
+}
+
+.search-match-highlight {
+ border-radius: 2px;
+ animation: search-highlight-flash 1.2s ease-in-out;
+ background-color: rgba(255, 235, 59, 0.4);
+}
diff --git a/client/src/components/Editor/Editor.tsx b/client/src/components/Editor/Editor.tsx
index 14bba7c..7f40d4b 100644
--- a/client/src/components/Editor/Editor.tsx
+++ b/client/src/components/Editor/Editor.tsx
@@ -1,201 +1,194 @@
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-import { Editor, rootCtx, editorViewOptionsCtx, defaultValueCtx, editorViewCtx, parserCtx } from '@milkdown/core';
-// import { getNord } from "@milkdown/theme-nord";
-import { diagram } from '@milkdown/plugin-diagram';
-import { emoji } from '@milkdown/plugin-emoji';
-import { history } from '@milkdown/plugin-history';
-import { indent } from '@milkdown/plugin-indent';
-import { listener, listenerCtx } from '@milkdown/plugin-listener';
-import { Slice } from '@milkdown/prose/model';
-import { ReactEditor, useEditor, EditorRef } from '@milkdown/react';
-import { getTokyo } from '@milkdown/theme-tokyo';
-import React, { useEffect, useRef } from 'react';
+import { editorViewCtx } from '@milkdown/kit/core';
+import { Ctx } from '@milkdown/kit/ctx';
+import { outline } from '@milkdown/utils';
+import { ScrollPanel } from 'primereact/scrollpanel';
+import React, { useCallback, useEffect, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
-import { useParams } from 'react-router-dom';
-
-import gfm from './configs/gfmConfig';
-import menu from './configs/menuConfig';
-import prism from './configs/prismConfig';
-import slash from './configs/slashConfig';
-import tooltip from './configs/tooltipConfig';
-import upload from './configs/uploadConfig';
-import { removeEvents, scrollHandler, blurHandler, addClipboard, anchorHandler, syncMirror } from './mountedAddons';
-import iframe from './plugins/iframe-plugin/iframe';
-import { EditorWrappedRef } from '../EditorContainer/EditorContainer';
-
-import { useGetDocQuery } from '@/redux-api/docsApi';
-import { useUploadImgMutation } from '@/redux-api/imgStoreApi';
-import { updateCurDoc, selectCurDoc, selectCurTabs } from '@/redux-feature/curDocSlice';
-import { selectDocGlobalOpts } from '@/redux-feature/globalOptsSlice';
-import { useEditorScrollToAnchor } from '@/utils/hooks/docHooks';
-
-import './Editor.less';
-
-export default React.forwardRef((_, editorWrappedRef) => {
- const { contentPath: curPath } = useParams<{
- contentPath: string;
- }>();
+import { useNavigate, useParams, useLocation } from 'react-router-dom';
+
+import { CrepeEditor, CrepeEditorRef } from './MilkdownEditor';
+import { searchAndHighlight } from './mountedAddons';
+import { EditorRef } from './type';
+
+import { useGetDocQuery } from '@/redux-api/docs';
+import { useGetSettingsQuery } from '@/redux-api/settings';
+import { updateCurDoc, selectCurDoc, selectCurTabs, clearCurDoc } from '@/redux-feature/curDocSlice';
+import { clearDraft, selectDraft, setDraft } from '@/redux-feature/draftsSlice';
+import { selectNarrowMode, selectReadonly, selectTheme } from '@/redux-feature/globalOptsSlice';
+import Toast from '@/utils/Toast';
+import { getDraftKey, normalizePath } from '@/utils/utils';
+
+import '@milkdown/crepe/theme/common/style.css';
+import '@milkdown/crepe/theme/frame.css';
+import './Editor.scss';
+
+const SEARCH_HIGHLIGHT_DELAY_SAME_DOC = 50;
+const SEARCH_HIGHLIGHT_DELAY_NEW_DOC = 200;
+
+const getDefaultDoc = () => ({
+ content: 'Loading...',
+ filePath: '',
+ headings: [],
+ keywords: [],
+});
- const { content: globalContent, contentPath: globalPath, scrollTop } = useSelector(selectCurDoc);
- const { isDarkMode, readonly, anchor } = useSelector(selectDocGlobalOpts);
+export const MarkdownEditor: React.FC<{ ref: React.RefObject }> = ({ ref: editorWrappedRef }) => {
+ const { docPath = '' } = useParams<{
+ docPath: string;
+ }>();
+ const curDocPath = normalizePath([docPath]);
- const dispatch = useDispatch();
+ const navigate = useNavigate();
+ const location = useLocation();
- const scrollToAnchor = useEditorScrollToAnchor();
+ // useGetDocQuery will be cached (within a limited time) according to different contentPath
+ // with auto refetch when the doc is updated
+ const { data: fetchedDoc = getDefaultDoc(), isSuccess, error } = useGetDocQuery(curDocPath);
+ const { data: settings } = useGetSettingsQuery();
- const uploadImgMutation = useUploadImgMutation();
+ const { content: storedContent, contentIdent: storedContentPath } = useSelector(selectCurDoc);
+ const draftKey = getDraftKey(settings?.docRootPath, curDocPath);
+ const draft = useSelector(selectDraft(draftKey));
+ const theme = useSelector(selectTheme);
+ const readonly = useSelector(selectReadonly);
+ const narrowMode = useSelector(selectNarrowMode);
- // useGetDocQuery will be cached (within a limited time) according to different contentPath
- const {
- data = {
- content: 'Loading...',
- filePath: '',
- headings: [],
- keywords: [],
- },
- isSuccess,
- } = useGetDocQuery(curPath);
-
- /**
- * below is to avoid remount when saving a edited article (avoid losing focus)
- */
- const dataContentRef = useRef(data.content); // avoid closure issue when markdownUpdated
- const pathEqualRef = useRef(false);
- const pathChangeRef = useRef(false); // used to trigger the editor to remount
- // remount editor when from in-equal to equal
- // it means the global doc has been sync after switching article
- // and we can get the actual content
- if (!pathEqualRef.current && curPath === globalPath) {
- pathChangeRef.current = !pathChangeRef.current;
- pathEqualRef.current = true;
- }
- // when switching articles, reset the pathEqualRef to be false
- useEffect(() => {
- pathEqualRef.current = false;
- }, [curPath]);
-
- const editor = useEditor(
- (root) =>
- Editor.make()
- .config((ctx) => {
- ctx.set(rootCtx, root);
- // when updated, get the string value of the markdown
- ctx
- .get(listenerCtx)
- .mounted(() => {
- removeEvents();
-
- scrollHandler(scrollTop, dispatch);
-
- blurHandler(dispatch);
-
- addClipboard(readonly);
-
- anchorHandler(anchor, dispatch, scrollToAnchor);
-
- syncMirror(readonly);
- })
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
- .markdownUpdated((updateCtx, markdown, prevMarkdown) => {
- // data.content is the original cached content
- // markdown is the updated content
- let isDirty = false;
-
- // being edited
- if (markdown !== dataContentRef.current) {
- isDirty = true;
- }
-
- // update the global current doc
- dispatch(
- updateCurDoc({
- content: markdown,
- isDirty,
- contentPath: curPath,
- }),
- );
- });
-
- // edit mode
- ctx.set(editorViewOptionsCtx, {
- editable: () => !readonly,
- });
+ const dispatch = useDispatch();
- // global content and global path have been sync
- ctx.set(defaultValueCtx, globalContent);
- })
- // .use(getNord(isDarkMode))
- .use(getTokyo(isDarkMode))
- .use(gfm)
- .use(listener)
- .use(tooltip)
- .use(slash)
- .use(menu)
- .use(history)
- .use(emoji)
- .use(indent)
- .use(upload(uploadImgMutation, curPath))
- .use(iframe)
- .use(prism)
- .use(diagram),
- [isDarkMode, readonly, pathChangeRef.current],
- );
+ const crepeEditorRef = useRef(null);
// for update the editor using a wrapped ref
- const editorRef = useRef(null);
React.useImperativeHandle(editorWrappedRef, () => ({
update: (markdown: string) => {
- if (!editorRef.current) return;
- // eslint-disable-next-line @typescript-eslint/no-shadow
- const editor = editorRef.current.get();
+ crepeEditorRef.current?.update(markdown);
+ },
+ }));
+
+ const curTabs = useSelector(selectCurTabs);
+
+ const pendingSearchRef = useRef<{ query: string; lineContent: string } | null>(null);
+
+ // Capture search highlight state from navigation and clear it from the URL
+ useEffect(() => {
+ const state = location.state as { searchQuery?: string; lineContent?: string } | null;
+ if (!state?.searchQuery) return;
+
+ pendingSearchRef.current = { query: state.searchQuery, lineContent: state.lineContent ?? '' };
+ void navigate(location.pathname, { replace: true, state: null });
+
+ // If the editor already has the correct doc loaded (same-doc navigation), apply immediately
+ if (storedContentPath === curDocPath) {
+ const editor = crepeEditorRef.current?.get();
if (!editor) return;
- editor.action((ctx) => {
+ const pending = pendingSearchRef.current;
+ setTimeout(() => {
+ try {
+ editor.action((ctx) => {
+ const view = ctx.get(editorViewCtx);
+ const found = searchAndHighlight(view, pending.query, pending.lineContent);
+ if (found) {
+ pendingSearchRef.current = null;
+ }
+ });
+ } catch {
+ /* editor might have been destroyed */
+ }
+ }, SEARCH_HIGHLIGHT_DELAY_SAME_DOC);
+ }
+ }, [location.key]); // only trigger on navigation events
+
+ // When the editor mounts (new doc), apply any pending search highlight
+ const onMounted = useCallback((ctx: Ctx) => {
+ const pending = pendingSearchRef.current;
+ if (!pending) return;
+
+ setTimeout(() => {
+ try {
const view = ctx.get(editorViewCtx);
- const parser = ctx.get(parserCtx);
- const doc = parser(markdown);
- if (!doc) return;
+ const found = searchAndHighlight(view, pending.query, pending.lineContent);
+ if (found) {
+ pendingSearchRef.current = null;
+ }
+ } catch {
+ /* editor might have been destroyed during re-render */
+ }
+ }, SEARCH_HIGHLIGHT_DELAY_NEW_DOC);
+ }, []);
- const state = view.state;
- view.dispatch(state.tr.replace(0, state.doc.content.size, new Slice(doc.content, 0, 0)));
- });
- },
- }));
+ useEffect(() => {
+ return () => {
+ dispatch(clearCurDoc());
+ };
+ }, []);
- const curTabs = useSelector(selectCurTabs);
+ useEffect(() => {
+ if (error) {
+ void navigate('/');
+ return Toast.error((error as unknown as Error).message ?? 'Failed to fetch doc');
+ }
+ }, [error]);
- /**
- * only run when the fetch data changed
- * 1. switch to another article
- * 2. loading to success
- */
+ // when switching the doc (or same doc refetched)
useEffect(() => {
- if (isSuccess) {
- dataContentRef.current = data.content;
-
- const tab = curTabs.find(({ path }) => path === curPath);
-
- // update the global current doc
- dispatch(
- updateCurDoc({
- content: data.content,
- // if switch, then false
- // if same path, then compare data.content === globalContent
- isDirty: pathEqualRef.current ? data.content !== globalContent : false,
- contentPath: curPath,
- scrollTop: pathEqualRef.current ? scrollTop : tab ? tab.scroll : 0,
- // the scroll top is initially set as 0 when switching (path is inequal)
- // unless it is been visited and has scroll record at the tabs
- }),
- );
+ if (!isSuccess) {
+ return;
}
- // eslint-disable-next-line
- }, [data.content]);
+ if (fetchedDoc?.filePath === storedContentPath) return;
+
+ const tab = curTabs.find(({ ident }) => ident === curDocPath);
+ const ctx = crepeEditorRef.current?.get()?.ctx;
+ const contentToUse = draft?.content ?? fetchedDoc?.content;
+ const headingsToUse = draft?.headings ?? (ctx ? outline()(ctx) : []);
+
+ dispatch(
+ updateCurDoc({
+ content: contentToUse,
+ isDirty: Boolean(draft),
+ contentIdent: fetchedDoc?.filePath,
+ headings: headingsToUse,
+ scrollTop: tab ? tab.scroll : 0,
+ type: 'workspace',
+ }),
+ );
+
+ crepeEditorRef.current?.reRender();
+ }, [fetchedDoc]);
+
+ const onUpdated = (ctx: Ctx, markdown: string) => {
+ const isDirty = markdown !== fetchedDoc?.content;
+
+ const headings = outline()(ctx);
+
+ dispatch(
+ updateCurDoc({
+ content: markdown,
+ isDirty,
+ contentIdent: curDocPath,
+ headings,
+ type: 'workspace',
+ }),
+ );
+
+ if (isDirty) {
+ dispatch(setDraft({ path: draftKey, content: markdown, headings }));
+ } else {
+ dispatch(clearDraft(draftKey));
+ }
+ };
return (
-
-
+
+
+
+
);
-});
+};
diff --git a/client/src/components/Editor/MilkdownEditor.tsx b/client/src/components/Editor/MilkdownEditor.tsx
new file mode 100644
index 0000000..b9ccc95
--- /dev/null
+++ b/client/src/components/Editor/MilkdownEditor.tsx
@@ -0,0 +1,158 @@
+import { Editor, editorViewCtx, editorViewOptionsCtx, parserCtx } from '@milkdown/kit/core';
+import { Ctx } from '@milkdown/kit/ctx';
+import { listenerCtx } from '@milkdown/kit/plugin/listener';
+import { Slice } from '@milkdown/kit/prose/model';
+import { Milkdown, useEditor } from '@milkdown/react';
+import { outline } from '@milkdown/utils';
+import { FC, useEffect, useImperativeHandle, useReducer, useRef } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+
+import { getCrepe } from './crepe';
+import { removeEvents, scrollHandler, blurHandler, anchorHandler, syncMirror } from './mountedAddons';
+import { headingConfig } from './plugins/plugin-heading';
+
+import { selectCurDoc, updateHeadings } from '@/redux-feature/curDocSlice';
+import { updateGlobalOpts } from '@/redux-feature/globalOptsSlice';
+import { scrollToEditorAnchor } from '@/utils/hooks/docHooks';
+import { updateLocationHash } from '@/utils/utils';
+
+export interface CrepeEditorRef {
+ update: (newContent: string) => void;
+ get: () => Editor | undefined;
+ /** used to trigger reser the editor */
+ reRender: () => void;
+}
+
+export interface CrepeEditorProps {
+ /** default as "" */
+ defaultValue?: string;
+ /** default as false */
+ isDarkMode?: boolean;
+ /** default as false */
+ readonly?: boolean;
+ onMounted?: (ctx: Ctx) => void;
+ onUpdated?: (ctx: Ctx, markdown: string) => void;
+ onToAnchor?: (id: string) => void;
+
+ ref: React.RefObject
;
+}
+
+export const CrepeEditor: FC = ({
+ defaultValue = '',
+ isDarkMode = false,
+ readonly = false,
+ onMounted,
+ onUpdated,
+ onToAnchor,
+ ref: editorWrappedRef,
+}) => {
+ const [reRenderMarker, reRender] = useReducer((x) => x + 1, 0);
+
+ const dispatch = useDispatch();
+ const { scrollTop } = useSelector(selectCurDoc);
+
+ // This is used to avoid the closure issue
+ // when hook functions from props are updated but editor is not reset
+ const hookRefs = useRef<{
+ onUpdated: CrepeEditorProps['onUpdated'];
+ onMounted: CrepeEditorProps['onMounted'];
+ onToAnchor: CrepeEditorProps['onToAnchor'];
+ }>({
+ onUpdated: (...args) => {
+ onUpdated?.(...args);
+ },
+ onMounted: (...args) => {
+ onMounted?.(...args);
+ },
+ onToAnchor: (...args) => {
+ onToAnchor?.(...args);
+ },
+ });
+
+ useEffect(() => {
+ hookRefs.current.onUpdated = onUpdated;
+ hookRefs.current.onMounted = onMounted;
+ hookRefs.current.onToAnchor = onToAnchor;
+ }, [onUpdated, onMounted, onToAnchor]);
+
+ const { get } = useEditor(
+ (root) => {
+ const crepe = getCrepe({
+ root,
+ defaultValue,
+ isDarkMode,
+ });
+
+ crepe.editor.config((ctx) => {
+ ctx
+ .get(listenerCtx)
+ .mounted(() => {
+ dispatch(updateHeadings(outline()(ctx)));
+
+ removeEvents();
+
+ scrollHandler(scrollTop, dispatch);
+
+ blurHandler(dispatch);
+
+ // has higher priority than the scrollHandler
+ anchorHandler(dispatch);
+
+ syncMirror(readonly);
+
+ hookRefs.current.onMounted?.(ctx);
+ })
+ .markdownUpdated((_, markdown) => {
+ const headings = outline()(ctx);
+ dispatch(updateHeadings(headings));
+
+ hookRefs.current.onUpdated?.(ctx, markdown);
+ });
+
+ // edit mode
+ ctx.set(editorViewOptionsCtx, {
+ editable: () => !readonly,
+ });
+
+ ctx.set(headingConfig.key, {
+ toAnchor: (id: string) => {
+ updateLocationHash(id);
+ scrollToEditorAnchor(id);
+ dispatch(updateGlobalOpts({ keys: ['anchor'], values: [id] }));
+
+ hookRefs.current.onToAnchor?.(id);
+ },
+ });
+ });
+
+ return crepe;
+ },
+ [isDarkMode, readonly, reRenderMarker],
+ );
+
+ // for update the editor using a wrapped ref
+ useImperativeHandle(
+ editorWrappedRef,
+ () => ({
+ get,
+ reRender,
+ update: (markdown: string) => {
+ const editor = get();
+ if (!editor) return;
+
+ editor.action((ctx) => {
+ const view = ctx.get(editorViewCtx);
+ const parser = ctx.get(parserCtx);
+ const doc = parser(markdown);
+ if (!doc) return;
+
+ const state = view.state;
+ view.dispatch(state.tr.replace(0, state.doc.content.size, new Slice(doc.content, 0, 0)));
+ });
+ },
+ }),
+ [get, reRender],
+ );
+
+ return ;
+};
diff --git a/client/src/components/Editor/components/TooltipInput/index.scss b/client/src/components/Editor/components/TooltipInput/index.scss
new file mode 100644
index 0000000..8f98355
--- /dev/null
+++ b/client/src/components/Editor/components/TooltipInput/index.scss
@@ -0,0 +1,18 @@
+@use '@/utils/utils.scss' as *;
+
+.tooltip-input {
+ .p-tooltip-arrow {
+ display: none;
+ }
+ .p-tooltip-text {
+ background-color: $backgroundColor;
+ padding: 10px;
+ @include flex-center();
+ input {
+ height: 24px;
+ margin-right: 1rem;
+ background-color: $backgroundColor;
+ color: $contentTextColor;
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/components/Editor/components/TooltipInput/index.tsx b/client/src/components/Editor/components/TooltipInput/index.tsx
new file mode 100644
index 0000000..0afa8e4
--- /dev/null
+++ b/client/src/components/Editor/components/TooltipInput/index.tsx
@@ -0,0 +1,47 @@
+import { InputText } from 'primereact/inputtext';
+import { Tooltip, TooltipProps } from 'primereact/tooltip';
+import { FC } from 'react';
+
+import { Icon, IconProps } from '@/components/Icon/Icon';
+
+import './index.scss';
+
+export interface TooltipInputProps {
+ tooltipOptions: TooltipProps;
+ iconOptions: IconProps;
+ value: string;
+ placeholder?: string;
+ onChange: (value: string) => void;
+ onConfirm: () => void;
+}
+
+export const TooltipInput: FC = ({
+ tooltipOptions,
+ iconOptions,
+ value,
+ placeholder,
+ onChange,
+ onConfirm,
+}) => {
+ return (
+
+ {
+ onChange(e.target.value);
+ }}
+ />
+ {
+ onConfirm();
+ }}
+ showToolTip={false}
+ {...iconOptions}
+ />
+
+ );
+};
diff --git a/client/src/components/Editor/configs/crepeFeatures.ts b/client/src/components/Editor/configs/crepeFeatures.ts
new file mode 100644
index 0000000..9e7899b
--- /dev/null
+++ b/client/src/components/Editor/configs/crepeFeatures.ts
@@ -0,0 +1,141 @@
+import { BlockEditFeatureConfig } from '@milkdown/crepe/lib/types/feature/block-edit';
+import { CodeMirrorFeatureConfig } from '@milkdown/crepe/lib/types/feature/code-mirror';
+import { ToolbarFeatureConfig } from '@milkdown/crepe/lib/types/feature/toolbar';
+import { commandsCtx } from '@milkdown/kit/core';
+import {
+ clearTextInCurrentBlockCommand,
+ addBlockTypeCommand,
+ isMarkSelectedCommand,
+ codeBlockSchema,
+ wrapInBlockTypeCommand,
+} from '@milkdown/kit/preset/commonmark';
+import { githubDark, githubLight } from '@uiw/codemirror-theme-github';
+import mermaid from 'mermaid';
+
+import { containerSchema, ContainerType } from '../plugins/plugin-container';
+import { highlightSchema, showColorPickerCommand } from '../plugins/plugin-highlight';
+import { iframeBlockSchema } from '../plugins/plugin-iframe';
+
+import Toast from '@/utils/Toast';
+import { nextTick, uid } from '@/utils/utils';
+
+export const getBlockEditConfig = (): BlockEditFeatureConfig => ({
+ buildMenu(builder) {
+ const advancedGroup = builder.getGroup('advanced');
+
+ advancedGroup.addItem('Iframe', {
+ icon: ' ',
+ label: 'Iframe',
+ onRun: (ctx) => {
+ const commands = ctx.get(commandsCtx);
+ const iframeBlock = iframeBlockSchema.type(ctx);
+ commands.call(clearTextInCurrentBlockCommand.key);
+ commands.call(addBlockTypeCommand.key, {
+ nodeType: iframeBlock,
+ attrs: { src: '' },
+ });
+ },
+ });
+
+ advancedGroup.addItem('Mermaid', {
+ icon: ' ',
+ label: 'Mermaid',
+ onRun: (ctx) => {
+ const commands = ctx.get(commandsCtx);
+ const codeBlock = codeBlockSchema.type(ctx);
+
+ commands.call(clearTextInCurrentBlockCommand.key);
+ commands.call(addBlockTypeCommand.key, {
+ nodeType: codeBlock,
+ attrs: { language: 'mermaid' },
+ });
+ },
+ });
+
+ const containerGroup = builder.addGroup('container', 'Container');
+ const containerItems = Object.values(ContainerType);
+
+ containerItems.forEach((item) => {
+ containerGroup.addItem(item, {
+ icon: ' ',
+ label: item.toUpperCase(),
+ onRun: (ctx) => {
+ const commands = ctx.get(commandsCtx);
+ const container = containerSchema.type(ctx);
+
+ commands.call(clearTextInCurrentBlockCommand.key);
+ commands.call(wrapInBlockTypeCommand.key, {
+ nodeType: container,
+ attrs: {
+ containerType: item,
+ },
+ });
+ },
+ });
+ });
+ },
+});
+
+export const getCodeMirrorConfig = (isDarkMode: boolean): CodeMirrorFeatureConfig => {
+ mermaid.initialize({ suppressErrorRendering: true, startOnLoad: false, theme: isDarkMode ? 'dark' : 'neutral' });
+
+ return {
+ theme: isDarkMode ? githubDark : githubLight,
+ renderPreview: (language, content, applyPreview) => {
+ if (language === 'mermaid' && content.trim()) {
+ const container = document.createElement('div');
+ const containerId = `mermaid-preview-container-${uid() as string}`;
+ container.setAttribute('id', containerId);
+
+ const placeholderDom = document.createElement('div');
+ placeholderDom.setAttribute('style', 'width: 100%; height: 250px;');
+ const placeholderDomId = `mermaid-preview-${uid() as string}`;
+ placeholderDom.setAttribute('id', placeholderDomId);
+ container.appendChild(placeholderDom);
+
+ const renderMermaid = async () => {
+ try {
+ const { svg, bindFunctions } = await mermaid.render(placeholderDomId, content);
+
+ applyPreview(container);
+
+ nextTick(() => {
+ // the container dom will be santified by applyPreview to another new dom
+ const santifiedContainer = document.getElementById(containerId);
+ if (!santifiedContainer) return;
+
+ santifiedContainer.innerHTML = svg;
+ bindFunctions?.(santifiedContainer);
+ });
+ } catch (e) {
+ container.innerHTML = `Error: ${
+ (e as Error).message
+ }
`;
+ }
+ };
+
+ void renderMermaid();
+ return;
+ }
+
+ return null;
+ },
+ onCopy: () => Toast('Code copied'),
+ };
+};
+
+export const getToolbarConfig = (): ToolbarFeatureConfig => ({
+ buildToolbar(builder) {
+ builder.getGroup('formatting').addItem('highlighter', {
+ icon: ' ',
+ active: (ctx) => {
+ const commands = ctx.get(commandsCtx);
+ return commands.call(isMarkSelectedCommand.key, highlightSchema.type(ctx));
+ },
+ onRun: (ctx) => {
+ const commands = ctx.get(commandsCtx);
+ commands.call(showColorPickerCommand.key);
+ },
+ });
+ },
+});
diff --git a/client/src/components/Editor/configs/gfmConfig.ts b/client/src/components/Editor/configs/gfmConfig.ts
deleted file mode 100644
index db66e15..0000000
--- a/client/src/components/Editor/configs/gfmConfig.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { gfm, codeFence } from '@milkdown/preset-gfm';
-
-const languageOptions = [
- '',
- 'javascript',
- 'typescript',
- 'html',
- 'css',
- 'bash',
- 'jsx',
- 'tsx',
- 'sql',
- 'json',
- 'c',
- 'cpp',
- 'java',
- 'ruby',
- 'python',
- 'go',
- 'rust',
- 'markdown',
-];
-
-export default gfm.configure(codeFence, {
- languageList: languageOptions,
-});
diff --git a/client/src/components/Editor/configs/menuConfig.ts b/client/src/components/Editor/configs/menuConfig.ts
deleted file mode 100644
index 6c30860..0000000
--- a/client/src/components/Editor/configs/menuConfig.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { menu, menuPlugin } from '@milkdown/plugin-menu';
-
-export default menu.configure(menuPlugin, {
- config: [
- [
- {
- type: 'button',
- icon: 'bulletList',
- key: 'WrapInBulletList',
- },
- {
- type: 'button',
- icon: 'orderedList',
- key: 'WrapInOrderedList',
- },
- {
- type: 'button',
- icon: 'taskList',
- key: 'TurnIntoTaskList',
- },
- ],
- [
- {
- type: 'button',
- icon: 'image',
- key: 'InsertImage',
- },
- {
- type: 'button',
- icon: 'table',
- key: 'InsertTable',
- },
- {
- type: 'button',
- icon: 'code',
- key: 'TurnIntoCodeFence',
- },
- {
- type: 'button',
- icon: 'quote',
- key: 'WrapInBlockquote',
- },
- {
- type: 'button',
- icon: 'link',
- key: 'InsertIframe',
- },
- ],
- ],
-});
diff --git a/client/src/components/Editor/configs/prismConfig.ts b/client/src/components/Editor/configs/prismConfig.ts
deleted file mode 100644
index f434e8d..0000000
--- a/client/src/components/Editor/configs/prismConfig.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { prismPlugin } from '@milkdown/plugin-prism';
-import jsx from 'refractor/lang/jsx';
-import tsx from 'refractor/lang/tsx';
-
-export default prismPlugin({
- configureRefractor: (refractor) => {
- refractor.register(jsx);
- refractor.register(tsx);
- },
-});
diff --git a/client/src/components/Editor/configs/slashConfig.ts b/client/src/components/Editor/configs/slashConfig.ts
deleted file mode 100644
index 5199cfc..0000000
--- a/client/src/components/Editor/configs/slashConfig.ts
+++ /dev/null
@@ -1,117 +0,0 @@
-import { Ctx, themeManagerCtx, commandsCtx, schemaCtx } from '@milkdown/core';
-import { slashPlugin, slash, createDropdownItem } from '@milkdown/plugin-slash';
-import { WrappedAction } from '@milkdown/plugin-slash/lib/item';
-
-const getActions = (ctx: Ctx, input = '/'): WrappedAction[] => {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
- const { nodes } = ctx.get(schemaCtx);
-
- const actions: (WrappedAction & { keyword: string[]; typeName: string })[] = [
- {
- id: 'bulletList',
- dom: createDropdownItem(ctx.get(themeManagerCtx), 'Bullet List', 'bulletList'),
- command: () => ctx.get(commandsCtx).call('WrapInBulletList'),
- keyword: ['bullet list', 'ul'],
- typeName: 'bullet_list',
- },
- {
- id: 'orderedList',
- dom: createDropdownItem(ctx.get(themeManagerCtx), 'Ordered List', 'orderedList'),
- command: () => ctx.get(commandsCtx).call('WrapInOrderedList'),
- keyword: ['ordered list', 'ol'],
- typeName: 'ordered_list',
- },
- {
- id: 'taskList',
- dom: createDropdownItem(ctx.get(themeManagerCtx), 'Task List', 'taskList'),
- command: () => ctx.get(commandsCtx).call('TurnIntoTaskList'),
- keyword: ['task list', 'task'],
- typeName: 'task_list_item',
- },
- {
- id: 'image',
- dom: createDropdownItem(ctx.get(themeManagerCtx), 'Image', 'image'),
- command: () => ctx.get(commandsCtx).call('InsertImage'),
- keyword: ['image'],
- typeName: 'image',
- },
- {
- id: 'blockquote',
- dom: createDropdownItem(ctx.get(themeManagerCtx), 'Quote', 'quote'),
- command: () => ctx.get(commandsCtx).call('WrapInBlockquote'),
- keyword: ['quote', 'blockquote'],
- typeName: 'blockquote',
- },
- {
- id: 'table',
- dom: createDropdownItem(ctx.get(themeManagerCtx), 'Table', 'table'),
- command: () => ctx.get(commandsCtx).call('InsertTable'),
- keyword: ['table'],
- typeName: 'table',
- },
- {
- id: 'code',
- dom: createDropdownItem(ctx.get(themeManagerCtx), 'Code Fence', 'code'),
- command: () => ctx.get(commandsCtx).call('TurnIntoCodeFence'),
- keyword: ['code'],
- typeName: 'fence',
- },
- {
- id: 'divider',
- dom: createDropdownItem(ctx.get(themeManagerCtx), 'Divide Line', 'divider'),
- command: () => ctx.get(commandsCtx).call('InsertHr'),
- keyword: ['divider', 'hr'],
- typeName: 'hr',
- },
- {
- id: 'iframe',
- dom: createDropdownItem(ctx.get(themeManagerCtx), 'Iframe', 'link'),
- command: () => ctx.get(commandsCtx).call('InsertIframe'),
- keyword: ['iframe'],
- typeName: 'iframe',
- },
- ];
-
- const userInput = input.slice(1).toLocaleLowerCase();
-
- return (
- actions
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- .filter((action) => !!nodes[action.typeName] && action.keyword.some((keyword) => keyword.includes(userInput)))
- // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
- .map(({ keyword, typeName, ...action }) => action)
- );
-};
-export default slash.configure(slashPlugin, {
- config: (ctx) => {
- // Get default slash plugin items
- const actions = getActions(ctx);
-
- // Define a status builder
- return ({ isTopLevel, content }) => {
- // You can only show something at root level
- if (!isTopLevel) return null;
-
- // Empty content ? Set your custom empty placeholder !
- if (!content) {
- return { placeholder: 'Type / to use the slash commands...' };
- }
-
- // Define the placeholder & actions (dropdown items) you want to display depending on content
- if (content.startsWith('/')) {
- // Add some actions depending on your content's parent node
- // if (parentNode.type.name === "iframe") {
- // }
-
- return content === '/'
- ? {
- placeholder: 'Type to search...',
- actions,
- }
- : {
- actions: getActions(ctx, content),
- };
- }
- };
- },
-});
diff --git a/client/src/components/Editor/configs/tooltipConfig.ts b/client/src/components/Editor/configs/tooltipConfig.ts
deleted file mode 100644
index 67fc2b2..0000000
--- a/client/src/components/Editor/configs/tooltipConfig.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { tooltip } from '@milkdown/plugin-tooltip';
-
-export default tooltip;
-// .configure(tooltipPlugin, {
-// link: {
-// placeholder: "Please input link...",
-// buttonText: "Confirm",
-// },
-// image: {
-// placeholder: "Please input image link...",
-// buttonText: "OK",
-// },
-// inlineMath: {
-// placeholder: "Please input inline math...",
-// },
-// });
diff --git a/client/src/components/Editor/configs/uploadConfig.ts b/client/src/components/Editor/configs/uploadConfig.ts
index 6a2db38..63c557e 100644
--- a/client/src/components/Editor/configs/uploadConfig.ts
+++ b/client/src/components/Editor/configs/uploadConfig.ts
@@ -1,70 +1,73 @@
-import { upload, uploadPlugin } from '@milkdown/plugin-upload';
+import { inlineImageConfig } from '@milkdown/kit/component/image-inline';
+import { uploadConfig, Uploader } from '@milkdown/kit/plugin/upload';
-import { useUploadImgMutation } from '@/redux-api/imgStoreApi';
-import Toast from '@/utils/Toast';
-import { dateFormat } from '@/utils/utils';
+import type { Ctx } from '@milkdown/kit/ctx';
+import type { Node } from '@milkdown/kit/prose/model';
-const uploader = ([uploadImgMutation]: ReturnType, curPath: string) =>
- upload.configure(uploadPlugin, {
- uploader: async (files, schema) => {
- const images: File[] = [];
+import { SERVER_PORT } from '@/constants';
- for (let i = 0; i < files.length; i++) {
- const file = files.item(i);
- if (!file) {
- continue;
- }
+export function getImageUrl(url: string) {
+ if (url.startsWith('/')) {
+ return `http://127.0.0.1:${SERVER_PORT}/api/imgs${url}`;
+ }
+ return url;
+}
- // You can handle whatever the file type you want, we handle image here.
- if (!file.type.includes('image')) {
- continue;
- }
-
- images.push(file);
- }
+export async function uploadImage(file: File) {
+ const formData = new FormData();
+ formData.append('file', file);
+ const res = await fetch(`http://127.0.0.1:${SERVER_PORT}/api/imgs/upload`, {
+ method: 'POST',
+ body: formData,
+ });
+ const json = await res.json();
+ return json.data as string;
+}
- const data = await Promise.all(
- images.map(async (image) => {
- let src = '';
- let alt = '';
+const uploader: Uploader = async (files, schema) => {
+ const images: File[] = [];
- try {
- const resp = await uploadImgMutation({
- imgFile: image,
- fileName: `${curPath}_${dateFormat(new Date(Date.now()), 'YYYY-MM-DD-HH:mm:ss') as string}.${
- image.name.split('.')[1]
- }`,
- }).unwrap();
+ for (let i = 0; i < files.length; i++) {
+ const file = files.item(i);
+ if (!file) {
+ continue;
+ }
- // eslint-disable-next-line @typescript-eslint/no-magic-numbers
- if (resp.err === 1 || resp.status !== 200) throw new Error(resp.message);
+ // we only handle image here.
+ if (!file.type.includes('image')) {
+ continue;
+ }
- src = `${resp.requestUrls[0] as string}`;
- alt = `${Date.now()}-${image.name}`;
+ images.push(file);
+ }
- Toast(resp.message, 'SUCCESS');
- } catch (err) {
- Toast(`failed to upload: ${String(err)}`, 'ERROR');
- src = `http://markdown-img-store.oss-cn-shenzhen.aliyuncs.com/snow2.png`;
- alt = image.name;
- }
+ const nodes: Node[] = await Promise.all(
+ images.map(async (image) => {
+ const src = await uploadImage(image);
+ const alt = image.name;
+ return schema.nodes.image.createAndFill({
+ src,
+ alt,
+ })!;
+ }),
+ );
- return {
- src,
- alt,
- };
- }),
- );
+ return nodes;
+};
- return data.map(
- ({ src, alt }) =>
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
- schema.nodes.image.createAndFill({
- src,
- alt,
- })!,
- );
- },
+export const configureImageUploader = (ctx: Ctx) => {
+ ctx.update(uploadConfig.key, (prev) => {
+ return {
+ ...prev,
+ uploader,
+ };
});
-export default uploader;
+ ctx.update(inlineImageConfig.key, (prev) => {
+ return {
+ ...prev,
+ proxyDomURL: getImageUrl,
+ onUpload: uploadImage,
+ };
+ });
+};
diff --git a/client/src/components/Editor/crepe.ts b/client/src/components/Editor/crepe.ts
new file mode 100644
index 0000000..f219ee4
--- /dev/null
+++ b/client/src/components/Editor/crepe.ts
@@ -0,0 +1,55 @@
+import { Crepe } from '@milkdown/crepe';
+import { listener } from '@milkdown/kit/plugin/listener';
+import { upload } from '@milkdown/kit/plugin/upload';
+
+import { getBlockEditConfig, getCodeMirrorConfig, getToolbarConfig } from './configs/crepeFeatures';
+import { configureImageUploader, getImageUrl, uploadImage } from './configs/uploadConfig';
+import { containerPlugin } from './plugins/plugin-container';
+import { headingPlugin } from './plugins/plugin-heading';
+import { configureColorPicker, highlightMarkerPlugin } from './plugins/plugin-highlight';
+import { iframePlugin } from './plugins/plugin-iframe';
+
+import Toast from '@/utils/Toast';
+
+export function getCrepe({
+ root,
+ defaultValue,
+ isDarkMode,
+}: {
+ root: HTMLElement;
+ defaultValue: string;
+ isDarkMode: boolean;
+}) {
+ const crepe = new Crepe({
+ root,
+ defaultValue,
+ featureConfigs: {
+ [Crepe.Feature.BlockEdit]: getBlockEditConfig(),
+ [Crepe.Feature.Toolbar]: getToolbarConfig(),
+ [Crepe.Feature.CodeMirror]: getCodeMirrorConfig(isDarkMode),
+ [Crepe.Feature.LinkTooltip]: {
+ onCopyLink: () => {
+ Toast('Link copied');
+ },
+ },
+ [Crepe.Feature.ImageBlock]: {
+ proxyDomURL: getImageUrl,
+ onUpload: uploadImage,
+ },
+ },
+ });
+
+ crepe.editor
+ .config((ctx) => {
+ configureColorPicker(ctx);
+ configureImageUploader(ctx);
+ })
+ .use(upload)
+ .use(headingPlugin)
+ .use(listener)
+ .use(iframePlugin)
+ .use(containerPlugin)
+ .use(highlightMarkerPlugin);
+
+ return crepe;
+}
diff --git a/client/src/components/Editor/internalDocs/guide.ts b/client/src/components/Editor/internalDocs/guide.ts
new file mode 100644
index 0000000..666110d
--- /dev/null
+++ b/client/src/components/Editor/internalDocs/guide.ts
@@ -0,0 +1,41 @@
+import { APP_VERSION } from '@/constants';
+import { getServerDownloadUrl } from '@/utils/utils';
+
+export const getGuideDoc = () => {
+ const serverDownloadUrl = getServerDownloadUrl(APP_VERSION);
+
+ return `
+ ## Guide (${APP_VERSION})
+
+ Welcome to use the Markdown Editor.
+
+ ## Install the local server
+
+ To unlock the full features of the editor, you need to install the local server.
+
+ 1. Install the [local server](${serverDownloadUrl}).
+
+ 2. Unzip the file and run the binary file **as administrator**.
+
+ :::warning
+ The file will automatically register auto start when you open your computer.
+ :::
+
+ :::tip
+ you can manage the server in terminal
+
+ \`\`\`bash
+ # checkout the help information
+ $ mds -h
+
+ # stop the server
+ $ mds stop
+ # checkout server status
+ $ mds status
+
+ # uninstall the server, this will remove the cli
+ $ mds uninstall
+ \`\`\`
+ :::
+ `;
+};
diff --git a/client/src/components/Editor/internalDocs/versionMismatch.ts b/client/src/components/Editor/internalDocs/versionMismatch.ts
new file mode 100644
index 0000000..d78f8af
--- /dev/null
+++ b/client/src/components/Editor/internalDocs/versionMismatch.ts
@@ -0,0 +1,23 @@
+import { APP_VERSION, GITHUB_PAGES_BASE_PATH } from '@/constants';
+import { getServerDownloadUrl } from '@/utils/utils';
+
+export const getVersionMismatchDoc = () => {
+ const serverDownloadUrl = getServerDownloadUrl(APP_VERSION);
+ const guideHref = `${GITHUB_PAGES_BASE_PATH}internal/guide`;
+
+ return `
+ ## Version mismatch
+
+ Current version: ${APP_VERSION}
+
+ The version of the local server is different from the current version of the editor.
+
+ Please install the latest version of the local server.
+
+ 1. Install the [local server](${serverDownloadUrl}).
+
+ 2. Unzip the file and run the binary file **as administrator**.
+
+ For more information, please refer to the [guide](${guideHref}).
+ `;
+};
diff --git a/client/src/components/Editor/mountedAddons.tsx b/client/src/components/Editor/mountedAddons.tsx
index 12e0ce8..f53010e 100644
--- a/client/src/components/Editor/mountedAddons.tsx
+++ b/client/src/components/Editor/mountedAddons.tsx
@@ -1,18 +1,14 @@
/* eslint-disable @typescript-eslint/no-magic-numbers */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-import ClipboardJS from 'clipboard';
-import ReactDOM from 'react-dom';
-import { useDispatch, Provider } from 'react-redux';
-import { BrowserRouter } from 'react-router-dom';
+import { Decoration, DecorationSet } from '@milkdown/kit/prose/view';
+import { useDispatch } from 'react-redux';
-import Outline from '../Outline/Outline';
+import type { EditorView } from '@milkdown/kit/prose/view';
import { updateScrolling } from '@/redux-feature/curDocSlice';
import { updateGlobalOpts } from '@/redux-feature/globalOptsSlice';
-import { store } from '@/store';
-import { useEditorScrollToAnchor } from '@/utils/hooks/docHooks';
-import Toast from '@/utils/Toast';
-import { throttle, updateLocationHash } from '@/utils/utils';
+import { getEditorScrollContainer, scrollToEditorAnchor } from '@/utils/hooks/docHooks';
+import { throttle } from '@/utils/utils';
let removers: (() => void)[] = [];
function pushRemover(r: () => void) {
@@ -33,26 +29,36 @@ export function removeEvents() {
* then record current docs scroll top globally
*/
export function scrollHandler(prevScroll: number, dispatch: ReturnType) {
- const milkdownDom = document.getElementsByClassName('milkdown')[0];
+ const milkdownDom = getEditorScrollContainer();
+ if (!milkdownDom) return;
// get the previous scroll top
milkdownDom.scrollTop = prevScroll;
+ const syncAnchor = () => {
+ const headings = document.querySelectorAll('.milkdown-heading');
+ headings.forEach((ha) => {
+ const rect = ha.getBoundingClientRect();
+ if (rect.top > 0 && rect.top < 150) {
+ // notify the outline to select the heading, but not set the anchor to location hash
+ dispatch(updateGlobalOpts({ keys: ['anchor'], values: [ha.id] }));
+ }
+ });
+ };
+
+ // wait for the outline
+ setTimeout(() => {
+ syncAnchor();
+ }, 100);
+
// bind the event after the first rendering caused by the above operation...
setTimeout(() => {
const eventFn = throttle(() => {
const currentScrollTop = milkdownDom.scrollTop;
- // sync to the location hash
- const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
- headings.forEach((ha) => {
- const rect = ha.getBoundingClientRect();
- if (rect.top > 0 && rect.top < 150) {
- updateLocationHash(ha.getAttribute('id') ?? '');
- }
- });
-
dispatch(updateScrolling({ scrollTop: currentScrollTop }));
+
+ syncAnchor();
}, 100);
milkdownDom.addEventListener('scroll', eventFn);
@@ -98,49 +104,17 @@ export function blurHandler(dispatch: ReturnType) {
/**
* handle anchor
*/
-export function anchorHandler(
- anchor: string,
- dispatch: ReturnType,
- scrollToAnchor: ReturnType,
-) {
+export function anchorHandler(dispatch: ReturnType) {
const locationHash = window.location.hash.slice(1);
let hashAnchor = '';
if (locationHash) {
const heading = document.getElementById(decodeURI(locationHash));
if (heading) {
- hashAnchor = heading.innerText;
+ hashAnchor = heading.id;
}
}
- scrollToAnchor(hashAnchor || anchor);
-
- // clear the anchor to avoid re-anchor when switch modes
- // the actual scrolling will be recorded in current global doc info above
- dispatch(updateGlobalOpts({ keys: ['anchor'], values: [''] }));
-}
-
-/**
- * handle heading anchor (add the outline aside headings)
- */
-export function addHeadingAnchor(curPath: string[]) {
- // add outline on each heading
- const headingDoms = document.getElementsByClassName('heading');
- if (!headingDoms) return;
-
- for (const headingDom of headingDoms) {
- const div = document.createElement('div');
- div.classList.add('heading-outline');
-
- headingDom.appendChild(div);
-
- ReactDOM.render(
-
-
-
-
- ,
- div,
- );
- }
+ scrollToEditorAnchor(hashAnchor);
+ dispatch(updateGlobalOpts({ keys: ['anchor'], values: [hashAnchor || ''] }));
}
export function keywordsHandler(keywords: string[]) {
@@ -159,49 +133,6 @@ export function keywordsHandler(keywords: string[]) {
}
}
-/**
- * add a copy btn at each code fence
- */
-export function addClipboard(readonly: boolean) {
- if (!readonly) return;
-
- const codeFences = document.getElementsByClassName('code-fence') as HTMLCollectionOf;
-
- const clipboards: ClipboardJS[] = [];
-
- for (const [idx, codeFence] of [...codeFences].entries()) {
- // get the code dom and add an id attribute
- const codeDom = codeFence.querySelector('code');
- codeDom?.setAttribute('id', `code-${idx}`);
-
- const copyBtn = document.createElement('button');
- copyBtn.classList.add('code-fence-copy-btn');
- copyBtn.innerText = `copy`;
- copyBtn.setAttribute('data-clipboard-target', `#code-${idx}`);
-
- codeFence.appendChild(copyBtn);
-
- const clipboard = new ClipboardJS(copyBtn);
-
- clipboard
- .on('success', (e) => {
- e.clearSelection();
- Toast('copied!', 'SUCCESS');
- })
- .on('error', () => {
- Toast('failed to copy...', 'ERROR');
- });
-
- clipboards.push(clipboard);
- }
-
- pushRemover(() => {
- clipboards.forEach((c) => {
- c.destroy();
- });
- });
-}
-
/**
* sync the mirror anchor when double clicking a element in Editor
* but only works for readonly mode currently...
@@ -209,7 +140,7 @@ export function addClipboard(readonly: boolean) {
export function syncMirror(readonly: boolean) {
if (!readonly) return;
- const editorDom = document.querySelector('.editor');
+ const editorDom = document.querySelector('.milkdown .editor');
if (!editorDom) return;
const blockDoms = editorDom.children;
@@ -228,13 +159,17 @@ export function syncMirror(readonly: boolean) {
curTotalLine--;
}
+ if (blockDom.classList.contains('milkdown-code-block')) {
+ curTotalLine -= blockDom.querySelector('.cm-content')?.children.length ?? 0;
+ }
+
return curTotalLine + lines.length + 1;
}, 0);
[...blockDoms].forEach((blockDom, idx) => {
const dbClickEvent = (e: Event) => {
- const mirrorDom = document.querySelector('.cm-content');
- const mirrorScroller = document.querySelector('.cm-scroller');
+ const mirrorDom = document.querySelector('.code-mirror-container .cm-content');
+ const mirrorScroller = document.querySelector('.code-mirror-container .cm-scroller');
if (!mirrorDom || !mirrorScroller) return;
const lineDoms = mirrorDom.children;
@@ -247,7 +182,7 @@ export function syncMirror(readonly: boolean) {
let lineNum = blockLineNum[idx];
// when it is a paragraph and it is one of children of the blockDom
// make the position more accurate
- if (clickDom !== blockDom && clickDom.classList.contains('paragraph')) {
+ if (clickDom !== blockDom && clickDom.tagName === 'P') {
const lines = (blockDom as HTMLElement).innerText.split('\n');
if (clickDom) {
@@ -258,7 +193,8 @@ export function syncMirror(readonly: boolean) {
}
mirrorScroller.scroll({
- top: lineNum * oneLineHeight,
+ // just make some offset
+ top: (lineNum - 3) * oneLineHeight,
behavior: 'smooth',
});
};
@@ -269,3 +205,117 @@ export function syncMirror(readonly: boolean) {
});
});
}
+
+const HIGHLIGHT_DURATION_MS = 3500;
+
+function applyHighlightAndScroll(view: EditorView, from: number, to: number) {
+ // Scroll the match into view, positioned at roughly 1/3 from the top
+ const scrollContainer = getEditorScrollContainer();
+ if (scrollContainer) {
+ try {
+ const coords = view.coordsAtPos(from);
+ const containerRect = scrollContainer.getBoundingClientRect();
+ const scrollTop = Number(scrollContainer.scrollTop);
+ scrollContainer.scrollTo({
+ top: scrollTop + (coords.top - containerRect.top) - containerRect.height / 3,
+ behavior: 'instant',
+ });
+ } catch {
+ /* ignore coordinate errors */
+ }
+ }
+
+ // Add a visual decoration highlight that works regardless of focus state.
+ // Native selection depends on focus, contenteditable, and browser quirks;
+ // decorations are rendered as styled DOM elements and always visible.
+ try {
+ view.setProps({
+ decorations(state) {
+ try {
+ return DecorationSet.create(state.doc, [Decoration.inline(from, to, { class: 'search-match-highlight' })]);
+ } catch {
+ return DecorationSet.empty;
+ }
+ },
+ });
+
+ setTimeout(() => {
+ try {
+ if (!view.dom.isConnected) return;
+ view.setProps({ decorations: () => DecorationSet.empty });
+ } catch {
+ /* view may have been destroyed */
+ }
+ }, HIGHLIGHT_DURATION_MS);
+ } catch {
+ /* setProps failed */
+ }
+}
+
+function stripMarkdownSyntax(text: string): string {
+ return text
+ .replace(/^#{1,6}\s+/, '')
+ .replace(/\*\*(.+?)\*\*/g, '$1')
+ .replace(/__(.+?)__/g, '$1')
+ .replace(/\*(.+?)\*/g, '$1')
+ .replace(/_(.+?)_/g, '$1')
+ .replace(/~~(.+?)~~/g, '$1')
+ .replace(/`(.+?)`/g, '$1')
+ .replace(/^\s*[-*+]\s+/, '')
+ .replace(/^\s*\d+\.\s+/, '')
+ .replace(/^\s*>\s+/, '')
+ .replace(/\[(.+?)\]\(.+?\)/g, '$1')
+ .trim();
+}
+
+/**
+ * Search for query text in the ProseMirror document and select the best match.
+ * Uses lineContent (raw markdown line) for disambiguation when multiple matches exist.
+ * Returns true if a match was found and selected.
+ */
+export function searchAndHighlight(view: EditorView, query: string, lineContent: string): boolean {
+ const { doc } = view.state;
+ const lowerQuery = query.toLowerCase();
+ const strippedLine = stripMarkdownSyntax(lineContent).toLowerCase();
+
+ const matches: { from: number; to: number; blockText: string }[] = [];
+
+ doc.descendants((node, pos) => {
+ if (!node.isTextblock) return;
+
+ const text = node.textContent;
+ const lowerText = text.toLowerCase();
+ let searchFrom = 0;
+
+ while (searchFrom < lowerText.length) {
+ const idx = lowerText.indexOf(lowerQuery, searchFrom);
+ if (idx === -1) break;
+
+ matches.push({
+ from: pos + 1 + idx,
+ to: pos + 1 + idx + query.length,
+ blockText: lowerText,
+ });
+
+ searchFrom = idx + 1;
+ }
+
+ return false;
+ });
+
+ if (matches.length === 0) return false;
+
+ let bestMatch = matches[0];
+ if (matches.length > 1 && strippedLine) {
+ for (const match of matches) {
+ if (strippedLine.includes(match.blockText) || match.blockText.includes(strippedLine)) {
+ bestMatch = match;
+ break;
+ }
+ }
+ }
+
+ applyHighlightAndScroll(view, bestMatch.from, bestMatch.to);
+
+ return true;
+}
diff --git a/client/src/components/Editor/plugins/iframe-plugin/iframe.ts b/client/src/components/Editor/plugins/iframe-plugin/iframe.ts
deleted file mode 100644
index b5d2f4b..0000000
--- a/client/src/components/Editor/plugins/iframe-plugin/iframe.ts
+++ /dev/null
@@ -1,218 +0,0 @@
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-explicit-any */
-import * as emotion from '@emotion/css';
-import { RemarkPlugin, createCmdKey, createCmd, ThemeInputChipType, commandsCtx } from '@milkdown/core';
-import { findSelectedNodeOfType, NodeViewFactory } from '@milkdown/prose';
-import { Plugin, PluginKey } from '@milkdown/prose/state';
-import { EditorView } from '@milkdown/prose/view';
-import { AtomList, createNode } from '@milkdown/utils';
-import { InputRule } from 'prosemirror-inputrules';
-import directive from 'remark-directive';
-
-import getIframeRenderer from './renderer';
-import { IframeOptions } from './type';
-export const InsertIframe = createCmdKey('InsertIframe');
-export const ModifyIframe = createCmdKey('ModifyIframe');
-
-const key = new PluginKey('MILKDOWN_IFRAME_INPUT');
-
-const id = 'iframe';
-const iframe = createNode((utils, options) => {
- return {
- id,
- schema: () => ({
- attrs: {
- src: { default: null },
- },
- group: 'inline',
- inline: true,
- marks: '',
- atom: true,
- parseDOM: [
- {
- tag: 'iframe',
- getAttrs: (dom) => {
- if (!(dom instanceof HTMLElement)) {
- throw new Error();
- }
- return {
- src: dom.getAttribute('src'),
- };
- },
- },
- ],
- toDOM: (node) => {
- const div = document.createElement('div');
- div.classList.add('iframe-click');
- div.innerText = 'click';
-
- return ['iframe', { ...node.attrs, class: 'iframe' }];
- },
- parseMarkdown: {
- match: (node) => {
- return node.type === 'textDirective' && node.name === 'iframe';
- },
- runner: (state, node, type) => {
- state.addNode(type, {
- src: (node.attributes as { src: string }).src,
- });
- },
- },
- toMarkdown: {
- match: (node) => node.type.name === id,
- runner: (state, node) => {
- state.addNode('textDirective', undefined, undefined, {
- name: 'iframe',
- attributes: {
- src: node.attrs.src as string,
- },
- });
- },
- },
- }),
- inputRules: (nodeType) => [
- new InputRule(
- // :iframe{src="url"}
- /:iframe\{src="(?[^"]+)?"?\}/,
- (state, match, start, end) => {
- const [okay, src = ''] = match;
- const { tr } = state;
- if (okay) {
- tr.replaceWith(start, end, nodeType.create({ src }));
- }
-
- return tr;
- },
- ),
- ],
- commands: (type) => [
- createCmd(InsertIframe, (src = '') => (state: any, dispatch: any) => {
- if (!dispatch) return true;
- const { tr } = state;
- const node = type.create({ src });
- if (!node) {
- return true;
- }
- // eslint-disable-next-line @typescript-eslint/naming-convention
- const _tr = tr.replaceSelectionWith(node);
- dispatch(_tr.scrollIntoView());
- return true;
- }),
- createCmd(ModifyIframe, (src = '') => (state: any, dispatch: any) => {
- const node = findSelectedNodeOfType(state.selection, type);
- if (!node) return false;
-
- const { tr } = state;
- dispatch?.(
- tr
- .setNodeMarkup(node.pos, undefined, {
- ...node.node.attrs,
- loading: true,
- src,
- })
- .scrollIntoView(),
- );
-
- return true;
- }),
- ],
- view: () =>
- ((node) => {
- let currNode = node;
-
- const placeholder = options?.placeholder ?? 'Add an Iframe';
- const isBlock = options?.isBlock ?? false;
-
- const renderer = getIframeRenderer(
- utils.themeManager,
- emotion,
- )({
- placeholder,
- isBlock,
- });
-
- if (!renderer) {
- return {};
- }
-
- const { dom, onUpdate } = renderer;
- onUpdate(currNode);
-
- return {
- dom,
- update: (updatedNode) => {
- if (updatedNode.type.name !== id) return false;
-
- currNode = updatedNode;
- onUpdate(currNode);
-
- return true;
- },
- selectNode: () => {
- dom.classList.add('ProseMirror-selectedNode');
- },
- deselectNode: () => {
- dom.classList.remove('ProseMirror-selectedNode');
- },
- };
- }) as NodeViewFactory,
- prosePlugins: (type, ctx) => {
- return [
- new Plugin({
- key,
- view: (editorView) => {
- const inputChipRenderer = utils.themeManager.get('input-chip', {
- placeholder: options?.input?.placeholder ?? 'Input Iframe Link',
- buttonText: options?.input?.buttonText,
- onUpdate: (value) => {
- ctx.get(commandsCtx).call(ModifyIframe, value);
- },
- });
- if (!inputChipRenderer) return {};
- const shouldDisplay = (view: EditorView) => {
- return Boolean(view.hasFocus() && type && findSelectedNodeOfType(view.state.selection, type));
- };
- const getCurrentLink = (view: EditorView) => {
- const result = findSelectedNodeOfType(view.state.selection, type);
- if (!result) return;
-
- const value = result.node.attrs.src;
- return value as string;
- };
- const renderByView = (view: EditorView) => {
- if (!view.editable) {
- return;
- }
- const display = shouldDisplay(view);
- if (display) {
- inputChipRenderer.show(view);
- inputChipRenderer.update(getCurrentLink(view) ?? '');
- } else {
- inputChipRenderer.hide();
- }
- };
- inputChipRenderer.init(editorView);
- renderByView(editorView);
-
- return {
- update: (view, prevState) => {
- const isEqualSelection =
- prevState?.doc.eq(view.state.doc) && prevState.selection.eq(view.state.selection);
- if (isEqualSelection) return;
-
- renderByView(view);
- },
- destroy: () => {
- inputChipRenderer.destroy();
- },
- };
- },
- }),
- ];
- },
- remarkPlugins: () => [directive as RemarkPlugin],
- };
-});
-
-export default AtomList.create([iframe()]);
diff --git a/client/src/components/Editor/plugins/iframe-plugin/renderer.ts b/client/src/components/Editor/plugins/iframe-plugin/renderer.ts
deleted file mode 100644
index 7d2d85e..0000000
--- a/client/src/components/Editor/plugins/iframe-plugin/renderer.ts
+++ /dev/null
@@ -1,179 +0,0 @@
-/* eslint-disable @typescript-eslint/no-magic-numbers */
-import { Emotion, getPalette, Icon, ThemeIcon, ThemeManager, ThemeSize } from '@milkdown/core';
-
-import { ThemeOptions } from './type';
-
-import type { Node } from '@milkdown/prose/model';
-
-const renderer = (manager: ThemeManager, { css }: Emotion) => {
- const palette = getPalette(manager);
- return ({ placeholder, isBlock, onError, onLoad }: ThemeOptions) => {
- const createIcon = (icon: Icon) => manager.get(ThemeIcon, icon)?.dom;
-
- const container = document.createElement('span');
- container.classList.add('iframe-container');
-
- manager.onFlush(() => {
- const style = css`
- width: 100%;
- padding: 0 3rem 0 0;
- position: relative;
- &::before {
- content: '';
- position: absolute;
- width: 2rem;
- margin-left: 1rem;
- height: 100%;
- right: 0;
- top: 0;
- background-color: rgb(55, 52, 52);
- cursor: pointer;
- }
- display: inline-block;
- position: relative;
- text-align: center;
- font-size: 0;
- vertical-align: text-bottom;
- line-height: 1;
- ${isBlock
- ? `
- width: 100%;
- margin: 0 auto;
- `
- : ''}
- &.ProseMirror-selectedNode::after {
- content: '';
- background: ${palette('secondary', 0.38)};
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- }
- iframe {
- width: 100%;
- height: 45rem;
- border: none;
- }
- .icon,
- .placeholder {
- display: none;
- }
- &.system {
- width: 100%;
- padding: 0 2em;
- font-size: inherit;
- iframe {
- width: 0;
- height: 0;
- display: none;
- }
- .icon,
- .placeholder {
- display: inline;
- }
- .icon {
- margin-left: 3rem;
- }
- box-sizing: border-box;
- height: 3em;
- background-color: ${palette('background')};
- border-radius: ${manager.get(ThemeSize, 'radius')};
- display: inline-flex;
- gap: 2em;
- justify-content: flex-start;
- align-items: center;
- .placeholder {
- margin: 0;
- line-height: 1;
- &::before {
- content: '';
- font-size: 0.875em;
- color: ${palette('neutral', 0.6)};
- }
- }
- }
- &.empty {
- .placeholder {
- &::before {
- content: '${placeholder}';
- }
- }
- }
- `;
-
- if (style) {
- container.classList.add(style);
- }
- });
-
- const content = document.createElement('iframe');
-
- container.append(content);
- let icon = createIcon('link');
- const $placeholder = document.createElement('span');
- $placeholder.classList.add('placeholder');
- container.append(icon, $placeholder);
-
- const setIcon = (name: Icon) => {
- const nextIcon = createIcon(name);
- container.replaceChild(nextIcon, icon);
- icon = nextIcon;
- };
-
- const loadIframe = (src: string) => {
- container.classList.add('system', 'loading');
- setIcon('loading');
- const iframe = document.createElement('iframe');
- iframe.src = src;
-
- iframe.onerror = () => {
- onError?.(iframe);
- };
-
- iframe.onload = () => {
- onLoad?.(iframe);
- };
- };
-
- const onUpdate = (node: Node) => {
- const { src, loading, failed } = node.attrs;
- content.src = src as string;
-
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- if (src.length === 0) {
- container.classList.add('system', 'empty');
- setIcon('link');
- return;
- }
-
- if (loading) {
- loadIframe(src);
- return;
- }
-
- if (failed) {
- container.classList.remove('loading', 'empty');
- container.classList.add('system', 'failed');
- setIcon('brokenImage');
- return;
- }
-
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- if (src.length > 0) {
- container.classList.remove('system', 'empty', 'loading');
- return;
- }
-
- container.classList.add('system', 'empty');
- setIcon('link');
- };
-
- return {
- dom: container,
- onUpdate,
- };
- };
-};
-
-export default renderer;
diff --git a/client/src/components/Editor/plugins/iframe-plugin/type.ts b/client/src/components/Editor/plugins/iframe-plugin/type.ts
deleted file mode 100644
index 60b86a0..0000000
--- a/client/src/components/Editor/plugins/iframe-plugin/type.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-export interface ThemeOptions {
- isBlock: boolean;
- placeholder: string;
- onError?: (img: HTMLIFrameElement) => void;
- onLoad?: (img: HTMLIFrameElement) => void;
-}
-
-// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
-export type IframeOptions = {
- isBlock: boolean;
- placeholder: string;
- input: {
- placeholder: string;
- buttonText?: string;
- };
-};
diff --git a/client/src/components/Editor/plugins/plugin-container/ContainerTitle.tsx b/client/src/components/Editor/plugins/plugin-container/ContainerTitle.tsx
new file mode 100644
index 0000000..850d085
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-container/ContainerTitle.tsx
@@ -0,0 +1,147 @@
+import { ListBox } from 'primereact/listbox';
+import { FC, useEffect, useId, useMemo, useRef, useState } from 'react';
+
+import { ContainerType } from './types';
+
+import { TooltipInput } from '@/components/Editor/components/TooltipInput';
+
+export interface ContainerTitleProps {
+ desc?: string;
+ containerType: ContainerType;
+ contentDom: HTMLElement;
+ getReadonly: () => boolean;
+ setAttrs?: (attrs: { containerType: ContainerType; desc: string }) => void;
+}
+
+export const ContainerTitle: FC = ({
+ desc = '',
+ containerType,
+ contentDom,
+ getReadonly,
+ setAttrs,
+}) => {
+ const uid = useId();
+
+ const isDetails = containerType === ContainerType.DETAILS;
+ const title = desc || (isDetails ? 'Details' : containerType.toUpperCase());
+
+ const [showOperations, setShowOperations] = useState(false);
+ const [showSelectorMenu, setShowSelectorMenu] = useState(false);
+ const [showEditDesc, setEditDesc] = useState(false);
+
+ const [descInput, setDescInput] = useState(desc);
+
+ const iconRef = useRef(null);
+ const listContainerRef = useRef(null);
+
+ const showSelectorIcon = useMemo(() => {
+ return (showOperations || showSelectorMenu) && !getReadonly();
+ }, [showOperations, showSelectorMenu, getReadonly]);
+ const showEditDescIcon = useMemo(() => {
+ return (showOperations || showEditDesc) && !getReadonly();
+ }, [showOperations, showEditDesc, getReadonly]);
+
+ const onToggle = () => {
+ if (containerType !== ContainerType.DETAILS) {
+ return;
+ }
+
+ iconRef.current?.classList.toggle('pi-angle-down');
+ iconRef.current?.classList.toggle('pi-angle-right');
+ contentDom.classList.toggle('container-content-hidden');
+ };
+
+ const showSelectContainerType = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ setShowSelectorMenu(!showSelectorMenu);
+ };
+
+ const showDescEditor = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ setEditDesc(!showEditDesc);
+ };
+
+ const selectContainerType = (value: ContainerType) => {
+ setAttrs?.({ containerType: value, desc: descInput });
+ };
+
+ const clickHandler = (e: MouseEvent) => {
+ const target = e.target as HTMLElement;
+
+ if (listContainerRef.current?.contains?.(target)) return;
+
+ setShowSelectorMenu(false);
+ };
+
+ useEffect(() => {
+ window.addEventListener('click', clickHandler);
+ return () => {
+ window.removeEventListener('click', clickHandler);
+ };
+ }, []);
+
+ const titleContent = (
+
+
{title}
+ {showSelectorIcon && (
+
+
+ {showSelectorMenu && (
+ {
+ e.stopPropagation();
+ }}
+ >
+ {
+ selectContainerType(e.value);
+ }}
+ options={Object.keys(ContainerType)}
+ />
+
+ )}
+
+ {showEditDescIcon && (
+
+ {
+ setDescInput(value);
+ }}
+ onConfirm={() => {
+ setAttrs?.({ containerType: containerType, desc: descInput });
+ }}
+ />
+
+ )}
+
+ )}
+
+ );
+
+ return (
+ {
+ setShowOperations(true);
+ }}
+ onMouseLeave={() => {
+ setShowOperations(false);
+ }}
+ >
+ {isDetails && }
+ {titleContent}
+
+ );
+};
diff --git a/client/src/components/Editor/plugins/plugin-container/index.scss b/client/src/components/Editor/plugins/plugin-container/index.scss
new file mode 100644
index 0000000..a2a189e
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-container/index.scss
@@ -0,0 +1,78 @@
+@use '@/utils/utils.scss' as *;
+@use '@/components/Editor/components/TooltipInput';
+
+$tipBackgroundColor: #646cff24;
+$infoBackgroundColor: #8e96aa24;
+$warningBackgroundColor: #eab30824;
+$dangerBackgroundColor: #f43f5e24;
+$detailsBackgroundColor: #8e96aa24;
+
+.milkdown-container-block {
+ margin: 16px 0;
+ padding: 16px 16px 8px;
+ border-radius: 8px;
+ &-title {
+ font-weight: 600;
+ line-height: 24px;
+ @include flex-center();
+ justify-content: flex-start;
+ .pi {
+ margin-right: 3px;
+ cursor: pointer;
+ }
+ .title-container {
+ position: relative;
+ @include flex-center();
+ justify-content: flex-start;
+ .operations {
+ margin-left: 5px;
+ @include flex-center();
+ i {
+ font-size: 12px;
+ margin-right: 5px;
+ }
+ }
+ .list-container {
+ position: absolute;
+ z-index: 1000;
+ }
+ .p-listbox-list-wrapper,
+ .p-listbox-list,
+ .p-listbox-item-group {
+ border-radius: 5px;
+ padding: 0.5rem 0;
+ background-color: var(--crepe-color-surface);
+ }
+ .p-listbox {
+ // border: 1px solid $shadowColor;
+ box-shadow: var(--crepe-shadow-1);
+ }
+ .p-listbox-item {
+ padding: 0.5rem;
+ font-weight: bold;
+ }
+ }
+ }
+ .container-content-hidden {
+ display: none;
+ }
+ &[data-container-type="tip"] {
+ background-color: $tipBackgroundColor;
+ }
+ &[data-container-type="warning"] {
+ background-color: $warningBackgroundColor;
+ }
+ &[data-container-type="info"] {
+ background-color: $infoBackgroundColor;
+ }
+ &[data-container-type="danger"] {
+ background-color: $dangerBackgroundColor;
+ }
+ &[data-container-type="details"] {
+ background-color: $detailsBackgroundColor;
+ .milkdown-container-block-title {
+ margin-bottom: 8px;
+ cursor: pointer;
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/components/Editor/plugins/plugin-container/index.ts b/client/src/components/Editor/plugins/plugin-container/index.ts
new file mode 100644
index 0000000..6d300e9
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-container/index.ts
@@ -0,0 +1,160 @@
+/* eslint-disable @typescript-eslint/naming-convention */
+import { MilkdownPlugin } from '@milkdown/kit/ctx';
+import { wrapIn } from '@milkdown/kit/prose/commands';
+import { $nodeSchema, $inputRule, $command, $view } from '@milkdown/utils';
+import { wrappingInputRule } from 'prosemirror-inputrules';
+import { createElement } from 'react';
+import { createRoot } from 'react-dom/client';
+
+import { ContainerTitle } from './ContainerTitle';
+import { ContainerType, ContainerNodeAttrs } from './types';
+
+export * from './types';
+
+export const containerSchema = $nodeSchema('container', () => {
+ return {
+ content: 'block+',
+ group: 'block',
+ defining: true,
+ attrs: {
+ containerType: {
+ default: ContainerType.TIP,
+ validate: (value) => Object.values(ContainerType).includes(value as ContainerType),
+ },
+ desc: {
+ default: '',
+ validate: 'string',
+ },
+ },
+ parseDOM: [
+ {
+ tag: 'div',
+ getAttrs: (dom) => ({
+ containerType: dom.getAttribute('data-container-type') ?? ContainerType.TIP,
+ desc: dom.getAttribute('data-description') ?? '',
+ }),
+ },
+ ],
+ parseMarkdown: {
+ match: (node) => {
+ return node.type === 'containerDirective' && Object.values(ContainerType).includes(node.name as ContainerType);
+ },
+ runner: (state, node, type) => {
+ state
+ .openNode(type, {
+ containerType: node.name,
+ desc: (node.attributes as ContainerNodeAttrs).desc,
+ })
+ .next(node.children)
+ .closeNode();
+ },
+ },
+ toMarkdown: {
+ match: (node) => node.type.name === 'container',
+ runner: (state, node) => {
+ const attributes: Record = {};
+ if (node.attrs.desc) {
+ attributes.desc = node.attrs.desc;
+ }
+ state
+ .openNode('containerDirective', undefined, {
+ name: node.attrs.containerType,
+ attributes,
+ })
+ .next(node.content)
+ .closeNode();
+ },
+ },
+ };
+});
+
+const containerView = $view(containerSchema.node, () => {
+ return (initialNode, view, getPos) => {
+ const { containerType, desc } = initialNode.attrs;
+ const isDetails = containerType === ContainerType.DETAILS;
+
+ const contentDom = document.createElement('p');
+ contentDom.classList.add('milkdown-container-block-content');
+ if (isDetails) {
+ contentDom.classList.add('container-content-hidden');
+ }
+
+ const dom = document.createElement('div');
+ dom.classList.add('milkdown-container-block');
+ dom.setAttribute('data-container-type', containerType);
+ dom.setAttribute('data-description', desc);
+
+ const titleContainerDom = document.createElement('div');
+ dom.appendChild(titleContainerDom);
+ dom.appendChild(contentDom);
+
+ const titleRoot = createRoot(titleContainerDom);
+
+ const setAttrs = (attrs: { containerType: ContainerType; desc: string }) => {
+ const pos = getPos();
+ if (pos == null) return;
+ view.dispatch(view.state.tr.setNodeAttribute(pos, 'containerType', attrs.containerType.toLowerCase()));
+ const inValidDesc = Object.keys(ContainerType).includes(attrs.desc.trim().toUpperCase());
+ view.dispatch(view.state.tr.setNodeAttribute(pos, 'desc', inValidDesc ? '' : attrs.desc));
+ };
+
+ titleRoot.render(
+ createElement(ContainerTitle, {
+ desc,
+ containerType,
+ contentDom,
+ getReadonly: () => !view.editable,
+ setAttrs,
+ }),
+ );
+
+ return {
+ dom,
+ contentDOM: contentDom,
+ update: (updatedNode) => {
+ if (
+ updatedNode.type !== initialNode.type ||
+ updatedNode.attrs.desc !== desc ||
+ updatedNode.attrs.containerType !== containerType
+ )
+ return false;
+
+ titleRoot.render(
+ createElement(ContainerTitle, {
+ desc: updatedNode.attrs.desc,
+ containerType: updatedNode.attrs.containerType,
+ contentDom,
+ getReadonly: () => !view.editable,
+ setAttrs,
+ }),
+ );
+ return true;
+ },
+ destroy() {
+ titleRoot.unmount();
+ },
+ ignoreMutation(mutation) {
+ if (mutation.type === 'selection' && mutation.target.nodeName === 'SPAN') return false;
+ if (mutation.type === 'selection' && (mutation.target as HTMLElement).classList.contains('title-container'))
+ return false;
+ return true;
+ },
+ };
+ };
+});
+
+const containerInputRule = $inputRule((ctx) => {
+ return wrappingInputRule(
+ /^:::(tip|warning|info|danger|details)\s+([\S\s]+)\s?:::$/,
+ containerSchema.type(ctx),
+ (match) => ({
+ containerType: match[1] || ContainerType.TIP,
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
+ desc: match[2] || '',
+ }),
+ );
+});
+
+export const wrapInContainerCommand = $command('WrapInContainer', (ctx) => () => wrapIn(containerSchema.type(ctx)));
+
+export const containerPlugin: MilkdownPlugin[] = [containerSchema, containerView, containerInputRule].flat();
diff --git a/client/src/components/Editor/plugins/plugin-container/types.ts b/client/src/components/Editor/plugins/plugin-container/types.ts
new file mode 100644
index 0000000..05ad224
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-container/types.ts
@@ -0,0 +1,11 @@
+export interface ContainerNodeAttrs {
+ desc: string;
+}
+
+export enum ContainerType {
+ TIP = 'tip',
+ WARNING = 'warning',
+ INFO = 'info',
+ DANGER = 'danger',
+ DETAILS = 'details',
+}
diff --git a/client/src/components/Editor/plugins/plugin-heading/Anchor.tsx b/client/src/components/Editor/plugins/plugin-heading/Anchor.tsx
new file mode 100644
index 0000000..57369f2
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-heading/Anchor.tsx
@@ -0,0 +1,28 @@
+import { FC } from 'react';
+
+import { updateLocationHash } from '@/utils/utils';
+
+export interface AnchorProps {
+ id: string;
+ text: string;
+ toAnchor?: (id: string) => void;
+}
+
+export const Anchor: FC = ({ id, text, toAnchor }) => {
+ const onClickAnchor = (e: React.MouseEvent) => {
+ e.stopPropagation();
+
+ if (toAnchor) {
+ toAnchor(id);
+ return;
+ }
+
+ updateLocationHash(id);
+ };
+
+ return (
+
+ #
+
+ );
+};
diff --git a/client/src/components/Editor/plugins/plugin-heading/heading.scss b/client/src/components/Editor/plugins/plugin-heading/heading.scss
new file mode 100644
index 0000000..19ef326
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-heading/heading.scss
@@ -0,0 +1,70 @@
+@use '@/utils/utils.scss' as *;
+
+.milkdown .ProseMirror {
+ h1, h2, h3, h4, h5, h6 {
+ position: relative;
+ margin-bottom: 16px;
+ margin-top: 24px;
+ font-weight: 500;
+ line-height: 1.25;
+ padding: 0;
+ }
+
+ & >.milkdown-heading:nth-child(1) {
+ margin-top: 0;
+ }
+ // when selected, the first child is .prosemirror-xx
+ div[contenteditable] {
+ & + .milkdown-heading {
+ margin-top: 0;
+ }
+ }
+
+ h1.milkdown-heading {
+ font-size: 2em;
+ border-bottom: 1px solid $shadowColor;
+ padding-bottom: .3em;
+ }
+ h2.milkdown-heading {
+ font-size: 1.5em;
+ border-bottom: 1px solid $shadowColor;
+ padding-bottom: .3em;
+ }
+ h3.milkdown-heading {
+ font-size: 1.25em;
+ }
+ h4.milkdown-heading {
+ font-size: 1em;
+ }
+ h5.milkdown-heading {
+ font-size: 0.875em;
+ }
+ h6.milkdown-heading {
+ font-size: 0.75em;
+ }
+}
+
+.heading-anchor {
+ cursor: pointer;
+ user-select: none;
+ opacity: 0;
+ font-weight: 500;
+ text-decoration: none;
+ transition: color .25s, opacity .25s;
+ position: absolute;
+ margin-left: -1.5rem;
+ top: 50%;
+ transform: translateY(-50%);
+ left: 0;
+ width: 20px;
+ height: 20px;
+ color: $anchorColor;
+ display: inline-flex;
+ @include flex-center();
+ z-index: 1000;
+ font-weight: normal;
+ font-size: 20px;
+ &:hover {
+ opacity: 1;
+ }
+}
diff --git a/client/src/components/Editor/plugins/plugin-heading/index.ts b/client/src/components/Editor/plugins/plugin-heading/index.ts
new file mode 100644
index 0000000..f40b84a
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-heading/index.ts
@@ -0,0 +1,87 @@
+import { MilkdownPlugin } from '@milkdown/kit/ctx';
+import { headingAttr, headingSchema } from '@milkdown/kit/preset/commonmark';
+import { $ctx, $view } from '@milkdown/utils';
+import { createElement } from 'react';
+import { createRoot } from 'react-dom/client';
+
+import { Anchor } from './Anchor';
+
+import { headerToId } from '@/utils/utils';
+
+export interface HeadingConfig {
+ toAnchor?: (id: string) => void;
+}
+
+export const headingDefaultConfig: HeadingConfig = {};
+
+export const headingConfig = $ctx(headingDefaultConfig, 'headingConfig');
+
+export const headingView = $view(headingSchema.node, (ctx) => {
+ return (initialNode) => {
+ const dom = document.createElement(`h${initialNode.attrs.level as string}`);
+ const attrs = {
+ ...ctx.get(headingAttr.key)(initialNode),
+ id: initialNode.attrs.id || headerToId(initialNode.textContent),
+ };
+ Object.entries(attrs).forEach(([key, value]) => {
+ dom.setAttribute(key, value as string);
+ });
+ dom.classList.add('milkdown-heading');
+
+ const contentDom = document.createElement('span');
+ contentDom.textContent = initialNode.textContent;
+ dom.appendChild(contentDom);
+
+ const anchorContainer = document.createElement('div');
+ anchorContainer.classList.add('heading-anchor-container');
+ dom.appendChild(anchorContainer);
+ const root = createRoot(anchorContainer);
+
+ const { toAnchor } = ctx.get(headingConfig.key);
+
+ root.render(
+ createElement(Anchor, {
+ id: initialNode.attrs.id as string,
+ text: initialNode.textContent,
+ toAnchor,
+ }),
+ );
+
+ return {
+ dom,
+ contentDOM: contentDom,
+ update: (updatedNode) => {
+ if (updatedNode.type !== initialNode.type) return false;
+
+ dom.setAttribute('id', updatedNode.attrs.id);
+
+ root.render(
+ createElement(Anchor, {
+ id: updatedNode.attrs.id as string,
+ text: updatedNode.textContent,
+ toAnchor,
+ }),
+ );
+ return true;
+ },
+ destroy() {
+ root.unmount();
+ },
+ ignoreMutation(mutation) {
+ if (
+ mutation.type === 'childList' &&
+ (mutation.target as HTMLElement).classList.contains('heading-anchor-container')
+ )
+ return true;
+
+ // avoid typing blur
+ if (mutation.type === 'attributes' && (mutation.target as HTMLElement).classList.contains('heading-anchor'))
+ return true;
+
+ return false;
+ },
+ };
+ };
+});
+
+export const headingPlugin: MilkdownPlugin[] = [headingView, headingConfig].flat();
diff --git a/client/src/components/Editor/plugins/plugin-highlight/commnads.ts b/client/src/components/Editor/plugins/plugin-highlight/commnads.ts
new file mode 100644
index 0000000..83d6e48
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-highlight/commnads.ts
@@ -0,0 +1,49 @@
+import { Mark } from '@milkdown/kit/prose/model';
+import { $command } from '@milkdown/utils';
+
+import { highlightSchema } from './index';
+import { colorPickTooltipAPI } from './slices';
+
+export const toggleColorPickerCommand = $command('ToggleColorPicker', (ctx) => {
+ return () => (state) => {
+ const {
+ doc,
+ selection: { from, to },
+ } = state;
+ const markType = highlightSchema.type(ctx);
+ const hasColorPicker = doc.rangeHasMark(from, to, markType);
+ if (hasColorPicker) {
+ ctx.get(colorPickTooltipAPI.key).removeColorPicker(from, to);
+ return true;
+ }
+
+ let mark: Mark | null = null;
+ doc.nodesBetween(from, to, (node) => {
+ if (!markType.isInSet(node.marks)) return;
+ mark = node.marks.find((m) => m.type === markType) ?? null;
+ });
+
+ ctx.get(colorPickTooltipAPI.key).addColorPicker(mark, from, to);
+ return true;
+ };
+});
+
+export const showColorPickerCommand = $command('ToggleColorPicker', (ctx) => {
+ return () => (state) => {
+ const {
+ doc,
+ selection: { from, to },
+ } = state;
+
+ const markType = highlightSchema.type(ctx);
+ let mark: Mark | null = null;
+ doc.nodesBetween(from, to, (node) => {
+ if (!markType.isInSet(node.marks)) return;
+ mark = node.marks.find((m) => m.type === markType) ?? null;
+ });
+
+ ctx.get(colorPickTooltipAPI.key).addColorPicker(mark, from, to);
+
+ return true;
+ };
+});
diff --git a/client/src/components/Editor/plugins/plugin-highlight/configration.ts b/client/src/components/Editor/plugins/plugin-highlight/configration.ts
new file mode 100644
index 0000000..7aa054f
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-highlight/configration.ts
@@ -0,0 +1,26 @@
+import { colorPickTooltipAPI } from './slices';
+import { colorPickTooltip } from './tooltip';
+import { ColorPickerTooltip } from './view';
+
+import type { Ctx } from '@milkdown/kit/ctx';
+
+export function configureColorPicker(ctx: Ctx) {
+ let colorPickerTooltipView: ColorPickerTooltip | null = null;
+
+ ctx.update(colorPickTooltipAPI.key, (api) => ({
+ ...api,
+ addColorPicker: (mark, from, to) => {
+ colorPickerTooltipView?.show(mark, from, to);
+ },
+ removeColorPicker: (from, to) => {
+ colorPickerTooltipView?.remove(from, to);
+ },
+ }));
+
+ ctx.set(colorPickTooltip.key, {
+ view: (view) => {
+ colorPickerTooltipView = new ColorPickerTooltip(ctx, view);
+ return colorPickerTooltipView;
+ },
+ });
+}
diff --git a/client/src/components/Editor/plugins/plugin-highlight/index.ts b/client/src/components/Editor/plugins/plugin-highlight/index.ts
new file mode 100644
index 0000000..e7d847a
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-highlight/index.ts
@@ -0,0 +1,82 @@
+import { MilkdownPlugin } from '@milkdown/kit/ctx';
+import { markRule } from '@milkdown/kit/prose';
+import { $inputRule, $markSchema, $remark } from '@milkdown/kit/utils';
+import { Mark } from 'mdast';
+
+import { showColorPickerCommand, toggleColorPickerCommand } from './commnads';
+import { remarkMarkColor } from './remark-mark-color';
+import { colorPickTooltipAPI, colorOptionsConfig } from './slices';
+import { colorPickTooltip } from './tooltip';
+
+export const DEFAULT_COLOR = '#ffff00';
+
+export const highlightSchema = $markSchema('mark', () => {
+ return {
+ attrs: {
+ color: {
+ default: DEFAULT_COLOR,
+ validate: 'string',
+ },
+ },
+ parseDOM: [
+ {
+ tag: 'mark',
+ getAttrs: (node: HTMLElement) => {
+ return {
+ color: node.style.backgroundColor,
+ };
+ },
+ },
+ ],
+ toDOM: (mark) => ['mark', { style: `background-color: ${mark.attrs.color as string}` }],
+ parseMarkdown: {
+ match: (node) => node.type === 'mark',
+ runner: (state, node, markType) => {
+ const color = (node as Mark).data?.color;
+ state.openMark(markType, { color });
+ state.next(node.children);
+ state.closeMark(markType);
+ },
+ },
+ toMarkdown: {
+ match: (node) => node.type.name === 'mark',
+ runner: (state, mark) => {
+ let color = mark.attrs.color;
+ if (color?.toLowerCase() === DEFAULT_COLOR.toLowerCase()) {
+ color = undefined;
+ }
+ state.withMark(mark, 'mark', undefined, {
+ data: { color },
+ });
+ },
+ },
+ };
+});
+
+export const highlightInputRule = $inputRule((ctx) => {
+ return markRule(/(?:==)(?:\{([^}]+)\})?([^=]+)(?:==)$/, highlightSchema.type(ctx), {
+ getAttr: (match) => {
+ const color = match[1];
+ return {
+ color: color || null,
+ };
+ },
+ });
+});
+
+const markRemark = $remark('highlightColor', () => remarkMarkColor);
+
+export const highlightMarkerPlugin: MilkdownPlugin[] = [
+ highlightSchema,
+ highlightInputRule,
+ markRemark,
+ colorPickTooltip,
+ colorPickTooltipAPI,
+ colorOptionsConfig,
+ toggleColorPickerCommand,
+ showColorPickerCommand,
+].flat();
+
+export * from './configration';
+export * from './slices';
+export * from './commnads';
diff --git a/client/src/components/Editor/plugins/plugin-highlight/remark-mark-color.ts b/client/src/components/Editor/plugins/plugin-highlight/remark-mark-color.ts
new file mode 100644
index 0000000..e6fa711
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-highlight/remark-mark-color.ts
@@ -0,0 +1,168 @@
+import { highlightMark } from 'micromark-extension-highlight-mark';
+import { visit } from 'unist-util-visit';
+
+import type { Parent, Root, RootContent } from 'mdast';
+import type { CompileContext, Extension as FromMarkdownExtension } from 'mdast-util-from-markdown';
+import type { Handle, Options as ToMarkdownExtension } from 'mdast-util-to-markdown';
+import type { Extension as MicromarkExtension, Token } from 'micromark-util-types';
+import type { Data, Processor } from 'unified';
+
+declare module 'mdast-util-to-markdown' {
+ interface ConstructNameMap {
+ mark: 'mark';
+ }
+}
+
+declare module 'mdast' {
+ export interface Mark extends Parent {
+ type: 'mark';
+ data: {
+ color?: string;
+ };
+ children: PhrasingContent[];
+ }
+
+ export interface StaticPhrasingContentMap {
+ mark: Mark;
+ }
+
+ interface PhrasingContentMap {
+ mark: Mark;
+ }
+
+ interface RootContentMap {
+ mark: Mark;
+ }
+}
+
+function add(
+ data: Data,
+ field: 'fromMarkdownExtensions' | 'micromarkExtensions' | 'toMarkdownExtensions',
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
+ value: FromMarkdownExtension | FromMarkdownExtension[] | MicromarkExtension | ToMarkdownExtension,
+) {
+ // @ts-expect-error error
+ const list = (data[field] = data[field] ?? []);
+ if (Array.isArray(value)) {
+ for (const v of value) {
+ if (!list.includes(v)) list.push(v);
+ }
+ } else {
+ if (!list.includes(value)) list.push(value);
+ }
+}
+
+/**
+ * Serialize `mark` node back to markdown.
+ * Courtesy of mdast-util-highlight-mark implementation but extended for `{color}`.
+ */
+const handleMarkColor: Handle = (node, _, state, info) => {
+ const marker = '==';
+ const tracker = state.createTracker(info);
+ const exit = state.enter('mark');
+ let value = tracker.move(marker);
+ // prepend color if any
+ if (node.data?.color) {
+ value += tracker.move(`{${node.data.color as string}}`);
+ }
+ value += tracker.move(state.containerPhrasing(node, { before: value, after: marker, ...tracker.current() }));
+ value += tracker.move(marker);
+ exit();
+ return value;
+};
+
+const markColorToMarkdown: ToMarkdownExtension = {
+ unsafe: [
+ // Copy from highlight-mark's unsafe definition so `==` inside phrasing escapes correctly
+ {
+ character: '=',
+ inConstruct: 'phrasing',
+ notInConstruct: [
+ 'autolink',
+ 'destinationLiteral',
+ 'destinationRaw',
+ 'reference',
+ 'titleQuote',
+ 'titleApostrophe',
+ ],
+ },
+ ],
+ handlers: {
+ mark: handleMarkColor,
+ },
+};
+
+function enterMark(this: CompileContext, token: Token) {
+ // `this` is CompileContext
+ this.enter({ type: 'mark', children: [], data: {} }, token);
+}
+
+function exitMark(this: CompileContext, token: Token) {
+ // node is currently on top of the stack
+ const node = this.stack[this.stack.length - 1] as Parent;
+ if (node?.children?.length) {
+ const first = node.children[0];
+ if (first?.type === 'text') {
+ const match = /^\{([^}]+)\}/.exec(first.value);
+ if (match) {
+ node.data = { ...(node.data ?? {}), color: match[1] };
+ first.value = first.value.slice(match[0].length);
+ if (first.value.length === 0) node.children.shift();
+ }
+ }
+ }
+ this.exit(token);
+}
+
+// ---------- FromMarkdown (token -> mdast) ----------
+const markColorFromMarkdown: FromMarkdownExtension = {
+ canContainEols: ['mark'],
+ enter: { highlight: enterMark },
+ exit: { highlight: exitMark },
+};
+
+/**
+ * remark plugin to support ==highlighted text== syntax, optionally with a color: =={#ff0}text==
+ *
+ * It recognises highlights in the markdown and converts them to `mark` nodes in mdast.
+ * A `mark` node looks like:
+ * {
+ * type: 'mark',
+ * data: { color?: string },
+ * children: [...]
+ * }
+ *
+ * The plugin also teaches `remark-stringify` how to turn those nodes back into markdown.
+ */
+export function remarkMarkColor(this: Processor) {
+ const data = this.data();
+ // Register micromark + mdast extensions at parser stage
+ add(data, 'micromarkExtensions', highlightMark());
+ add(data, 'fromMarkdownExtensions', markColorFromMarkdown);
+ add(data, 'toMarkdownExtensions', markColorToMarkdown);
+
+ // Transformer: convert `highlight` -> `mark` & parse color
+ return (tree: Root) => {
+ visit(tree, 'highlight', (node: Parent & RootContent, index: number, parent: Parent) => {
+ if (!parent) return;
+ const first = node.children[0];
+ let color: string | undefined = undefined;
+ if (first?.type === 'text') {
+ const match = /^\{([^}]+)\}/.exec(first.value);
+ if (match) {
+ color = match[1];
+ first.value = first.value.slice(match[0].length);
+ if (first.value.length === 0) {
+ node.children.shift();
+ }
+ }
+ }
+ node.type = 'mark';
+ if (color) {
+ node.data = { ...node.data, color };
+ }
+ // update in parent
+ parent.children[index] = node;
+ });
+ };
+}
diff --git a/client/src/components/Editor/plugins/plugin-highlight/slices.ts b/client/src/components/Editor/plugins/plugin-highlight/slices.ts
new file mode 100644
index 0000000..407b0c2
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-highlight/slices.ts
@@ -0,0 +1,67 @@
+import { Mark } from '@milkdown/kit/prose/model';
+import { $ctx } from '@milkdown/utils';
+
+import { ColorOptions } from './view/ColorPicker';
+
+export interface ColorPickTooltipAPI {
+ addColorPicker: (mark: Mark | null, from: number, to: number) => void;
+ removeColorPicker: (from: number, to: number) => void;
+}
+
+const defaultAPI: ColorPickTooltipAPI = {
+ addColorPicker: () => {
+ //...
+ },
+ removeColorPicker: () => {
+ //...
+ },
+};
+
+export const colorPickTooltipAPI = $ctx({ ...defaultAPI }, 'colorPickTooltipAPICtx');
+
+export const defaultColorOptions: ColorOptions[] = [
+ {
+ label: 'Background',
+ code: 'BG',
+ items: [
+ {
+ label: 'Default',
+ value: 'default',
+ },
+ {
+ label: 'Red',
+ value: '#fed5d5',
+ },
+ {
+ label: 'Orange',
+ value: '#fedfbb',
+ },
+ {
+ label: 'Yellow',
+ value: '#fef3a1',
+ },
+ {
+ label: 'Green',
+ value: '#e1fab1',
+ },
+ {
+ label: 'Teal',
+ value: '#adf8e9',
+ },
+ {
+ label: 'Blue',
+ value: '#cce2fe',
+ },
+ {
+ label: 'Purple',
+ value: '#edddff',
+ },
+ {
+ label: 'Grey',
+ value: '#eaecef',
+ },
+ ],
+ },
+];
+
+export const colorOptionsConfig = $ctx(defaultColorOptions, 'colorOptionsCtx');
diff --git a/client/src/components/Editor/plugins/plugin-highlight/tooltip.ts b/client/src/components/Editor/plugins/plugin-highlight/tooltip.ts
new file mode 100644
index 0000000..97856af
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-highlight/tooltip.ts
@@ -0,0 +1,3 @@
+import { tooltipFactory } from '@milkdown/kit/plugin/tooltip';
+
+export const colorPickTooltip = tooltipFactory('COLOR_PICK');
diff --git a/client/src/components/Editor/plugins/plugin-highlight/view/ColorPicker.scss b/client/src/components/Editor/plugins/plugin-highlight/view/ColorPicker.scss
new file mode 100644
index 0000000..b4c6186
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-highlight/view/ColorPicker.scss
@@ -0,0 +1,54 @@
+@use '@/utils/utils.scss' as *;
+
+.milkdown-color-picker {
+ position: absolute;
+ z-index: 10;
+ box-shadow: var(--crepe-shadow-1);
+ background-color: var(--crepe-color-surface);
+ border-radius: $borderRadius;
+ &[data-show='false'] {
+ display: none;
+ }
+
+ .color-picker-container {
+ width: 150px;
+ padding: 10px;
+ .group-title {
+ font-size: 12px;
+ color: $shallowTextColor;
+ text-align: start;
+ font-weight: normal;
+ margin-bottom: 0.5rem
+ }
+ .color-item {
+ @include flex-center();
+ justify-content: flex-start;
+ padding: 5px;
+ border-radius: $borderRadius;
+ .demo {
+ border: 1px solid $shadowColor;
+ border-radius: $borderRadius;
+ padding: 3px;
+ width: 16px;
+ height: 16px;
+ font-size: 12px;
+ font-weight: normal;
+ margin-right: 1rem;
+ @include flex-center();
+ }
+ .desc {
+ font-size: 12px;
+ font-weight: normal;
+ }
+ }
+ }
+
+ .color-picker-listbox {
+ border: none;
+ .p-listbox-list-wrapper,
+ .p-listbox-list,
+ .p-listbox-item-group {
+ background-color: var(--crepe-color-surface);
+ }
+ }
+}
diff --git a/client/src/components/Editor/plugins/plugin-highlight/view/ColorPicker.tsx b/client/src/components/Editor/plugins/plugin-highlight/view/ColorPicker.tsx
new file mode 100644
index 0000000..1d59ca3
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-highlight/view/ColorPicker.tsx
@@ -0,0 +1,57 @@
+import { ListBox, ListBoxChangeEvent } from 'primereact/listbox';
+import { FC, useEffect, useState } from 'react';
+
+export interface ColorOptions {
+ label: string;
+ value?: string;
+ code?: string;
+ items?: ColorOptions[];
+}
+
+export interface ColorPickerProps {
+ onColorChange: (color: string) => void;
+ initialColor: string;
+ colorOptions: ColorOptions[];
+}
+
+export const ColorPicker: FC = ({ onColorChange, initialColor, colorOptions }) => {
+ const [selectedColor, setSelectedColor] = useState(initialColor);
+
+ useEffect(() => {
+ setSelectedColor(initialColor);
+ }, [initialColor]);
+
+ const groupTemplate = (option: { label: string }) => {
+ return {option.label}
;
+ };
+
+ const itemTemplate = (option: { label: string; value: string }) => {
+ return (
+
+
+ A
+
+
{option.label}
+
+ );
+ };
+
+ return (
+
+ {
+ setSelectedColor(e.value);
+ onColorChange(e.value);
+ }}
+ />
+
+ );
+};
diff --git a/client/src/components/Editor/plugins/plugin-highlight/view/index.ts b/client/src/components/Editor/plugins/plugin-highlight/view/index.ts
new file mode 100644
index 0000000..37f4594
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-highlight/view/index.ts
@@ -0,0 +1,147 @@
+import { editorViewCtx } from '@milkdown/kit/core';
+import { TooltipProvider } from '@milkdown/kit/plugin/tooltip';
+import { posToDOMRect } from '@milkdown/kit/prose';
+import { TextSelection } from '@milkdown/kit/prose/state';
+import { createElement } from 'react';
+import { createRoot, type Root } from 'react-dom/client';
+
+import { ColorPicker } from './ColorPicker';
+import { colorOptionsConfig, highlightSchema } from '..';
+
+import type { Ctx } from '@milkdown/kit/ctx';
+import type { Mark } from '@milkdown/kit/prose/model';
+import type { PluginView } from '@milkdown/kit/prose/state';
+import type { EditorView } from '@milkdown/kit/prose/view';
+
+import './ColorPicker.scss';
+
+interface Data {
+ from: number;
+ to: number;
+ mark: Mark | null;
+}
+
+const defaultData: Data = {
+ from: -1,
+ to: -1,
+ mark: null,
+};
+
+export class ColorPickerTooltip implements PluginView {
+ #content: HTMLElement;
+
+ #provider: TooltipProvider;
+
+ #data: Data = { ...defaultData };
+
+ #root: Root;
+
+ // #color: string;
+
+ constructor(private readonly ctx: Ctx, view: EditorView) {
+ const content = document.createElement('div');
+ content.className = 'milkdown-color-picker';
+
+ this.#content = content;
+
+ this.#root = createRoot(content);
+ this.#root.render(
+ createElement(ColorPicker, {
+ onColorChange: this.#onColorChange,
+ initialColor: '',
+ colorOptions: [],
+ }),
+ );
+
+ this.#provider = new TooltipProvider({
+ content,
+ floatingUIOptions: {
+ placement: 'bottom',
+ },
+ debounce: 0,
+ shouldShow: () => false,
+ });
+ this.#provider.onHide = () => {
+ requestAnimationFrame(() => {
+ view.dom.focus({ preventScroll: true });
+ });
+ };
+ this.#provider.update(view);
+ }
+
+ public update = (view: EditorView) => {
+ const { state } = view;
+ const { selection } = state;
+ if (!(selection instanceof TextSelection)) return;
+ const { from, to } = selection;
+ if (from === this.#data.from && to === this.#data.to) return;
+
+ this.#reset();
+ };
+
+ public destroy = () => {
+ this.#provider.destroy();
+ this.#content.remove();
+ };
+
+ public show = (mark: Mark | null, from: number, to: number) => {
+ this.#data = {
+ from,
+ to,
+ mark,
+ };
+ // this.#color = mark.attrs.color;
+ const view = this.ctx.get(editorViewCtx);
+ view.dispatch(view.state.tr.setSelection(TextSelection.create(view.state.doc, from, to)));
+ this.#root.render(
+ createElement(ColorPicker, {
+ onColorChange: this.#onColorChange,
+ initialColor: mark?.attrs.color ?? '',
+ colorOptions: this.ctx.get(colorOptionsConfig.key),
+ }),
+ );
+ this.#provider.show({
+ getBoundingClientRect: () => posToDOMRect(view, from, to),
+ });
+ };
+
+ public remove = (from: number, to: number) => {
+ const view = this.ctx.get(editorViewCtx);
+
+ const tr = view.state.tr;
+ tr.removeMark(from, to, highlightSchema.type(this.ctx));
+ view.dispatch(tr);
+
+ this.#reset();
+ };
+
+ #reset = () => {
+ this.#provider.hide();
+ this.#data = { ...defaultData };
+ };
+
+ #onColorChange = (color: string) => {
+ const { from, to, mark } = this.#data;
+
+ if (color === 'default') {
+ this.#reset();
+ this.remove(from, to);
+ return;
+ }
+
+ const view = this.ctx.get(editorViewCtx);
+ const type = highlightSchema.type(this.ctx);
+ if (mark?.attrs?.color === color) {
+ this.#reset();
+ return;
+ }
+
+ const tr = view.state.tr;
+ if (mark) tr.removeMark(from, to, mark);
+
+ tr.addMark(from, to, type.create({ color }));
+ view.dispatch(tr);
+
+ this.#reset();
+ };
+}
diff --git a/client/src/components/Editor/plugins/plugin-iframe/IframeView.tsx b/client/src/components/Editor/plugins/plugin-iframe/IframeView.tsx
new file mode 100644
index 0000000..2ac4f6f
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-iframe/IframeView.tsx
@@ -0,0 +1,67 @@
+import { ProgressSpinner } from 'primereact/progressspinner';
+import { FC, useEffect, useId, useRef, useState } from 'react';
+
+import { TooltipInput } from '@/components/Editor/components/TooltipInput';
+
+interface IframeViewProps {
+ src: string;
+ readonly?: boolean;
+ setAttrs?: (attrs: { src: string }) => void;
+}
+
+export const IframeView: FC = ({ src, setAttrs, readonly = true }) => {
+ const uid = useId();
+ const [inputSrc, setInputSrc] = useState(src);
+ const [loading, setLoading] = useState(true);
+
+ const linkRef = useRef(null);
+
+ useEffect(() => {
+ setLoading(true);
+ }, [src]);
+
+ const showEdit = !(loading || readonly);
+
+ return (
+
+ {showEdit && (
+
{
+ setInputSrc(value);
+ }}
+ onConfirm={() => {
+ setAttrs?.({ src: inputSrc });
+ }}
+ />
+ )}
+ src && window.open(src, '_blank')}
+ >
+
+
+ {loading && (
+
+ )}
+
+ );
+};
diff --git a/client/src/components/Editor/plugins/plugin-iframe/index.scss b/client/src/components/Editor/plugins/plugin-iframe/index.scss
new file mode 100644
index 0000000..d32f41f
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-iframe/index.scss
@@ -0,0 +1,70 @@
+@use '@/utils/utils.scss' as *;
+@use 'sass:color';
+@use '@/components/Editor/components/TooltipInput';
+
+.milkdown .iframe-plugin-container {
+ border-radius: $borderRadius;
+ margin: 16px 0;
+ display: flex;
+ flex-direction: column;
+ align-items: start;
+ justify-content: center;
+ &.ProseMirror-selectednode {
+ background-color: $backgroundColor;
+ }
+ .iframe-loader {
+ @include flex-center();
+ width: 100%;
+ height: 100%;
+ }
+ .iframe-plugin {
+ border-radius: $borderRadius;
+ width: fit-content;
+ width: 100%;
+ aspect-ratio: var(--aspect-video, 16 / 9);
+ outline: none;
+ border: none;
+ &.loading {
+ display: none;
+ }
+ }
+ .iframe-plugin-link {
+ cursor: pointer;
+ color: $contentTextColor;
+ font-weight: light;
+ height: 32px;
+ padding: 4px 8px;
+ border-top-left-radius: .25rem;
+ border-top-right-radius: .25rem;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 4px;
+ font-size: 16px;
+ text-decoration: none;
+ background-color: $buttonBgcColor;
+ &:hover {
+ background-color: $buttonHoverBgcColor;
+ }
+ span {
+ font-size: 14px;
+ }
+ }
+}
+
+.iframe-link-tooltip {
+ .p-tooltip-arrow {
+ display: none;
+ }
+ .p-tooltip-text {
+ background-color: $backgroundColor;
+ padding: 10px;
+ @include flex-center();
+ input {
+ height: 24px;
+ margin-right: 1rem;
+ background-color: $backgroundColor;
+ color: $contentTextColor;
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/components/Editor/plugins/plugin-iframe/index.ts b/client/src/components/Editor/plugins/plugin-iframe/index.ts
new file mode 100644
index 0000000..4e760ed
--- /dev/null
+++ b/client/src/components/Editor/plugins/plugin-iframe/index.ts
@@ -0,0 +1,90 @@
+import { MilkdownPlugin } from '@milkdown/kit/ctx';
+import { $remark, $nodeSchema, $inputRule, $view } from '@milkdown/kit/utils';
+import { InputRule } from 'prosemirror-inputrules';
+import { createElement } from 'react';
+import { createRoot } from 'react-dom/client';
+import directive from 'remark-directive';
+
+import { IframeView } from './IframeView';
+
+const remarkPluginId = 'Iframe';
+const remarkDirective = $remark(remarkPluginId, () => directive);
+
+export const iframeBlockSchema = $nodeSchema('iframe', () => ({
+ group: 'block', // Block-level node
+ atom: true, // Cannot be split
+ isolating: true, // Cannot be merged with adjacent nodes
+ marks: '', // No marks allowed
+ attrs: {
+ src: { default: 'https://example.com' }, // URL attribute
+ },
+ defining: true,
+ code: true,
+ parseDOM: [
+ {
+ tag: 'iframe',
+ getAttrs: (dom) => ({
+ src: dom.getAttribute('src'),
+ }),
+ },
+ ],
+ parseMarkdown: {
+ match: (node) => node.type === 'leafDirective' && node.name === 'iframe',
+ runner: (state, node, type) => {
+ state.addNode(type, { src: (node.attributes as { src: string }).src });
+ },
+ },
+ toMarkdown: {
+ match: (node) => node.type.name === 'iframe',
+ runner: (state, node) => {
+ state.addNode('leafDirective', undefined, undefined, {
+ name: 'iframe',
+ attributes: { src: node.attrs.src },
+ });
+ },
+ },
+}));
+
+const iframeView = $view(iframeBlockSchema.node, () => {
+ return (initialNode, view, getPos) => {
+ const dom = document.createElement('div');
+ dom.style.borderRadius = '5px';
+ const root = createRoot(dom);
+
+ const setAttrs = ({ src }: { src: string }) => {
+ if (!view.editable) return;
+ const pos = getPos();
+ if (pos == null) return;
+ view.dispatch(view.state.tr.setNodeAttribute(pos, 'src', src));
+ };
+
+ root.render(createElement(IframeView, { readonly: !view.editable, src: initialNode.attrs.src, setAttrs }));
+
+ return {
+ dom,
+ update: (updatedNode) => {
+ if (updatedNode.type !== initialNode.type) return false;
+
+ root.render(createElement(IframeView, { readonly: !view.editable, src: updatedNode.attrs.src, setAttrs }));
+ return true;
+ },
+ destroy() {
+ root.unmount();
+ },
+ };
+ };
+});
+
+const iframeInputRule = $inputRule(
+ (ctx) =>
+ new InputRule(/:iframe\{src="(?[^"]+)?"?\}/, (state, match, start, end) => {
+ const [okay, src = ''] = match;
+ const { tr } = state;
+ if (okay) {
+ tr.replaceWith(start - 1, end, iframeBlockSchema.type(ctx).create({ src }));
+ }
+ return tr;
+ }),
+);
+
+export const iframePlugin: MilkdownPlugin[] = [remarkDirective, iframeBlockSchema, iframeView, iframeInputRule].flat();
diff --git a/client/src/components/Editor/type.d.ts b/client/src/components/Editor/type.d.ts
index 9f30602..be75ec6 100644
--- a/client/src/components/Editor/type.d.ts
+++ b/client/src/components/Editor/type.d.ts
@@ -11,3 +11,7 @@ export type ContentCacheType = Record<
editedContent: string;
}
>;
+
+export interface EditorRef {
+ update: (markdown: string) => void;
+}
diff --git a/client/src/components/EditorContainer/EditorContainer.less b/client/src/components/EditorContainer/EditorContainer.less
deleted file mode 100644
index e30b0de..0000000
--- a/client/src/components/EditorContainer/EditorContainer.less
+++ /dev/null
@@ -1,34 +0,0 @@
-@import url('../../utils/utils.less');
-
-.editor-container {
- transition: @transition;
- flex: 1;
- height: 100vh;
- max-width: 100vw;
- overflow: hidden;
- padding-left: 0.5rem;
- display: grid;
- grid-template-rows: 6% 4% 90%;
- grid-template-columns: 96% 4%;
- .pure-page {
- width: 100%;
- height: 40rem;
- display: flex;
- justify-content: center;
- align-items: center;
- font-weight: bold;
- font-size: 3rem;
- }
- .header-container {
- grid-area: 1/1/2/3;
- }
- .doc-area {
- grid-area: 3/1/4/2;
- }
- .open-tab-container {
- grid-area: 2/1/3/2;
- }
- .side-panel {
- grid-area: 2/2/4/3;
- }
-}
diff --git a/client/src/components/EditorContainer/EditorContainer.scss b/client/src/components/EditorContainer/EditorContainer.scss
new file mode 100644
index 0000000..d1e2ee6
--- /dev/null
+++ b/client/src/components/EditorContainer/EditorContainer.scss
@@ -0,0 +1,32 @@
+@use '../../utils/utils.scss' as *;
+
+.editor-container {
+ transition: $transition;
+ flex: 1;
+ height: calc(100vh - $footerHeight);
+ width: 100%;
+ overflow: hidden;
+ z-index: 1000;
+ display: flex;
+ flex-direction: column;
+ .pure-page {
+ width: 100%;
+ height: 40rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ font-weight: bold;
+ font-size: 3rem;
+ }
+ .header-container {
+ height: $headerHeight;
+ }
+ .doc-area {
+ background-color: $backgroundColor;
+ height: $editorHeight;
+ width: 100%;
+ }
+ .open-tab-container {
+ height: $tabHeight;
+ }
+}
diff --git a/client/src/components/EditorContainer/EditorContainer.tsx b/client/src/components/EditorContainer/EditorContainer.tsx
index 80b28e3..fc713a9 100644
--- a/client/src/components/EditorContainer/EditorContainer.tsx
+++ b/client/src/components/EditorContainer/EditorContainer.tsx
@@ -1,80 +1,114 @@
+/* eslint-disable @typescript-eslint/no-magic-numbers */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-import React, { useState, useRef } from 'react';
+import { MilkdownProvider } from '@milkdown/react';
+import Split from '@uiw/react-split';
+import { useMemo, useRef, FC } from 'react';
import { useSelector } from 'react-redux';
-import { Switch, Route, Redirect } from 'react-router-dom';
+import { Navigate, Route, Routes } from 'react-router-dom';
-import ResizableBox from '../../utils/ResizableBox/ResizableBox';
-import DocMirror from '../DocMirror/DocMirror';
-import MarkdownEditor from '../Editor/Editor';
+import { DocMirror } from '../DocMirror/DocMirror';
+import { DraftEditor } from '../Editor/DraftEditor';
+import { MarkdownEditor } from '../Editor/Editor';
+import { EditorRef } from '../Editor/type';
import Header from '../Header/Header';
import OpenTab from '../OpenTab/OpenTab';
-import SidePanel from '../SidePanel/SidePanel';
+import { OutlineContainer } from '../Outline/OutlineContainer';
+import { SplitBar } from '../SplitBar';
-import { selectCurActiveTab } from '@/redux-feature/curDocSlice';
+import { GITHUB_PAGES_BASE_PATH } from '@/constants';
+import { selectCurActiveTab, selectCurContent } from '@/redux-feature/curDocSlice';
import { selectGlobalOpts } from '@/redux-feature/globalOptsSlice';
-import { smoothCollapse } from '@/utils/utils';
+import { useShortCut } from '@/utils/hooks/tools';
-import './EditorContainer.less';
-
-export interface EditorWrappedRef {
- update: (newContent: string) => void;
-}
+import './EditorContainer.scss';
export const PurePage = () => {
return Just pick one!
;
};
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function EditorContainer() {
- const curTab = useSelector(selectCurActiveTab);
+const isDocPage = (path: string) => {
+ return (
+ path.startsWith(`${GITHUB_PAGES_BASE_PATH}article/`) ||
+ path.startsWith(`${GITHUB_PAGES_BASE_PATH}draft/`) ||
+ path.startsWith(`${GITHUB_PAGES_BASE_PATH}internal/`)
+ );
+};
- const editorRef = useRef(null);
+export const EditorContainer: FC = () => {
+ useShortCut();
- const { mirrorCollapse } = useSelector(selectGlobalOpts);
+ const isDocPageFlag = isDocPage(location.pathname);
- // just for hidden and show UI experience
- const [unmountMirror, setUnmountMirror] = useState(true);
- const [hideResizeBar, setHideResizeBar] = useState(false);
+ const editorRef = useRef(null);
- const editorEffect = smoothCollapse(mirrorCollapse);
- const mirrorEffect = smoothCollapse(
- mirrorCollapse,
- // wait for the collapsing finishing then unmount the mirror and hide the bar
- () => {
- setUnmountMirror(true);
- setHideResizeBar(true);
- },
- // when to open the box, open the mirror and show the bar immediately
- () => {
- setUnmountMirror(false);
- setHideResizeBar(false);
- },
- );
+ const curTab = useSelector(selectCurActiveTab);
+ const { mirrorCollapse, isEditorBlur, outlineCollapse } = useSelector(selectGlobalOpts);
+ const globalContent = useSelector(selectCurContent);
+
+ const defaultPagePath = useMemo(() => {
+ return curTab ? `/article/${curTab.ident}` : '/purePage';
+ }, [curTab]);
+
+ const handleDocMirrorChange = (value: string) => {
+ if (isEditorBlur && editorRef.current && value !== globalContent) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ editorRef.current.update(value);
+ }
+ };
return (
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+ } />
+ } />
+
+
+ {!mirrorCollapse && (
+
+
+
+ )}
+ {!outlineCollapse && isDocPageFlag && (
+
+
+
+ )}
+
-
);
-}
+};
diff --git a/client/src/components/FolderSelector/FolderSelector.scss b/client/src/components/FolderSelector/FolderSelector.scss
new file mode 100644
index 0000000..f882e55
--- /dev/null
+++ b/client/src/components/FolderSelector/FolderSelector.scss
@@ -0,0 +1,64 @@
+@use '@/utils/utils.scss' as *;
+
+.folder-selector {
+ width: fit-content;
+ height: 600px;
+ .selected-folder-display {
+ margin-bottom: 10px;
+ @include flex-center();
+ justify-content: space-between;
+ }
+ .current-folders-display {
+ width: 600px;
+ height: 500px;
+ overflow: auto;
+ .folder-item {
+ @include flex-center();
+ justify-content: space-between;
+ padding: 12px 20px;
+ &-arrow {
+ &:hover {
+ background-color: $hoverBgcColor;
+ border-radius: $borderRadius;
+ }
+ }
+ }
+ .p-listbox {
+ border: none;
+ .p-listbox-item {
+ padding: 0;
+ }
+ }
+ &-empty {
+ @include flex-center();
+ justify-content: center;
+ flex-direction: column;
+ height: 100%;
+ width: 100%;
+ color: $shallowTextColor;
+ font-size: 14px;
+ i {
+ font-size: 30px;
+ margin-bottom: 10px;
+ }
+ }
+ }
+ .p-breadcrumb {
+ padding: 5px;
+ width: 600px;
+ .p-breadcrumb-list {
+ width: 100%;
+ }
+ }
+ .breadcrumb-item {
+ cursor: pointer;
+ padding: 5px;
+ &:hover {
+ background-color: $hoverBgcColor;
+ border-radius: $borderRadius;
+ }
+ &-home {
+ padding: 5px;
+ }
+ }
+}
diff --git a/client/src/components/FolderSelector/FolderSelector.tsx b/client/src/components/FolderSelector/FolderSelector.tsx
new file mode 100644
index 0000000..5473c64
--- /dev/null
+++ b/client/src/components/FolderSelector/FolderSelector.tsx
@@ -0,0 +1,268 @@
+import { BreadCrumb } from 'primereact/breadcrumb';
+import { Button } from 'primereact/button';
+import { Dialog } from 'primereact/dialog';
+import { ListBox } from 'primereact/listbox';
+import { MenuItem } from 'primereact/menuitem';
+import { FC, useEffect, useMemo, useState } from 'react';
+
+import { NewFolder } from './NewFolder';
+
+import { useLazyGetDocSubItemsQuery } from '@/redux-api/docs';
+
+import './FolderSelector.scss';
+
+interface FolderSelectorProps {
+ /** path string: path/to/folder */
+ onSelectFolder?: (folderPath: string) => void;
+ initialPath?: string;
+}
+
+interface FolderItem {
+ path: string[];
+}
+
+const getMenuItemsFromPath = (path: string[]) => {
+ const homeDirPrefix = path[0];
+ const slicePath = path.slice(1);
+ return slicePath.map((p, i) => {
+ return {
+ label: p,
+ data: { path: [homeDirPrefix, ...slicePath.slice(0, i + 1)] },
+ };
+ });
+};
+
+/**
+ * - Mac: "/f1/f2" <-> ["/", "f1", "f2"]
+ * - Windows: "D:/f1/f2" <-> ["", "D:", "f1", "f2"]
+ * */
+function normalizeFolderPath(path: P): R {
+ if (Array.isArray(path)) {
+ const trimmedPath = path.filter((p) => p); // remove empty string for windows root dir
+ // Macos home_dir will be '/'
+ return trimmedPath.join('/').replaceAll('//', '/') as R;
+ }
+
+ let trimmedPath: string = path;
+ if (path.endsWith('/')) {
+ trimmedPath = path.slice(0, -1);
+ }
+
+ // Macos
+ if (trimmedPath.startsWith('/')) {
+ return ['/', ...trimmedPath.split('/').slice(1)] as R;
+ }
+
+ // windows
+ return ['', ...trimmedPath.split('/').slice(1)] as R;
+}
+
+export const FolderSelector: FC = ({ onSelectFolder, initialPath = '' }) => {
+ const [fetchSubItems] = useLazyGetDocSubItemsQuery();
+
+ const [breadcrumbItems, setBreadcrumbItems] = useState([]);
+ const [currentFolders, setCurrentFolders] = useState([]);
+ const [currentSelectedFolder, setCurrentSelectedFolder] = useState(null);
+
+ const selectedFolderPath = useMemo(() => {
+ const lastItemPath = (breadcrumbItems.at(-1)?.data.path ?? []) as string[];
+ if (!lastItemPath.length) return '/';
+ return normalizeFolderPath(lastItemPath);
+ }, [breadcrumbItems]);
+
+ const getSubFolders = async (parentPath = '') => {
+ const { data: subItems } = await fetchSubItems({ folderDocPath: parentPath, homeRootDir: true });
+ return (
+ subItems?.filter((item) => !item.isFile).map((item) => ({ label: item.name, data: { path: item.path } })) ?? []
+ );
+ };
+
+ const transformToBreadItem = (menuItem: MenuItem) => {
+ const { data, label } = menuItem;
+
+ const handleSelect = async () => {
+ const lastMenuPath = data.path.slice(0, -1);
+ const lastMenuLabel = lastMenuPath.slice(-1)[0];
+
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
+ await expandFolderItem({ label: lastMenuLabel, data: { path: lastMenuPath } }, false);
+
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
+ handleSelectFolderItem(menuItem);
+ };
+
+ return {
+ label,
+ data,
+ template: () => (
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
+ {
+ void handleSelect();
+ }}
+ >
+
{label}
+
+ ),
+ };
+ };
+
+ /** when clicking one folder item to select */
+ function handleSelectFolderItem(menuItem: MenuItem | null) {
+ if (!menuItem) return;
+
+ const { data } = menuItem;
+ const { path } = data as FolderItem;
+
+ setCurrentSelectedFolder(menuItem);
+
+ const newBreadcrumbItems = getMenuItemsFromPath(path).map(transformToBreadItem);
+ setBreadcrumbItems(newBreadcrumbItems);
+
+ onSelectFolder?.(normalizeFolderPath(path));
+ }
+
+ // when double click one folder item or click at the breadcrumb item
+ // get the sub folder items(expand)
+ async function expandFolderItem(item: MenuItem, setBreadcrumb = true) {
+ const { data } = item;
+ const { path } = data as FolderItem;
+
+ // remove home dir prefix
+ const subItems = await getSubFolders(path.slice(1).join('/'));
+ setCurrentFolders(subItems);
+
+ setCurrentSelectedFolder(null);
+
+ if (setBreadcrumb) {
+ const newBreadcrumbItems = getMenuItemsFromPath(path).map(transformToBreadItem);
+ setBreadcrumbItems(newBreadcrumbItems);
+ }
+ }
+
+ useEffect(() => {
+ const fn = async () => {
+ const path = normalizeFolderPath(initialPath);
+ const parentPath = path.slice(0, -1);
+ const subItems = await getSubFolders(parentPath.join('/'));
+ setCurrentFolders(subItems);
+
+ handleSelectFolderItem({ label: path.at(-1) ?? '', data: { path } });
+ };
+ void fn();
+ }, [initialPath]);
+
+ const folderItemTemplate = (item: MenuItem) => {
+ return (
+ {
+ handleSelectFolderItem(item);
+ }}
+ >
+
{item.label}
+
{
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
+ void expandFolderItem(item);
+ }}
+ >
+
+ );
+ };
+
+ const handleNewFolderCreated = async (folderPath: string) => {
+ const folderPathParts = normalizeFolderPath(folderPath);
+ const parentPathParts = folderPathParts.slice(0, -1);
+ const parentItem: MenuItem = {
+ label: parentPathParts.at(-1) ?? '',
+ data: { path: parentPathParts },
+ };
+ await expandFolderItem(parentItem, false);
+ handleSelectFolderItem({
+ label: folderPathParts.at(-1) ?? '',
+ data: { path: folderPathParts },
+ });
+ };
+
+ return (
+
+
+ 👇 Selected Folder:
+
+
+
(
+ {
+ void expandFolderItem({ label: '/', data: { path: ['/'] } });
+ }}
+ >
+
+
+ ),
+ }}
+ />
+
+ {currentFolders.length ? (
+
+ ) : (
+
+
+ Empty
+
+ )}
+
+
+ );
+};
+
+export interface FolderSelectorModalProps extends FolderSelectorProps {
+ visible: boolean;
+ onHide: () => void;
+}
+
+export const FolderSelectorModal: FC = (props) => {
+ const { onSelectFolder, visible, onHide } = props;
+ const [selectFolderPath, setSelectFolderPath] = useState('');
+
+ return (
+ ⚙️ Select Workspace
}
+ footer={
+
+ {
+ onHide();
+ setSelectFolderPath('');
+ }}
+ outlined
+ >
+ Cancel
+
+ {
+ onSelectFolder?.(selectFolderPath);
+ onHide();
+ setSelectFolderPath('');
+ }}
+ >
+ Confirm
+
+
+ }
+ visible={visible}
+ onHide={onHide}
+ >
+
+
+ );
+};
diff --git a/client/src/components/FolderSelector/NewFolder.scss b/client/src/components/FolderSelector/NewFolder.scss
new file mode 100644
index 0000000..719bb74
--- /dev/null
+++ b/client/src/components/FolderSelector/NewFolder.scss
@@ -0,0 +1,40 @@
+@use '@/utils/utils.scss' as *;
+
+.new-folder-container {
+ @include flex-center();
+ justify-content: space-between;
+ margin-right: 10px;
+ .new-folder-icon {
+ &.hidden {
+ display: none;
+ }
+ }
+ .input-container {
+ @include flex-center();
+ justify-content: space-between;
+ opacity: 0;
+ width: 0;
+ &.show {
+ width: 100%;
+ opacity: 1;
+ .input-confirm, .input-cancel {
+ opacity: 1;
+ }
+ }
+ }
+ .new-folder-input {
+ outline: none;
+ height: 25px;
+ width: 100%;
+ padding: 5px;
+ font-size: 12px;
+ margin-right: 0.5rem;
+ }
+ .input-confirm, .input-cancel {
+ cursor: pointer;
+ opacity: 0;
+ }
+ .input-cancel {
+ margin-left: 10px;
+ }
+}
diff --git a/client/src/components/FolderSelector/NewFolder.tsx b/client/src/components/FolderSelector/NewFolder.tsx
new file mode 100644
index 0000000..d194830
--- /dev/null
+++ b/client/src/components/FolderSelector/NewFolder.tsx
@@ -0,0 +1,91 @@
+import { InputText } from 'primereact/inputtext';
+import { FC, useState } from 'react';
+
+import { Icon } from '@/components/Icon/Icon';
+import { useCreateFolderMutation } from '@/redux-api/docs';
+import Toast from '@/utils/Toast';
+import { confirm } from '@/utils/utils';
+
+import './NewFolder.scss';
+
+export interface NewFolderProps {
+ onConfirm?: (folderPath: string) => Promise | void;
+ onCreated?: (folderPath: string) => Promise | void;
+ underFolder: string;
+}
+
+export const NewFolder: FC = ({ onConfirm, underFolder, onCreated }) => {
+ const [showInput, setShowInput] = useState(false);
+ const [inputValue, setInputValue] = useState('');
+
+ const [createFolder] = useCreateFolderMutation();
+
+ const handleConfirmClick = async () => {
+ if (
+ !(await confirm({
+ message: `Are you sure to create folder "${inputValue}" under "${underFolder}"?`,
+ }))
+ ) {
+ return;
+ }
+
+ try {
+ const createFolderPath = `${underFolder}${underFolder.endsWith('/') ? '' : '/'}${inputValue}`;
+ if (onConfirm) {
+ await onConfirm(createFolderPath);
+ return;
+ }
+
+ await createFolder({ folderPath: createFolderPath }).unwrap();
+
+ await onCreated?.(createFolderPath);
+ Toast('created successfully!');
+ setShowInput(false);
+ setInputValue('');
+ } catch (e) {
+ Toast.error((e as Error).message);
+ }
+ };
+
+ return (
+
+
+ {
+ setInputValue(e.target.value);
+ }}
+ placeholder="New folder name"
+ />
+ {
+ void handleConfirmClick();
+ }}
+ />
+ {
+ setShowInput(false);
+ setInputValue('');
+ }}
+ />
+
+
{
+ setShowInput(true);
+ }}
+ toolTipContent="New Folder"
+ />
+
+ );
+};
diff --git a/client/src/components/Footer/Footer.scss b/client/src/components/Footer/Footer.scss
new file mode 100644
index 0000000..a454e6a
--- /dev/null
+++ b/client/src/components/Footer/Footer.scss
@@ -0,0 +1,28 @@
+@use '@/utils/utils.scss' as *;
+
+.app-footer {
+ width: 100vw;
+ height: $footerHeight;
+ background-color: $backgroundColor;
+ border-top: 1px solid $shadowColor;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 10px;
+ .left-group {
+ .app-info-version-mismatch {
+ svg {
+ path {
+ color: rgb(239, 163, 21);
+ }
+ }
+ }
+ }
+ .right-group {
+ margin-right: 0.5rem;
+ @include flex-center();
+ .icon-wrapper {
+ margin-left: 0.5rem;
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/components/Footer/Footer.tsx b/client/src/components/Footer/Footer.tsx
new file mode 100644
index 0000000..e938635
--- /dev/null
+++ b/client/src/components/Footer/Footer.tsx
@@ -0,0 +1,89 @@
+import { HelpOutline } from '@mui/icons-material';
+import BallotIcon from '@mui/icons-material/BallotOutlined';
+import FullscreenExitIcon from '@mui/icons-material/FullscreenExitOutlined';
+import FullscreenIcon from '@mui/icons-material/FullscreenOutlined';
+import MirrorIcon from '@mui/icons-material/ImportContactsOutlined';
+import { FC } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+
+import { Icon } from '@/components/Icon/Icon';
+import {
+ selectMirrorCollapse,
+ selectNarrowMode,
+ selectOutlineCollapse,
+ selectServerStatus,
+ ServerStatus,
+ updateGlobalOpts,
+} from '@/redux-feature/globalOptsSlice';
+import { useSwitchNarrowMode } from '@/utils/hooks/reduxHooks';
+
+import './Footer.scss';
+
+export const Footer: FC = () => {
+ const dispatch = useDispatch();
+ const outlineCollapse = useSelector(selectOutlineCollapse);
+ const mirrorCollapse = useSelector(selectMirrorCollapse);
+ const narrowMode = useSelector(selectNarrowMode);
+ const serverStatus = useSelector(selectServerStatus);
+ const navigate = useNavigate();
+ const switchNarrowMode = useSwitchNarrowMode();
+
+ const clickOutline = () => {
+ dispatch(
+ updateGlobalOpts({
+ keys: ['outlineCollapse'],
+ values: [!outlineCollapse],
+ }),
+ );
+ };
+
+ return (
+
+
+ {
+ if (serverStatus === ServerStatus.VERSION_MISMATCHE) {
+ void navigate('/internal/version-mismatch');
+ } else if (serverStatus === ServerStatus.RUNNING) {
+ void navigate('/internal/guide');
+ }
+ }}
+ />
+
+
+
+ {
+ dispatch(
+ updateGlobalOpts({
+ keys: ['mirrorCollapse'],
+ values: [!mirrorCollapse],
+ }),
+ );
+ }}
+ />
+
+
+
+ );
+};
diff --git a/client/src/components/GitBox/GitBox.less b/client/src/components/GitBox/GitBox.scss
similarity index 80%
rename from client/src/components/GitBox/GitBox.less
rename to client/src/components/GitBox/GitBox.scss
index 434fc1a..e63245e 100644
--- a/client/src/components/GitBox/GitBox.less
+++ b/client/src/components/GitBox/GitBox.scss
@@ -1,15 +1,22 @@
-@import url("@/utils/utils.less");
+@use "@/utils/utils.scss" as *;
.git-box {
- border-radius: 5px;
+ .no-git-service {
+ width: 100%;
+ height: 100%;
+ @include flex-center();
+ i {
+ margin-right: 5px;
+ }
+ }
+ border-radius: $borderRadius;
width: 25rem;
- height: fit-content;
+ height: 400px;
padding: 0.5rem;
- background-color: @backgroundColor;
+ background-color: $backgroundColor;
display: flex;
flex-direction: column;
align-items: flex-start;
- box-shadow: @shadow;
cursor: default;
.git-btn {
min-width: 2.5rem;
@@ -34,17 +41,27 @@
margin-top: 1rem;
}
.op-box {
- width: fit-content;
+ width: 100%;
height: 2rem;
display: flex;
- justify-content: flex-start;
+ justify-content: flex-end;
align-items: center;
+ border-bottom: 1px solid $hoverBgcColor;
+ .icon-wrapper {
+ margin-left: 10px;
+ }
}
.space-box {
width: 100%;
.clean-space {
+ margin-top: 10px;
width: 100%;
- text-align: center;
+ @include flex-center();
+ flex-direction: column;
+ i {
+ font-size: 20px;
+ margin-bottom: 0.5rem;
+ }
}
.space-header {
width: 100%;
@@ -52,12 +69,11 @@
display: flex;
justify-content: space-between;
align-items: center;
- background-color: @boxColor;
+ background-color: $boxColor;
padding: 0.3rem 0.5rem;
border-radius: 5px;
font-weight: bold;
margin: 1rem 0 0.3rem 0;
- .hover-show-icon-box();
.op-icon-group {
display: flex;
justify-content: space-between;
@@ -66,9 +82,9 @@
margin: 0 0.1rem;
}
}
- div {
+ .item-title {
max-width: 80%;
- .text-overflow-omit();
+ @include text-overflow-omit();
}
}
.git-changes {
@@ -124,18 +140,9 @@
}
}
}
- .hover-show-icon-box {
- &:hover {
- .op-icon {
- display: block;
- }
- }
- }
.op-icon {
cursor: pointer;
position: relative;
- display: none;
- font-size: 20px;
&:hover {
&::after {
content: "";
@@ -145,7 +152,8 @@
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.2);
- border-radius: 5px;
+ z-index: 2;
+ border-radius: $borderRadius;
}
}
}
@@ -164,17 +172,8 @@
height: 3rem;
font-size: 16px;
border-radius: 5px;
- padding: 0 10px;
- outline: none;
- border: 0.5px solid #e7e0e0;
- position: relative;
- &:focus {
- border-color: #62b5ec;
- }
}
- textarea {
- min-width: 25rem;
- min-height: 5rem;
- font-family: inherit;
+ .p-inputtextarea {
+ min-height: 200px;
}
}
diff --git a/client/src/components/GitBox/GitBox.tsx b/client/src/components/GitBox/GitBox.tsx
index da445eb..e054818 100644
--- a/client/src/components/GitBox/GitBox.tsx
+++ b/client/src/components/GitBox/GitBox.tsx
@@ -2,12 +2,20 @@
/* eslint-disable @typescript-eslint/no-magic-numbers */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-import React, { useCallback, useState, useRef } from 'react';
-
-import Modal from '../../utils/Modal/Modal';
-import Spinner from '../../utils/Spinner/Spinner';
-
-import { useRefreshDocsMutation } from '@/redux-api/docsApi';
+import AddIcon from '@mui/icons-material/AddOutlined';
+import CheckOutlinedIcon from '@mui/icons-material/CheckOutlined';
+import CloudDownloadOutlinedIcon from '@mui/icons-material/CloudDownloadOutlined';
+import CloudUploadOutlinedIcon from '@mui/icons-material/CloudUploadOutlined';
+import FileOpenIcon from '@mui/icons-material/FileOpenOutlined';
+import RemoveIcon from '@mui/icons-material/RemoveOutlined';
+import UndoIcon from '@mui/icons-material/UndoOutlined';
+import { InputText } from 'primereact/inputtext';
+import { InputTextarea } from 'primereact/inputtextarea';
+import { ProgressSpinner } from 'primereact/progressspinner';
+import React, { useCallback, useState } from 'react';
+
+import { Icon } from '@/components/Icon/Icon';
+import { useGetDocSubItemsQuery } from '@/redux-api/docs';
import {
useGetGitStatusQuery,
useGitAddMutation,
@@ -15,41 +23,34 @@ import {
useGitCommitMutation,
useGitPullMutation,
useGitPushMutation,
- GitRestoreType,
Change,
-} from '@/redux-api/gitApi';
-import { useCurPath, useRestoreHandler } from '@/utils/hooks/docHooks';
+} from '@/redux-api/git';
+import { useCurPath, useRestoreEffects } from '@/utils/hooks/docHooks';
import { useSaveDoc } from '@/utils/hooks/reduxHooks';
import Toast from '@/utils/Toast';
+import { confirm, normalizePath } from '@/utils/utils';
-import './GitBox.less';
+import './GitBox.scss';
const defaultStatus = {
- workSpace: [],
+ workspace: [],
staged: [],
- err: 1,
changes: false,
noGit: true,
};
// eslint-disable-next-line @typescript-eslint/naming-convention
export default function GitBox() {
- const { routerHistory, curPath } = useCurPath();
+ const { navigate, curPath } = useCurPath();
- const { data: { changes, noGit, workSpace, staged, err } = defaultStatus } = useGetGitStatusQuery();
+ const { data: { noGit, workspace, staged } = defaultStatus, isLoading } = useGetGitStatusQuery();
const [commitMsgTitle, setCommitMsgTitle] = useState('');
const [commitMsgBody, setCommitMsgBody] = useState('');
const [opLoading, setOpLoading] = useState(false);
- // true when commit btn is clicked
- const [commitModalShow, setCommitModalShow] = useState(false);
- const [restoreConfirmShow, setRestoreConfirmShow] = useState(false);
- // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
- const restoreInfoRef = useRef(null);
-
- const restoreHandler = useRestoreHandler();
+ const restoreEffects = useRestoreEffects();
const [add] = useGitAddMutation();
const [restore] = useGitRestoreMutation();
@@ -57,30 +58,25 @@ export default function GitBox() {
const [pull] = useGitPullMutation();
const [push] = useGitPushMutation();
- const [refreshDoc] = useRefreshDocsMutation();
+ const { refetch: refreshDocMenu } = useGetDocSubItemsQuery();
const saveDoc = useSaveDoc();
const addClick = useCallback(
async (changePaths: string[]) => {
if (changePaths.length === 0) {
- Toast('no change needs to be added', 'WARNING');
+ Toast.warn('no change needs to be added');
return;
}
try {
setOpLoading(true);
- const resp = await add(changePaths).unwrap();
-
- if (resp.err === 1) {
- Toast(resp.message, 'ERROR', 2500);
- return;
- }
+ await add(changePaths).unwrap();
- Toast('added', 'SUCCESS');
- } catch {
- Toast('failed to add', 'ERROR', 2500);
+ Toast('added');
+ } catch (err) {
+ Toast.error((err as Error).message);
} finally {
setOpLoading(false);
}
@@ -92,333 +88,317 @@ export default function GitBox() {
// eslint-disable-next-line @typescript-eslint/no-shadow
async (staged: boolean, changes: Change[]) => {
if (changes.length === 0) {
- Toast('no change needs to be restored', 'WARNING');
+ Toast.warn('no change needs to be restored');
return;
}
- // when it is in working space and modal is not being opened
- if (!staged && !restoreConfirmShow) {
- restoreInfoRef.current = { staged, changes };
- setRestoreConfirmShow(true);
+ // when it is in working space
+ if (
+ !staged &&
+ !(await confirm({
+ message: 'Are you sure to restore?',
+ }))
+ ) {
return;
}
try {
setOpLoading(true);
- const resp = await restore({ staged, changes }).unwrap();
+ await restore({ staged, changes }).unwrap();
- if (resp.err === 1) {
- Toast(resp.message, 'ERROR', 2500);
- return;
- }
+ Toast('restored');
- Toast('restored', 'SUCCESS');
-
- restoreHandler(staged, changes);
- } catch {
- Toast('failed to restore', 'ERROR', 2500);
+ restoreEffects(staged, changes);
+ } catch (err) {
+ Toast.error((err as Error).message);
} finally {
setOpLoading(false);
}
},
- [restore, restoreConfirmShow, setOpLoading, restoreHandler],
+ [restore, setOpLoading, restoreEffects],
);
const pullClick = useCallback(
- async (e: React.MouseEvent) => {
+ async (e: React.MouseEvent) => {
e.stopPropagation();
try {
setOpLoading(true);
- const resp = await pull().unwrap();
-
- if (resp.err === 1) {
- Toast(resp.message, 'ERROR', 2500);
- return;
- }
+ await pull().unwrap();
- Toast('updated', 'SUCCESS');
+ Toast('updated');
// refresh the menu
- await refreshDoc().unwrap();
+ await refreshDocMenu().unwrap();
- Toast('refreshed', 'SUCCESS');
- } catch {
- Toast('fail to pull or refresh', 'ERROR');
+ Toast('refreshed');
+ } catch (err) {
+ Toast.error((err as Error).message);
} finally {
setOpLoading(false);
}
},
- [setOpLoading, pull, refreshDoc],
+ [setOpLoading, pull, refreshDocMenu],
);
- const commitConfirm = useCallback(async () => {
+ const commitClick = async () => {
+ if (staged.length === 0) {
+ Toast.warn('no change to be committed');
+ return;
+ }
+
+ if (
+ !(await confirm({
+ message: (
+
+
Title
+
{
+ setCommitMsgTitle(e.target.value);
+ }}
+ className="commit-msg-input"
+ placeholder="commit message title"
+ />
+ Body
+ {
+ setCommitMsgBody(e.target.value);
+ }}
+ className="commit-msg-input"
+ placeholder="commit message body"
+ />
+
+ ),
+ }))
+ ) {
+ return;
+ }
+
if (commitMsgTitle.trim() === '') {
- Toast('commit title can not be blank', 'WARNING');
+ Toast.warn('commit title can not be blank');
return;
}
try {
setOpLoading(true);
- const resp = await commit({
+ await commit({
title: commitMsgTitle,
body: commitMsgBody,
}).unwrap();
- if (resp.err === 1) {
- Toast(resp.message, 'ERROR', 2500);
- return;
- }
-
- Toast('committed', 'SUCCESS');
- } catch {
- Toast('fail to commit', 'ERROR');
+ Toast('committed');
+ } catch (err) {
+ Toast.error((err as Error).message);
} finally {
setOpLoading(false);
}
- }, [commit, commitMsgBody, commitMsgTitle, setOpLoading]);
+ };
const pushClick = useCallback(async () => {
try {
setOpLoading(true);
- const resp = await push().unwrap();
+ await push().unwrap();
- if (resp.err === 1) {
- Toast(resp.message, 'ERROR', 2500);
- return;
- }
-
- Toast('pushed', 'SUCCESS');
- } catch {
- Toast('fail to push', 'ERROR');
+ Toast('pushed');
+ } catch (err) {
+ Toast.error((err as Error).message);
} finally {
setOpLoading(false);
}
}, [push, setOpLoading]);
const openFile = (filePath: string) => {
- if (filePath.includes('.')) {
- Toast('This is not a markdown file', 'WARNING');
+ const norFilePath = normalizePath(filePath);
+ if (norFilePath.includes('.')) {
+ Toast.warn('This is not a markdown file');
return;
}
- if (curPath.join('-') !== filePath) {
+ if (normalizePath(curPath) !== norFilePath) {
saveDoc();
- routerHistory.push(`/article/${filePath}`);
+ void navigate(`/article/${norFilePath as string}`);
}
};
- return (
-
- {!noGit || err === 1 ? (
- <>
-
-
- {'pull'}
-
- {changes && (
-
{
- if (staged.length === 0) {
- Toast(`no change to be committed`, 'WARNING');
- return;
- }
-
- setCommitModalShow(true);
- }}
+ let content = (
+ <>
+
+ {opLoading && }
+
+
+ {
+ void pushClick();
+ }}
+ disabled={opLoading}
+ />
+
+
+
+ {staged.length !== 0 ? (
+
+ {staged.map((change) => (
+
- {'commit'}
-
- )}
- {opLoading &&
}
+
+ {change.changePath}
+
+
+ {change.status !== 'DELETED' && (
+ {
+ openFile(change.changePath.replace('.md', ''));
+ }}
+ />
+ )}
+ restoreClick(true, [change])}
+ />
+ {change.status[0]}
+
+
+ ))}
+
+ ) : (
+
+
+ No file is staged
-
-
- {staged.length !== 0 ? (
-
- {staged.map((change) => (
-
- {change.changePath}
-
- {change.status !== 'DELETED' && (
- {
- openFile(change.changePath.replace('.md', '').replaceAll('/', '-'));
- }}
- >
- file_open
-
- )}
- restoreClick(true, [change])}
- >
- remove
-
- {change.status[0]}
-
-
- ))}
-
- ) : (
-
no file is staged
- )}
+ )}
+
+
+
+ Working Space
+
+ restoreClick(false, workspace)}
+ />
+ addClick(workspace.map((change) => change.changePath as string))}
+ />
-
-
- {workSpace.length !== 0 ? (
-
- {workSpace.map((change) => (
-
- {change.changePath}
-
- {change.status !== 'DELETED' && (
- {
- openFile(change.changePath.replace('.md', '').replaceAll('/', '-'));
- }}
- >
- file_open
-
- )}
- restoreClick(false, [change])}
- >
- undo
-
- addClick([change.changePath])}
- >
- add
-
- {change.status[0]}
-
-
- ))}
-
- ) : (
-
working space is clean
- )}
+
+ {workspace.length !== 0 ? (
+
+ {workspace.map((change) => (
+
+
+ {change.changePath}
+
+
+ {change.status !== 'DELETED' && (
+ {
+ openFile(change.changePath.replace('.md', ''));
+ }}
+ />
+ )}
+ restoreClick(false, [change])}
+ />
+ addClick([change.changePath])}
+ />
+ {change.status[0]}
+
+
+ ))}
+
+ ) : (
+
+
+ Working space is clean
-
pushClick()} disabled={opLoading}>
- push
-
- {commitModalShow && (
-
{
- setLoading(true);
- await commitConfirm();
- setLoading(false);
-
- closeModal();
- }}
- >
-
-
Title
-
{
- setCommitMsgTitle(e.target.value);
- }}
- className="commit-msg-input"
- placeholder="commit message title"
- onClick={(e) => {
- e.stopPropagation();
- }}
- />
-
Body
-
-
- )}
- {restoreConfirmShow && (
-
{
- if (restoreInfoRef.current) {
- // eslint-disable-next-line @typescript-eslint/no-shadow
- const { staged, changes } = restoreInfoRef.current;
- void restoreClick(staged, changes);
- }
-
- closeModal();
- }}
- >
- Are you sure to restore?
-
- )}
- >
- ) : (
-
found no git config
- )}
-
+ )}
+
+ >
);
+
+ if (noGit) {
+ content = (
+
+
+ No git service found
+
+ );
+ } else if (isLoading) {
+ content =
;
+ }
+
+ return
;
}
diff --git a/client/src/components/Header/Header.less b/client/src/components/Header/Header.less
deleted file mode 100644
index 9588d7b..0000000
--- a/client/src/components/Header/Header.less
+++ /dev/null
@@ -1,38 +0,0 @@
-@import url("../../utils/utils.less");
-
-.header-container {
- width: 100%;
- margin-bottom: 0.3rem;
- box-shadow: 0 10px 5px -6px rgba(0, 0, 0, 0.5);
- background-color: @backgroundColor;
- border-radius: 0 5px 5px 0;
- position: sticky;
- top: 0;
- z-index: 1000;
- display: flex;
- padding: 0 2.5rem 0 0rem;
- justify-content: space-between;
- align-items: center;
- transition: @transition;
- .btn-group {
- width: fit-content;
- height: fit-content;
- display: flex;
- justify-content: flex-start;
- align-items: center;
- }
- .icon-btn {
- cursor: pointer;
- user-select: none;
- margin-left: 0.8rem;
- transition: @transition;
- position: relative;
- color: @headerTextColor;
- &:hover {
- transform: scale(1.2, 1.2);
- .git-operation {
- transform: scale(1, 1);
- }
- }
- }
-}
diff --git a/client/src/components/Header/Header.scss b/client/src/components/Header/Header.scss
new file mode 100644
index 0000000..2c92022
--- /dev/null
+++ b/client/src/components/Header/Header.scss
@@ -0,0 +1,26 @@
+@use "../../utils/utils.scss" as *;
+
+.header-container {
+ width: 100%;
+ @include shadow-box(0, 1px, 1px, 0);
+ background-color: $backgroundColor;
+ border-radius: 0 5px 5px 0;
+ position: sticky;
+ top: 0;
+ z-index: 1000;
+ display: flex;
+ padding: 0 2.5rem 0 0rem;
+ justify-content: space-between;
+ align-items: center;
+ transition: $transition;
+ .btn-group {
+ width: fit-content;
+ height: fit-content;
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+ }
+ .icon-wrapper {
+ margin-left: 0.8rem;
+ }
+}
diff --git a/client/src/components/Header/Header.tsx b/client/src/components/Header/Header.tsx
index 0e79c70..33ae66b 100644
--- a/client/src/components/Header/Header.tsx
+++ b/client/src/components/Header/Header.tsx
@@ -1,87 +1,97 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-import React from 'react';
-import { useDispatch, useSelector } from 'react-redux';
+import { Menu } from 'primereact/menu';
+import { MenuItem } from 'primereact/menuitem';
+import { useRef } from 'react';
+import { useSelector } from 'react-redux';
-import DocSearch from '../DocSearch/DocSearch';
-import ImgSearch from '../ImgSearch/ImgSearch';
-import UploadImg from '../UploadImg/UploadImg';
+import { DocSearch } from '../DocSearch/DocSearch';
+import { ImgManagement } from '../ImgManagement/ImgManagement';
+import { Icon } from '@/components/Icon/Icon';
import { selectCurDoc } from '@/redux-feature/curDocSlice';
-import { updateGlobalOpts, selectGlobalOpts } from '@/redux-feature/globalOptsSlice';
+import { selectGlobalOpts } from '@/redux-feature/globalOptsSlice';
import { useSaveDoc, useSwitchReadonlyMode, useSwitchTheme } from '@/utils/hooks/reduxHooks';
+import { nextTick } from '@/utils/utils';
-import './Header.less';
+import './Header.scss';
// eslint-disable-next-line @typescript-eslint/naming-convention
export default function Header() {
- const { isDarkMode, readonly, menuCollapse, mirrorCollapse } = useSelector(selectGlobalOpts);
+ const { readonly, theme } = useSelector(selectGlobalOpts);
+ const { isDirty, type, contentIdent } = useSelector(selectCurDoc);
- const { isDirty } = useSelector(selectCurDoc);
+ const themeMenuRef = useRef
(null);
const saveDoc = useSaveDoc();
const switchReadonlyMode = useSwitchReadonlyMode();
const switchTheme = useSwitchTheme();
- const dispatch = useDispatch();
+ const themeMenuItems: MenuItem[] = [
+ {
+ label: 'Themes',
+ items: [
+ {
+ label: 'Light',
+ icon: 'pi pi-sun',
+ className: theme === 'light' ? 'p-highlight' : '',
+ command: () => {
+ // avoid instant re-render to make the toggle abnormal
+ nextTick(() => switchTheme('light'));
+ },
+ },
+ {
+ label: 'Soft',
+ icon: 'pi pi-face-smile',
+ className: theme === 'soft' ? 'p-highlight' : '',
+ command: () => {
+ nextTick(() => switchTheme('soft'));
+ },
+ },
+ {
+ label: 'Dark',
+ icon: 'pi pi-moon',
+ className: theme === 'dark' ? 'p-highlight' : '',
+ command: () => {
+ nextTick(() => switchTheme('dark'));
+ },
+ },
+ ],
+ },
+ ];
return (
-
{
- dispatch(
- updateGlobalOpts({
- keys: ['menuCollapse'],
- values: [!menuCollapse],
- }),
- );
- }}
- title="menu-toggle"
- role="button"
- >
- menu
-
-
-
+
- {
- dispatch(
- updateGlobalOpts({
- keys: ['mirrorCollapse'],
- values: [!mirrorCollapse],
- }),
- );
- }}
- title="code-mirror"
- role="button"
- >
- {mirrorCollapse ? 'article' : 'chrome_reader_mode'}
-
- void saveDoc()} title="save" role="button">
- {isDirty ? 'save_as' : 'save'}
-
- void saveDoc()}
+ />
+
- {readonly ? 'visibility' : 'mode_edit'}
-
-
- {isDarkMode ? 'dark_mode' : 'light_mode'}
-
+ />
+ {
+ themeMenuRef.current?.toggle(e);
+ }}
+ />
+
);
diff --git a/client/src/components/Icon/Icon.scss b/client/src/components/Icon/Icon.scss
new file mode 100644
index 0000000..20fa6c8
--- /dev/null
+++ b/client/src/components/Icon/Icon.scss
@@ -0,0 +1,26 @@
+@use '@/utils/utils.scss' as *;
+
+.icon-wrapper {
+ @include flex-center();
+ .icon {
+ @include icon-btn();
+ &.disabled {
+ cursor: not-allowed;
+ opacity: 0.5;
+ }
+ }
+ .icon-com {
+ display: inline-block;
+ svg {
+ width: 100%;
+ height: 100%;
+ }
+ }
+}
+
+.icon-tool-tip {
+ .p-tooltip-text {
+ padding: 5px;
+ font-size: 12px;
+ }
+}
diff --git a/client/src/components/Icon/Icon.tsx b/client/src/components/Icon/Icon.tsx
new file mode 100644
index 0000000..4ddc1d6
--- /dev/null
+++ b/client/src/components/Icon/Icon.tsx
@@ -0,0 +1,64 @@
+import { Tooltip } from 'primereact/tooltip';
+import { FC } from 'react';
+
+import './Icon.scss';
+
+export interface IconProps {
+ /** for primeReact icon */
+ iconName?: string;
+ id: string;
+ /** for material icon */
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ icon?: any;
+ size?: string;
+ disabled?: boolean;
+ onClick?: (e: React.MouseEvent) => void;
+ className?: string;
+ style?: React.CSSProperties;
+ showToolTip?: boolean;
+ toolTipContent?: string;
+ toolTipPosition?: 'bottom' | 'left' | 'right' | 'top';
+}
+
+export const Icon: FC = ({
+ iconName,
+ id,
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ icon: IconCom,
+ size = '16px',
+ disabled = false,
+ onClick,
+ style,
+ className = '',
+ showToolTip = true,
+ toolTipContent,
+ toolTipPosition = 'bottom',
+}) => {
+ return (
+ {
+ if (disabled) {
+ return;
+ }
+ onClick?.(e);
+ }}
+ id={`icon-${id}`}
+ >
+ {showToolTip && (
+
+ )}
+ {IconCom ? (
+
+
+
+ ) : (
+
+ )}
+
+ );
+};
diff --git a/client/src/components/ImgManagement/ImgManagement.scss b/client/src/components/ImgManagement/ImgManagement.scss
new file mode 100644
index 0000000..b74f622
--- /dev/null
+++ b/client/src/components/ImgManagement/ImgManagement.scss
@@ -0,0 +1,88 @@
+@use "@/utils/utils.scss" as *;
+
+.img-management-container {
+ #icon-img-management-icon {
+ margin: 0;
+ }
+}
+
+.img-management-dialog {
+ width: 800px;
+ background-color: $backgroundColor;
+}
+
+.img-management-modal-content {
+ .p-dataview-emptymessage {
+ color: var(--shallowTextColor);
+ height: 100%;
+ @include flex-center();
+ }
+ .p-dataview-content {
+ height: 500px;
+ overflow: auto;
+ }
+ .img-list-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ .img-count {
+ font-size: 0.9rem;
+ opacity: 0.7;
+ }
+
+ .img-filter-dropdown {
+ width: 14rem;
+ }
+ }
+
+ .img-list-item {
+ display: flex;
+ align-items: center;
+ padding: 0.75rem 0;
+ gap: 1rem;
+ padding-right: 0.5rem;
+
+ &.border-top {
+ border-top: 1px solid var(--surface-border, #dee2e6);
+ }
+
+ .img-thumbnail {
+ width: 80px;
+ height: 80px;
+ object-fit: cover;
+ border-radius: 6px;
+ flex-shrink: 0;
+ }
+
+ .img-actions {
+ display: flex;
+ flex-shrink: 0;
+ gap: 0.25rem;
+ }
+
+ .img-details {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ flex: 1;
+ min-width: 0;
+
+ .img-name {
+ font-weight: 600;
+ word-break: break-all;
+ }
+
+ .img-meta {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ font-size: 0.85rem;
+
+ .img-date {
+ opacity: 0.6;
+ }
+ }
+ }
+ }
+}
diff --git a/client/src/components/ImgManagement/ImgManagement.tsx b/client/src/components/ImgManagement/ImgManagement.tsx
new file mode 100644
index 0000000..a26d0d1
--- /dev/null
+++ b/client/src/components/ImgManagement/ImgManagement.tsx
@@ -0,0 +1,180 @@
+import { Button } from 'primereact/button';
+import { DataView } from 'primereact/dataview';
+import { Dialog } from 'primereact/dialog';
+import { Dropdown } from 'primereact/dropdown';
+import { Tag } from 'primereact/tag';
+import { FC, useCallback, useEffect, useMemo, useState } from 'react';
+import { useSelector } from 'react-redux';
+
+import { getImageUrl } from '@/components/Editor/configs/uploadConfig';
+import { Icon } from '@/components/Icon/Icon';
+import { ImgListItem, useDeleteWorkspaceImgMutation, useGetImgListQuery } from '@/redux-api/imgStoreApi';
+import { selectCurContent } from '@/redux-feature/curDocSlice';
+import Toast from '@/utils/Toast';
+import { confirm } from '@/utils/utils';
+
+import './ImgManagement.scss';
+
+type FilterMode = 'all' | 'unused' | 'used';
+
+const filterOptions = [
+ { label: 'All Images', value: 'all' },
+ { label: 'Used in Current Doc', value: 'used' },
+ { label: 'Not Used in Current Doc', value: 'unused' },
+];
+
+function extractImageUrls(markdown: string): Set {
+ const urls = new Set();
+ const regex = /!\[.*?\]\((.*?)\)/g;
+ let match = regex.exec(markdown);
+ while (match !== null) {
+ urls.add(match[1]);
+ match = regex.exec(markdown);
+ }
+ return urls;
+}
+
+const MS_PER_SECOND = 1000;
+
+function formatDate(timestamp: number): string {
+ if (!timestamp) return '-';
+ return new Date(timestamp * MS_PER_SECOND).toLocaleDateString(undefined, {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ });
+}
+
+export const ImgManagement: FC = () => {
+ const [showImgManagementModal, setShowImgManagementModal] = useState(false);
+ const [filterMode, setFilterMode] = useState('used');
+
+ const { data: images = [], refetch: refetchImgList } = useGetImgListQuery(undefined, {
+ skip: !showImgManagementModal,
+ });
+ const [deleteImg] = useDeleteWorkspaceImgMutation();
+ const curContent = useSelector(selectCurContent);
+
+ const usedUrls = useMemo(() => extractImageUrls(curContent || ''), [curContent]);
+
+ const handleDelete = useCallback(
+ async (fileName: string) => {
+ const confirmed = await confirm({
+ message: `Are you sure you want to delete "${fileName}"?`,
+ });
+ if (!confirmed) return;
+ await deleteImg(fileName);
+ },
+ [deleteImg],
+ );
+
+ const filteredImages = useMemo(() => {
+ if (filterMode === 'all') return images;
+ return images.filter((img) => {
+ const isUsed = usedUrls.has(img.url);
+ return filterMode === 'used' ? isUsed : !isUsed;
+ });
+ }, [images, filterMode, usedUrls]);
+
+ useEffect(() => {
+ if (!showImgManagementModal) return;
+ refetchImgList();
+ }, [refetchImgList, usedUrls]);
+
+ const itemTemplate = (img: ImgListItem, index: number) => {
+ const isUsed = usedUrls.has(img.url);
+ return (
+
+
+
+
+
{img.fileName}
+
+ {formatDate(img.createdTime)}
+
+
+
+
+ {
+ void navigator.clipboard.writeText(img.url).then(() => {
+ Toast.success('Copied.');
+ });
+ }}
+ />
+ {
+ void handleDelete(img.fileName);
+ }}
+ />
+
+
+
+ );
+ };
+
+ const listTemplate = (items: ImgListItem[]) => {
+ if (!items || items.length === 0) return Empty
;
+ return {items.map((img, i) => itemTemplate(img, i))}
;
+ };
+
+ const header = (
+
+ {filteredImages.length} image(s)
+ {
+ setFilterMode(e.value as FilterMode);
+ }}
+ className="img-filter-dropdown"
+ />
+
+ );
+
+ return (
+
+ {
+ setShowImgManagementModal(true);
+ }}
+ />
+ Images
}
+ visible={showImgManagementModal}
+ onHide={() => {
+ setShowImgManagementModal(false);
+ }}
+ className="img-management-dialog"
+ >
+
+
+
+
+
+ );
+};
diff --git a/client/src/components/ImgSearch/ImgSearch.less b/client/src/components/ImgSearch/ImgSearch.less
deleted file mode 100644
index d9b39c6..0000000
--- a/client/src/components/ImgSearch/ImgSearch.less
+++ /dev/null
@@ -1,110 +0,0 @@
-@import url("@/utils/utils.less");
-
-.img-search-box {
- @width: 20rem;
- @height: 2rem;
- margin: 0 1rem;
- height: @height;
- position: relative;
- .search-input {
- width: @width;
- height: 100%;
- line-height: @height;
- font-size: 16px;
- border-radius: 15px;
- }
- .result-wrapper {
- position: absolute;
- left: 0;
- top: @height + 0.5rem;
- background-color: #e6e6e6;
- border-radius: 5px;
- padding: 0.5rem;
- font-weight: bold;
- box-shadow: @shadow;
- .result-info {
- margin-bottom: 1rem;
- }
- }
- .search-results-box {
- min-width: @width + 15rem;
- max-height: @width + 15rem;
- overflow: auto;
- overflow-x: hidden;
- border-radius: 5px;
- display: flex;
- flex-direction: column;
- justify-content: flex-start;
- .result-item {
- width: 100%;
- height: fit-content;
- padding: 0.5rem;
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- border-bottom: 1px solid rgba(0, 0, 0, 0.5);
- .info-btn-common {
- display: inline-block;
- background-color: #60b6ef;
- border-radius: 5px;
- padding: 0.3rem;
- font-weight: bold;
- &:hover {
- background-color: #3aa1e5;
- }
- }
- .img-info {
- // width: 20rem;
- height: fit-content;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- align-items: flex-start;
- margin-right: 1rem;
- .img-info-item {
- margin: 0.5rem 0;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: @width + 15rem;
- font-weight: lighter;
- cursor: pointer;
- position: relative;
- .info-label {
- .info-btn-common();
- margin-right: 0.5rem;
- }
- }
- .rename-btn {
- .info-btn-common();
- margin-left: 1rem;
- }
- }
- .img-store-img {
- width: 6rem;
- height: 6rem;
- object-fit: cover;
- border-radius: 5px;
- cursor: pointer;
- transition: @transition;
- &:hover {
- transform: scale(1.2);
- }
- }
- }
- }
-}
-
-.upload-img-rename-input {
- width: 30rem;
- margin: 0.5rem 2rem 1rem 2rem;
- display: block;
- padding: 0.5rem;
- outline: none;
- border-radius: 5px;
- border: 0.5px solid #e7e0e0;
- position: relative;
- &:focus {
- border-color: #62b5ec;
- }
-}
diff --git a/client/src/components/ImgSearch/ImgSearch.tsx b/client/src/components/ImgSearch/ImgSearch.tsx
deleted file mode 100644
index e75bf5a..0000000
--- a/client/src/components/ImgSearch/ImgSearch.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-return */
-/* eslint-disable @typescript-eslint/no-magic-numbers */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-import React, { useRef, useCallback, useEffect, useState } from 'react';
-
-import ResultBox from './ResultBox';
-import Spinner from '../../utils/Spinner/Spinner';
-
-import { useGetUploadHistoryQuery } from '@/redux-api/imgStoreApi';
-import { useDebounce } from '@/utils/hooks/tools';
-
-import './ImgSearch.less';
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function ImgSearch() {
- const {
- data: { imgList: uploadList, err, message } = {
- imgList: [],
- err: 0,
- message: '',
- },
- isSuccess,
- isError,
- } = useGetUploadHistoryQuery();
- const [searchRet, setSearchRet] = useState(uploadList);
- const [resultShow, setResultShow] = useState(false);
-
- const searchInputRef = useRef(null);
-
- const search = useCallback(
- (searchContent: string) =>
- searchContent
- .split(' ')
- .reduce((results, word) => results.filter((result) => result.name.toLowerCase().includes(word)), uploadList),
- [uploadList],
- );
-
- const handleSearch = useDebounce((e: React.ChangeEvent) => {
- setSearchRet(search(e.target.value));
- }, 500);
-
- useEffect(() => {
- if (searchInputRef.current && searchInputRef.current.value.trim() !== '') {
- setSearchRet(search(searchInputRef.current.value));
- }
- }, [search]);
-
- return (
-
-
{
- if (searchInputRef.current && searchInputRef.current.value.trim() === '') {
- setSearchRet(search(''));
- }
- setResultShow(true);
- }}
- onBlur={() => {
- setResultShow(false);
- }}
- />
-
-
{`found ${searchRet.length.toString() as string} related images`}
- {isSuccess ? (
-
- ) : (
-
- {isError || err === 1 ?
{message}
:
}
-
- )}
-
-
- );
-}
diff --git a/client/src/components/ImgSearch/ResultBox.tsx b/client/src/components/ImgSearch/ResultBox.tsx
deleted file mode 100644
index 34b53ea..0000000
--- a/client/src/components/ImgSearch/ResultBox.tsx
+++ /dev/null
@@ -1,217 +0,0 @@
-/* eslint-disable @typescript-eslint/naming-convention */
-/* eslint-disable @typescript-eslint/no-magic-numbers */
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-import React, { useCallback, useEffect, useRef, useState } from 'react';
-
-import Modal from '../../utils/Modal/Modal';
-import Spinner from '../../utils/Spinner/Spinner';
-
-import { useDeleteImgMutation, useRenameImgMutation, ImgDataType } from '@/redux-api/imgStoreApi';
-import Toast from '@/utils/Toast';
-import { hightLight, scrollToBottomListener } from '@/utils/utils';
-
-interface ResultBoxProps {
- results: ImgDataType[];
- searchContent?: string;
-}
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function ResultBox({ results, searchContent = '' }: ResultBoxProps) {
- const [showNum, setShowNum] = useState(4);
- const [isDeleting, setIsDeleting] = useState(new Array(results.length).fill(false));
- const [deleteConfirmShow, setDeleteConfirmShow] = useState(false);
- const [renameShow, setRenameShow] = useState(false);
- const [renameValue, setRenameValue] = useState('');
-
- const deleteInfoRef = useRef({ imgName: '', idx: 0 });
- const renameSelectedName = useRef('');
- const resultBoxRef = useRef(null);
-
- const [deleteImgMutation] = useDeleteImgMutation();
- const [renameImgMutation] = useRenameImgMutation();
-
- const copyInfo = useCallback(async (info: string) => {
- await navigator.clipboard.writeText(info);
- Toast('copied!', 'SUCCESS');
- }, []);
-
- const deleteImg = useCallback(
- async (imgName: string, idx: number) => {
- // eslint-disable-next-line @typescript-eslint/no-shadow
- setIsDeleting((isDeleting) => {
- const deleteStatus = [...isDeleting];
- deleteStatus[idx] = true;
- return deleteStatus;
- });
-
- try {
- const resp = await deleteImgMutation(imgName).unwrap();
-
- if (resp.err === 0) {
- Toast('deleted!', 'SUCCESS');
- return;
- }
-
- throw new Error();
- } catch {
- Toast('failed to delete', 'ERROR');
- } finally {
- // eslint-disable-next-line @typescript-eslint/no-shadow
- setIsDeleting((isDeleting) => {
- const deleteStatus = [...isDeleting];
- deleteStatus[idx] = false;
- return deleteStatus;
- });
- }
- },
- [setIsDeleting, deleteImgMutation],
- );
-
- const rename = useCallback(async () => {
- if (renameSelectedName.current.split('.')[0] === renameValue) {
- Toast('the name has not been changed', 'WARNING');
- return;
- }
-
- try {
- const resp = await renameImgMutation({
- fileName: renameSelectedName.current,
- newName: `${renameValue}.${renameSelectedName.current.split('.')[1]}`,
- }).unwrap();
-
- if (resp.err === 0) {
- Toast(resp.message, 'SUCCESS');
- return;
- }
-
- throw new Error(resp.message);
- } catch (err) {
- Toast(String(err), 'ERROR');
- }
- }, [renameImgMutation, renameValue]);
-
- /**
- * scroll down to the bottom to show more images
- */
- useEffect(() => {
- if (!resultBoxRef.current) return;
-
- // every time when the results changed, reset to only show 4 images
- setShowNum(4);
-
- const remover = scrollToBottomListener(resultBoxRef.current, () => {
- setShowNum((num) => (num + 4 > results.length ? results.length : num + 4));
- });
-
- return remover as () => void;
- }, [results]);
-
- return (
- <>
- {
- e.preventDefault();
- }}
- >
- {results.length !== 0 &&
- results.slice(0, showNum).map((imgData, idx) => (
-
-
-
void copyInfo(imgData.url)}
- >
- url:
- {imgData.url}
-
-
void copyInfo(imgData.name)}>
- name:
-
- {
- e.stopPropagation();
- setRenameValue(imgData.name.split('.')[0]);
- renameSelectedName.current = imgData.name;
- setRenameShow(true);
- }}
- >
- rename
-
-
-
- {isDeleting[idx] ? (
-
- ) : (
- {
- setDeleteConfirmShow(true);
- deleteInfoRef.current = { imgName: imgData.name, idx };
- }}
- >
- delete
-
- )}
-
-
-
{
- e.currentTarget.classList.add('img-error');
- e.currentTarget.classList.remove('img-loading');
- }}
- onLoad={(e) => {
- e.currentTarget.classList.remove('img-loading');
- }}
- />
-
- ))}
-
- {deleteConfirmShow && (
- {
- closeModal();
- const { imgName, idx } = deleteInfoRef.current;
- void deleteImg(imgName, idx);
- }}
- >
- Are you sure to delete it?
-
- )}
- {renameShow && (
- {
- setLoading(true);
- void rename().then(() => {
- setLoading(false);
- });
- }}
- >
- {
- setRenameValue(e.target.value);
- }}
- />
-
- )}
- >
- );
-}
diff --git a/client/src/components/Menu/Empty.tsx b/client/src/components/Menu/Empty.tsx
new file mode 100644
index 0000000..61b5b9c
--- /dev/null
+++ b/client/src/components/Menu/Empty.tsx
@@ -0,0 +1,43 @@
+import { Button } from 'primereact/button';
+import { FC, useState } from 'react';
+
+import { FolderSelectorModal } from '@/components/FolderSelector/FolderSelector';
+import { useUpdateSettingsMutation } from '@/redux-api/settings';
+import Toast from '@/utils/Toast';
+
+export const Empty: FC = () => {
+ const [showFolderSelector, setShowFolderSelector] = useState(false);
+ const [updateSettings] = useUpdateSettingsMutation();
+
+ const handleModalHidden = () => {
+ setShowFolderSelector(false);
+ };
+
+ const handleConfirm = async (selectedFolderPath: string) => {
+ if (!selectedFolderPath) return;
+
+ try {
+ await updateSettings({ docRootPath: selectedFolderPath }).unwrap();
+ Toast('Settings updated successfully');
+ } catch (e) {
+ Toast.error((e as Error).message);
+ } finally {
+ setShowFolderSelector(false);
+ }
+ };
+
+ return (
+
+ {
+ setShowFolderSelector(true);
+ }}
+ >
+ Select Workspace
+
+
+
+ );
+};
diff --git a/client/src/components/Menu/FileLink.tsx b/client/src/components/Menu/FileLink.tsx
deleted file mode 100644
index f765c97..0000000
--- a/client/src/components/Menu/FileLink.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react';
-import { Link } from 'react-router-dom';
-
-import Outline from '../Outline/Outline';
-
-import { useSaveDoc } from '@/utils/hooks/reduxHooks';
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-function FileLink({
- path,
- handleShowMenu,
-}: {
- path: string[];
- handleShowMenu: (e: React.MouseEvent, path: string[]) => void;
-}) {
- const saveDoc = useSaveDoc();
-
- return (
- {
- handleShowMenu(e, path);
- }}
- onClick={saveDoc}
- >
- {path[path.length - 1]}
-
-
- );
-}
-
-export default React.memo(FileLink);
diff --git a/client/src/components/Menu/Menu.scss b/client/src/components/Menu/Menu.scss
new file mode 100644
index 0000000..cad1ae4
--- /dev/null
+++ b/client/src/components/Menu/Menu.scss
@@ -0,0 +1,122 @@
+@use '@/utils/utils.scss' as *;
+@use "sass:color";
+
+.menu-container {
+ border-right: 1px solid $shadowColor;
+ height: 100%;
+ font-size: 20px;
+ @include flex-center();
+ .menu-wrapper {
+ width: 100%;
+ height: calc(100% - 54px);
+ .p-scrollpanel-content, .p-scrollpanel-wrapper {
+ padding-bottom: 20px;
+ }
+ }
+ .error-container {
+ color: $shallowTextColor;
+ }
+
+ .shortcut-bar {
+ position: sticky;
+ z-index: 1000;
+ background-color: $backgroundColor;
+ border-bottom: 1px solid $shadowColor;
+ top: 0;
+ font-size: 14px;
+ width: 100%;
+ height: 54px;
+ padding: 5px;
+ padding-right: 10px;
+ @include flex-center();
+ justify-content: flex-end;
+ .icon-wrapper {
+ margin-left: 0.8rem;
+ }
+ }
+
+ .item-container {
+ color: $contentTextColor;
+ // position: relative;
+ .link {
+ text-decoration: none;
+ display: inline-block;
+ cursor: pointer;
+ color: $contentTextColor;
+ i {
+ margin-right: 0.5rem;
+ color: $shallowTextColor;
+ }
+ }
+ .item-wrapper {
+ // border: 1px solid transparent;
+ position: relative;
+ opacity: 0.7;
+ &.selected {
+ background-color: $selectedBgcColor;
+ }
+ &:hover:not(.selected) {
+ background-color: $hoverBgcColor;
+ }
+ .expand-line {
+ position: absolute;
+ height: calc(100%);
+ width: 1px;
+ }
+ }
+ .item {
+ border-radius: $borderRadius;
+ cursor: pointer;
+ padding: 6px 12px;
+ padding-left: 0.5rem;
+ @include flex-center();
+ justify-content: flex-start;
+ &-arrow {
+ @include flex-center();
+ margin-right: 0.5rem;
+ i {
+ transition: $transition;
+ }
+ }
+ }
+ .menu-item-input {
+ outline: none;
+ height: 25px;
+ width: 100%;
+ padding: 5px;
+ font-size: 12px;
+ margin-right: 0.5rem;
+ }
+
+ .operation-item {
+ cursor: default;
+ background-color: $hoverBgcColor;
+ .input-confirm {
+ cursor: pointer;
+ color: $selectedBgcColor;
+ &:hover {
+ color: $shallowTextColor;
+ }
+ }
+ }
+ }
+
+ .drag-between-line {
+ position: absolute;
+ right: 0;
+ height: 4px;
+ background-color: $hoverBgcColor;
+ }
+
+ .rct-tree-items-container {
+ padding: 0;
+ }
+
+ .empty-container {
+ color: $shallowTextColor;
+ @include flex-center();
+ flex-direction: column;
+ font-size: 14px;
+ text-align: center;
+ }
+}
diff --git a/client/src/components/Menu/Menu.tsx b/client/src/components/Menu/Menu.tsx
index 0dacdf0..07f830a 100644
--- a/client/src/components/Menu/Menu.tsx
+++ b/client/src/components/Menu/Menu.tsx
@@ -1,59 +1,288 @@
-/* eslint-disable @typescript-eslint/no-unsafe-assignment */
-/* eslint-disable @typescript-eslint/no-unsafe-member-access */
-import React from 'react';
-import { useDispatch } from 'react-redux';
+import { ContextMenu } from 'primereact/contextmenu';
+import { MenuItem as PrimeMenuItem } from 'primereact/menuitem';
+import { ProgressSpinner } from 'primereact/progressspinner';
+import { ScrollPanel } from 'primereact/scrollpanel';
+import { Tooltip } from 'primereact/tooltip';
+import { FC, useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
+import {
+ UncontrolledTreeEnvironment,
+ Tree,
+ StaticTreeDataProvider,
+ TreeItemIndex,
+ TreeItem,
+ TreeRef,
+ DraggingPosition,
+ TreeEnvironmentRef,
+} from 'react-complex-tree';
+import { useSelector, useDispatch } from 'react-redux';
-import FileLink from './FileLink';
-import Subject from './Subject';
+import { Empty } from './Empty';
+import { useDropDoc, useNewDocItem, usePasteDoc, useUpdateSubDocItems } from './operations';
+import { createRenderItem, renderDragBetweenLine, renderItemArrow } from './renderer';
+import { Shortcut } from './Shortcut';
+import { TreeDataCtx, TreeRefCtx, TreeItemData, MenuCtx, TreeEnvRefCtx } from './type';
-import { DOC } from '@/redux-api/docsApiType';
-import { updateOperationMenu } from '@/redux-feature/operationMenuSlice';
+import { useGetDocSubItemsQuery } from '@/redux-api/docs';
+import { useGetSettingsQuery } from '@/redux-api/settings';
+import { selectCurDoc } from '@/redux-feature/curDocSlice';
+import { selectServerStatus, ServerStatus } from '@/redux-feature/globalOptsSlice';
+import { selectOperationMenu, updateSelectedItems } from '@/redux-feature/operationMenuSlice';
+import { normalizePath, scrollToView, waitAndCheck, denormalizePath } from '@/utils/utils';
+
+import './Menu.scss';
+
+export const Menu: FC = () => {
+ const tree = useRef(null);
+ const treeEnvRef = useRef(null);
+ const menuContainer = useRef(null);
+ const cm = useRef(null);
+
+ const [isEnterMenu, setIsEnterMenu] = useState(false);
+ const { data: docRootItems = [], isFetching, isSuccess, isError, error } = useGetDocSubItemsQuery();
+ const updateSubDocItems = useUpdateSubDocItems();
+
+ const { contentIdent: contentPath } = useSelector(selectCurDoc);
+ const { copyCutPaths } = useSelector(selectOperationMenu);
+ const serverStatus = useSelector(selectServerStatus);
+ const { data: settings } = useGetSettingsQuery();
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function Menu({ docs }: { docs: DOC[] }) {
const dispatch = useDispatch();
- // handle right click and show the menu
- // useCallback to memory the reference of the callback
- // so that as a prop it will not be considered as changed
- const handleShowMenu = React.useCallback(
- (e: React.MouseEvent, path: string[]) => {
- e.preventDefault();
- e.stopPropagation();
-
- dispatch(
- updateOperationMenu({
- isShow: true,
- xPos: e.clientX,
- yPos: e.clientY,
- path,
- }),
- );
+ const renderItem = useMemo(() => createRenderItem(), []);
+
+ const createNewDocItem = useNewDocItem();
+ const pasteDoc = usePasteDoc();
+ const dropDoc = useDropDoc();
+
+ const renderData = useMemo(() => {
+ const root: Record> = {
+ root: {
+ index: 'root',
+ canMove: false,
+ isFolder: true,
+ // first level sorted docs
+ children: docRootItems.map((d) => normalizePath(d.path)),
+ canRename: false,
+ data: { path: [], id: 'root', name: 'root', parentIdx: '' },
+ },
+ };
+
+ // the reset of sub docs will be reqeusted in require(when expanding)
+ return docRootItems.reduce((treeData, docRootItem) => {
+ const { path, id, name, isFile } = docRootItem;
+ const parentIdx = 'root';
+ const idx = normalizePath(path);
+ treeData[idx] = {
+ index: idx,
+ canMove: true,
+ isFolder: !isFile,
+ children: [],
+ canRename: true,
+ data: { path, id, name, parentIdx },
+ };
+ return treeData;
+ }, root);
+ }, [docRootItems, isFetching]);
+ const treeDataProvider = useMemo(() => new StaticTreeDataProvider(renderData), [renderData]);
+
+ const selectedItemPathKeys = useMemo(() => {
+ const selectedDocPath = denormalizePath(contentPath);
+ return selectedDocPath.reduce((pathKeys, pathItem, idx) => {
+ if (idx - 1 < 0) {
+ pathKeys.push(pathItem);
+ } else if (pathKeys[idx - 1]) {
+ pathKeys.push(`${pathKeys[idx - 1]}${normalizePath(`/${pathItem}`)}`);
+ }
+ return pathKeys;
+ }, []);
+ }, [contentPath]);
+
+ useEffect(() => {
+ void treeDataProvider.onDidChangeTreeDataEmitter.emit(['root']);
+ }, [treeDataProvider]);
+
+ useEffect(() => {
+ if (contentPath) {
+ const fn = async () => {
+ const expandKeys = selectedItemPathKeys.slice(0, -1);
+ // TODO: request sub docs in parallel, may need to wait for the parent doc logics
+ for (const docIdx of expandKeys) {
+ const docItem = renderData[docIdx];
+ if (!docItem || docItem.children?.length) continue;
+ await updateSubDocItems(docItem, renderData, treeDataProvider);
+ }
+
+ // FIXME: any better way to determine when the tree ref is available?
+ const hasTree = await waitAndCheck(() => Boolean(tree.current));
+ if (!hasTree) return;
+
+ if (expandKeys.length) {
+ await tree.current?.expandSubsequently(expandKeys);
+ }
+
+ const selectdItemPath = selectedItemPathKeys[selectedItemPathKeys.length - 1];
+ tree.current?.selectItems([selectdItemPath]);
+
+ const selectedItem = renderData[selectdItemPath];
+ if (!selectedItem) return;
+ // FIXME: any better way to determine when the children have been rendered?
+ const hasChildren = await waitAndCheck(() =>
+ Boolean(
+ document.querySelector('.menu-wrapper .p-scrollpanel-content') &&
+ document.getElementById(selectedItem.data.id),
+ ),
+ );
+ if (!hasChildren) return;
+
+ const scrollContainer = document.querySelector('.menu-wrapper .p-scrollpanel-content');
+ const target = document.getElementById(selectedItem.data.id);
+ if (!scrollContainer || !target) return;
+ scrollToView(scrollContainer as HTMLElement, target);
+ };
+ void fn();
+ }
+ }, [selectedItemPathKeys, renderData, treeDataProvider]);
+
+ const rootContextMenuItems: PrimeMenuItem[] = [
+ {
+ label: 'New File',
+ icon: 'pi pi-file',
+ command: () => {
+ void createNewDocItem(renderData.root, false, treeDataProvider, renderData);
+ },
},
- [dispatch],
- );
+ {
+ label: 'New Folder',
+ icon: 'pi pi-folder',
+ command: () => {
+ void createNewDocItem(renderData.root, true, treeDataProvider, renderData);
+ },
+ },
+ {
+ label: 'Paste',
+ icon: 'pi pi-clipboard',
+ disabled: !copyCutPaths.length,
+ command: () => {
+ void pasteDoc({
+ pasteParentPathArr: [],
+ providedTreeDataCtx: { data: renderData, provider: treeDataProvider },
+ });
+ },
+ },
+ ];
+ const onRightClick = (event: React.MouseEvent): void => {
+ if (cm.current) {
+ // clear the last one
+ document.body.click();
+ cm.current.show(event);
+ }
+ };
+
+ const onDrop = async (items: TreeItem[], target: DraggingPosition) => {
+ return dropDoc({ items, target, treeData: renderData, treeProvider: treeDataProvider });
+ };
+
+ const onSelectItems = (items: TreeItemIndex[]) => {
+ dispatch(updateSelectedItems(items as string[]));
+ };
+
+ const onClickMenuContainer = (e: React.MouseEvent) => {
+ if (e.target === document.querySelector('.menu-wrapper .p-scrollpanel-content')) {
+ // clear the selected items
+ tree.current?.selectItems([]);
+ }
+ };
+
+ const onExpandItem = async (item: TreeItem) => {
+ // already fetched
+ if (item.children?.length) return;
+
+ await updateSubDocItems(item, renderData, treeDataProvider);
+ };
+
+ let content: ReactNode = <>>;
+ if (isSuccess) {
+ if (!settings?.docRootPath) {
+ content = ;
+ } else {
+ content = (
+
+
+
+
+ ) => item.data.name}
+ viewState={{}}
+ renderItem={renderItem}
+ renderItemArrow={renderItemArrow}
+ renderDragBetweenLine={renderDragBetweenLine}
+ canSearchByStartingTyping={false}
+ canDragAndDrop={true}
+ canReorderItems={true}
+ canDropOnFolder={true}
+ canDropOnNonFolder={true}
+ onDrop={(...args) => void onDrop(...args)}
+ onExpandItem={(item) => {
+ void onExpandItem(item);
+ }}
+ canDropAt={(items, target) => {
+ const targetItem = target.targetType === 'between-items' ? target.parentItem : target.targetItem;
+ const isAlreadyInTarget = items.find((item) => renderData[targetItem].children?.includes(item.index));
+ if (isAlreadyInTarget) return false;
+ if (renderData[targetItem]?.isFolder) return true;
+ return false;
+ }}
+ >
+
+
+
+
+ );
+ }
+ } else if (isFetching) {
+ content = ;
+ } else if (isError) {
+ if (serverStatus === ServerStatus.RUNNING) {
+ content = (
+
+
+
+ Ops, something went wrong
+
+
+ );
+ } else {
+ // should install the server to select workspace
+ content = ;
+ }
+ }
return (
-
- {docs.map((doc) =>
- doc.isFile ? (
-
- ) : (
-
- ),
- )}
+
{
+ setIsEnterMenu(true);
+ }}
+ onMouseLeave={() => {
+ setIsEnterMenu(false);
+ }}
+ >
+
+
+
+ {content}
+
+
+
);
-}
-
-// export default React.memo(Menu);
-
-// export default React.memo(Menu, (prevProps, nextProps) => {
-// /*
-// 如果把 nextProps 传入 render 方法的返回结果与
-// 将 prevProps 传入 render 方法的返回结果一致则返回 true,
-// 否则返回 false
-// */
-// console.log(prevProps === nextProps);
-// return prevProps === nextProps;
-// });
+};
diff --git a/client/src/components/Menu/MenuContainer.less b/client/src/components/Menu/MenuContainer.less
deleted file mode 100644
index 76a724a..0000000
--- a/client/src/components/Menu/MenuContainer.less
+++ /dev/null
@@ -1,126 +0,0 @@
-@import url("../../utils/utils.less");
-
-.operation-menu {
- position: absolute;
- z-index: 2000;
- width: 10rem;
- height: fit-content;
- background-color: #fff;
- border: #e6e6e6 solid 1px;
- flex-direction: column;
- align-items: center;
- justify-content: space-between;
- .shadow-box();
- .operations {
- width: 100%;
- padding: 0.5rem;
- text-align: start;
- border-bottom: #e6e6e6 solid 1px;
- font-weight: bold;
- color: gray;
- cursor: pointer;
- &:hover {
- background-color: rgb(230, 221, 221);
- }
- }
-}
-
-.menu-container {
- display: flex;
- flex-direction: column;
- justify-content: space-around;
- width: 18%;
- height: 100vh;
- padding: 0.5rem 0rem 1rem 0rem;
- transition: @transition;
- .refresh-btn {
- margin: 0 1rem 0.3rem 0;
- align-self: flex-end;
- cursor: pointer;
- color: @headerTextColor;
- transition: @transition;
- &:hover {
- transform: scale(1.1);
- }
- }
- .fetching {
- animation: loading 0.5s infinite linear;
- }
- .menu-wrapper {
- flex: 1;
- overflow-y: scroll;
- padding: 0 0.3rem 0 0.5rem;
- .menu-box {
- width: 100%;
- height: fit-content;
- }
- }
-
- .link {
- text-decoration: none;
- display: inline-block;
- cursor: pointer;
- color: @contentTextColor;
- }
- .subject {
- width: 100%;
- height: fit-content;
- cursor: pointer;
- position: relative;
- .subject-title {
- width: 100%;
- height: 2.5rem;
- line-height: 2.5rem;
- font-weight: bold;
- padding-left: 10px;
- border-radius: 5px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- transition: all 0.4s ease-in-out;
- background-color: @boxColor;
- color: @headerTextColor;
- // position: relative;
- .expand-icon {
- transition: @transition;
- }
- }
- .sub-children {
- margin: 3px 0;
- width: 100%;
- padding-left: 0.5rem;
- transition: @transition;
- transform-origin: 0 0;
- }
- }
- .file {
- width: 100%;
- height: 2rem;
- padding: 0 0.5rem;
- line-height: 2rem;
- position: relative;
- display: flex;
- justify-content: space-between;
- &:hover {
- &:after {
- content: "";
- display: block;
- width: inherit;
- height: inherit;
- position: absolute;
- left: 0;
- top: 0;
- background: rgb(152, 149, 149);
- opacity: 0.3;
- }
- }
- .show-outline-icon {
- z-index: 1000;
- opacity: 0.5;
- color: @headerTextColor;
- &:hover {
- opacity: 1;
- }
- }
- }
-}
diff --git a/client/src/components/Menu/MenuContainer.tsx b/client/src/components/Menu/MenuContainer.tsx
deleted file mode 100644
index d0252d0..0000000
--- a/client/src/components/Menu/MenuContainer.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import React, { useEffect } from 'react';
-import { useSelector, useDispatch } from 'react-redux';
-
-import Menu from './Menu';
-import Refresh from './Refresh';
-import Spinner from '../../utils/Spinner/Spinner';
-import OperationMenu from '../OperationMenu/OperationMenu';
-
-import { useGetDocMenuQuery } from '@/redux-api/docsApi';
-import { selectMenuCollapse } from '@/redux-feature/globalOptsSlice';
-import { updateOperationMenu, selectOperationMenu } from '@/redux-feature/operationMenuSlice';
-
-import './MenuContainer.less';
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function MenuContainer() {
- const { data: docs = [], isFetching, isSuccess, isError } = useGetDocMenuQuery();
-
- const menuCollapse = useSelector(selectMenuCollapse);
- const { isShow, xPos, yPos, path } = useSelector(selectOperationMenu);
-
- const dispatch = useDispatch();
-
- let html: JSX.Element = <>>;
- if (isSuccess) {
- html = (
-
-
-
- );
- } else if (isFetching) {
- html =
;
- } else if (isError) {
- html =
Ops~
;
- }
-
- // handle right click and show the menu
- // eslint-disable-next-line @typescript-eslint/no-shadow
- const handleShowMenu = (e: React.MouseEvent
, path: string[]) => {
- e.preventDefault();
-
- dispatch(
- updateOperationMenu({
- isShow: true,
- xPos: e.clientX,
- yPos: e.clientY,
- path,
- }),
- );
- };
-
- // click elsewhere except the operation menu, close it
- useEffect(() => {
- const event = () => {
- // only dispatch when it is shown
- if (isShow)
- dispatch(
- updateOperationMenu({
- isShow: false,
- xPos: 0,
- yPos: 0,
- path: [],
- }),
- );
- };
-
- document.addEventListener('click', event);
-
- return () => {
- // remove the previous event when isShow changed
- document.removeEventListener('click', event);
- };
- // need the isShow as a closure to rebind the event
- }, [isShow, dispatch]);
-
- return (
- <>
- {isShow && }
- {
- handleShowMenu(e, []);
- }}
- className="menu-container"
- style={{ width: menuCollapse ? '0%' : '18%' }}
- >
-
- {html}
-
- >
- );
-}
diff --git a/client/src/components/Menu/MenuItem.tsx b/client/src/components/Menu/MenuItem.tsx
new file mode 100644
index 0000000..0c3dc4f
--- /dev/null
+++ b/client/src/components/Menu/MenuItem.tsx
@@ -0,0 +1,205 @@
+import { ContextMenu } from 'primereact/contextmenu';
+import { MenuItem as PrimeMenuItem } from 'primereact/menuitem';
+import { FC, useContext, useMemo, useRef } from 'react';
+import { TreeItem, TreeRenderProps } from 'react-complex-tree';
+import { useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+
+import {
+ CreateNewDocItem,
+ RenameDocItem,
+ useCopyCutDoc,
+ useDeleteDoc,
+ useNewDocItem,
+ usePasteDoc,
+ useRenameDoc,
+} from './operations';
+import { MenuCtx, TreeItemData } from './type';
+
+import { selectOperationMenu } from '@/redux-feature/operationMenuSlice';
+import { normalizePath } from '@/utils/utils';
+
+type FileLinkProps = TreeRenderProps['renderItem'] extends
+ | ((props: infer P) => React.ReactElement | null)
+ | undefined
+ ? P
+ : never;
+
+type Command = 'copy' | 'cut' | 'delete' | 'newFile' | 'newFolder' | 'paste' | 'rename';
+
+interface ExpandLineProps {
+ depth: number;
+ isEnterMenu: boolean;
+ item: TreeItem;
+}
+/** To avoid using position:relative at the parent container of each item, otherwise, the offset top is not accurate */
+const ExpandLine: FC = ({ depth, isEnterMenu }) => {
+ if (depth === 0) return null;
+
+ // to fill in the parent expand lines when expanding
+ const expandLines = new Array(depth).fill(0).map((_, index) => {
+ const expandLineStyles: React.CSSProperties = {
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
+ left: `${(index + 1) * 0.5}rem`,
+ backgroundColor: 'var(--shallowTextColor)',
+ opacity: isEnterMenu ? 1 : 0,
+ };
+ return
;
+ });
+
+ return <>{expandLines}>;
+};
+
+export const MenuItem: FC = ({ title, arrow, context, item, depth }) => {
+ const { data, isFolder } = item;
+ const { path, newFile, newFolder, rename } = data;
+ const { isFocused } = context;
+ const { isEnterMenu } = useContext(MenuCtx);
+
+ const navigate = useNavigate();
+ const docPath = useMemo(() => normalizePath(path), [path]);
+
+ const { copyCutPaths, isCopy } = useSelector(selectOperationMenu);
+
+ const createNewDocItem = useNewDocItem();
+ const deleteDoc = useDeleteDoc();
+ const renameDoc = useRenameDoc();
+ const copyCutDoc = useCopyCutDoc();
+ const pasteDoc = usePasteDoc();
+
+ const cm = useRef(null);
+
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ const isSelected = useMemo(() => Boolean(context.isSelected), [context.isSelected]);
+ const isCopyCut = useMemo(() => {
+ return copyCutPaths.some((copyCutPath) => copyCutPath === normalizePath(path));
+ }, [copyCutPaths, path]);
+
+ const onClickCommand = async (command: Command) => {
+ if (command === 'newFile' || command === 'newFolder') {
+ await createNewDocItem(item, command === 'newFolder');
+ } else if (command === 'delete') {
+ await deleteDoc(item);
+ } else if (command === 'rename') {
+ await renameDoc(item);
+ } else if (command === 'copy' || command === 'cut') {
+ copyCutDoc([normalizePath(path)], command === 'copy');
+ } else if (command === 'paste') {
+ if (copyCutPaths.length) {
+ await pasteDoc({ pasteParentPathArr: path });
+ }
+ }
+ };
+
+ const items: PrimeMenuItem[] = [
+ {
+ label: 'New File',
+ icon: 'pi pi-file',
+ visible: isFolder,
+ command: () => {
+ void onClickCommand('newFile');
+ },
+ },
+ {
+ label: 'New Folder',
+ icon: 'pi pi-folder',
+ visible: isFolder,
+ command: () => {
+ void onClickCommand('newFolder');
+ },
+ },
+ {
+ label: 'Copy',
+ icon: 'pi pi-copy',
+ disabled: isCopyCut && isCopy,
+ command: () => {
+ void onClickCommand('copy');
+ },
+ },
+ {
+ label: 'Cut',
+ icon: 'pi pi-clipboard',
+ disabled: isCopyCut && !isCopy,
+ command: () => {
+ void onClickCommand('cut');
+ },
+ },
+ {
+ label: 'Paste',
+ icon: 'pi pi-clipboard',
+ disabled: !isFolder || !copyCutPaths.length,
+ command: () => {
+ void onClickCommand('paste');
+ },
+ },
+ {
+ label: 'Rename',
+ icon: 'pi pi-file-edit',
+ command: () => {
+ void onClickCommand('rename');
+ },
+ },
+ {
+ label: 'Delete',
+ icon: 'pi pi-trash',
+ command: () => {
+ void onClickCommand('delete');
+ },
+ },
+ ];
+
+ const onRightClick = (event: React.MouseEvent): void => {
+ if (cm.current) {
+ // clear the last one
+ document.body.click();
+ cm.current.show(event);
+ }
+ };
+
+ const itemStyles: React.CSSProperties = {
+ // eslint-disable-next-line @typescript-eslint/no-magic-numbers
+ paddingLeft: `${depth * 0.5 + 0.5}rem`,
+ };
+
+ let itemContent = null;
+ if (newFile || newFolder) {
+ itemContent = ;
+ } else if (rename) {
+ itemContent = ;
+ } else if (isFolder) {
+ itemContent = (
+
+ {arrow}
+ {title}
+
+ );
+ } else {
+ const to = (e: React.MouseEvent) => {
+ if (e.ctrlKey || e.metaKey) return;
+
+ void navigate(`/article/${docPath as string}`);
+ };
+
+ itemContent = (
+
+
+ {title}
+
+ );
+ }
+
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
+ const isOperationItem = newFile || newFolder || rename;
+ // context props will make opertion item blur and removed
+ const contextProps = isOperationItem
+ ? {}
+ : { ...context.interactiveElementProps, ...context.itemContainerWithoutChildrenProps };
+
+ return (
+
+
+
+ {itemContent}
+
+ );
+};
diff --git a/client/src/components/Menu/Refresh.tsx b/client/src/components/Menu/Refresh.tsx
deleted file mode 100644
index 8612249..0000000
--- a/client/src/components/Menu/Refresh.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import React from 'react';
-
-import { useRefreshDocsMutation } from '@/redux-api/docsApi';
-import Toast from '@/utils/Toast';
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function Refresh({ isFetching }: { isFetching: boolean }) {
- const [refreshDoc] = useRefreshDocsMutation();
-
- const clickRefresh = async (e: React.MouseEvent) => {
- e.stopPropagation();
-
- try {
- await refreshDoc().unwrap();
-
- Toast('refreshed', 'SUCCESS');
- } catch (err) {
- Toast('failed to refresh...', 'ERROR');
- }
- };
-
- return (
- void clickRefresh(e)}
- >
- refresh
-
- );
-}
diff --git a/client/src/components/Menu/Shortcut.tsx b/client/src/components/Menu/Shortcut.tsx
new file mode 100644
index 0000000..ed3edd8
--- /dev/null
+++ b/client/src/components/Menu/Shortcut.tsx
@@ -0,0 +1,56 @@
+import { FC, useContext, useMemo } from 'react';
+import { TreeRef } from 'react-complex-tree';
+
+import { useNewDocItem } from './operations';
+import { TreeDataCtx } from './type';
+
+import { Icon } from '@/components/Icon/Icon';
+
+interface ShortcutProps {
+ visible: boolean;
+ tree: React.RefObject;
+}
+
+export const Shortcut: FC = ({ visible, tree }) => {
+ const treeDataCtx = useContext(TreeDataCtx);
+
+ const createNewDocItem = useNewDocItem();
+
+ const hiddenStyle = useMemo(() => ({ visibility: visible ? 'visible' : 'hidden' }), [visible]);
+
+ const createNewDoc = async (isFolder: boolean) => {
+ if (!treeDataCtx) return;
+ await createNewDocItem(treeDataCtx.data.root, isFolder);
+ };
+
+ return (
+ {
+ e.stopPropagation();
+ }}
+ >
+ void createNewDoc(true)}
+ style={hiddenStyle}
+ toolTipContent="New Folder"
+ />
+ void createNewDoc(false)}
+ style={hiddenStyle}
+ toolTipContent="New File"
+ />
+ tree.current?.collapseAll()}
+ style={hiddenStyle}
+ toolTipContent="Collapse All"
+ />
+
+ );
+};
diff --git a/client/src/components/Menu/Subject.tsx b/client/src/components/Menu/Subject.tsx
deleted file mode 100644
index bb4db91..0000000
--- a/client/src/components/Menu/Subject.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import React, { useState } from 'react';
-
-import DocMenu from './Menu';
-
-import { DOC } from '@/redux-api/docsApiType';
-
-const styles = {
- rotation: {
- transform: 'rotate(180deg)',
- },
- scale: {
- transform: 'scaleY(1)',
- maxHeight: '1000px',
- },
- hide: {
- transform: 'scaleY(0)',
- maxHeight: '0',
- },
-};
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-function Subject({
- doc,
- handleShowMenu,
-}: {
- doc: DOC;
- handleShowMenu: (e: React.MouseEvent, path: string[]) => void;
-}) {
- const [expand, setExpand] = useState(false);
-
- const { rotation, scale, hide } = styles;
-
- return (
- <>
-
-
{
- setExpand((v) => !v);
- }}
- onContextMenu={(e) => {
- handleShowMenu(e, doc.path);
- }}
- >
- {doc.name}
-
- expand_less
-
-
-
-
-
-
- >
- );
-}
-
-export default React.memo(Subject);
diff --git a/client/src/components/Menu/operations.tsx b/client/src/components/Menu/operations.tsx
new file mode 100644
index 0000000..e7618ac
--- /dev/null
+++ b/client/src/components/Menu/operations.tsx
@@ -0,0 +1,629 @@
+/* eslint-disable @typescript-eslint/no-magic-numbers */
+import { QueryStatus } from '@reduxjs/toolkit/query';
+import { InputText } from 'primereact/inputtext';
+import { FC, useContext, useEffect, useRef, useState, ReactNode } from 'react';
+import { DraggingPosition, StaticTreeDataProvider, TreeItem, TreeItemIndex } from 'react-complex-tree';
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+
+import { TreeDataCtx, TreeItemData, TreeRefCtx } from './type';
+
+import {
+ useCreateDocMutation,
+ useDeleteDocMutation,
+ useModifyDocNameMutation,
+ useCopyCutDocMutation,
+ useLazyGetDocSubItemsQuery,
+} from '@/redux-api/docs';
+import { selectCurDocDirty, updateCurDoc } from '@/redux-feature/curDocSlice';
+import { selectOperationMenu, selectSelectedItemIds, updateCopyCut } from '@/redux-feature/operationMenuSlice';
+import { useCurPath } from '@/utils/hooks/docHooks';
+import { useDeleteTab, useRenameTab } from '@/utils/hooks/reduxHooks';
+import Toast from '@/utils/Toast';
+import { confirm, denormalizePath, isPathsRelated, normalizePath } from '@/utils/utils';
+
+export function deleteSubDocItem(
+ parentItem: TreeItem,
+ idx: TreeItemIndex,
+ treeData: Record>,
+) {
+ if (!parentItem.children?.length) return;
+
+ parentItem.children = parentItem.children.filter((childIdx) => childIdx !== idx);
+
+ const isFolder = treeData[idx].isFolder;
+ if (isFolder) {
+ treeData[idx].children?.forEach((childIdx) => {
+ deleteSubDocItem(treeData[idx], childIdx, treeData);
+ });
+ }
+
+ if (treeData[idx]) {
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
+ delete treeData[idx];
+ }
+
+ return true;
+}
+
+export const useUpdateSubDocItems = () => {
+ const [getDocSubItems] = useLazyGetDocSubItemsQuery();
+
+ return async (
+ parentItem: TreeItem,
+ treeData: Record>,
+ provider: StaticTreeDataProvider,
+ ) => {
+ const { data: newSubItems, status } = await getDocSubItems({ folderDocPath: parentItem.data.path.join('/') });
+ if (status !== QueryStatus.fulfilled) {
+ Toast.error('Failed to get sub doc items');
+ return;
+ }
+
+ newSubItems.forEach(({ id, name, isFile, path }) => {
+ const idx = normalizePath(path);
+ if (treeData[idx]) return;
+ treeData[idx] = {
+ index: idx,
+ canMove: true,
+ isFolder: !isFile,
+ children: [],
+ canRename: true,
+ data: { path, id, name, parentIdx: parentItem.index },
+ };
+ });
+ parentItem.children = newSubItems.map((d) => normalizePath(d.path));
+ await provider.onDidChangeTreeDataEmitter.emit([parentItem.index]);
+ };
+};
+
+export const useNewDocItem = () => {
+ const treeDataCtx = useContext(TreeDataCtx);
+ const treeRefCtx = useContext(TreeRefCtx);
+
+ return async (
+ item: TreeItem,
+ isFolder: boolean,
+ // for root menu to provide
+ treeProvider?: StaticTreeDataProvider,
+ renderData?: Record>,
+ ) => {
+ const treeData = renderData ?? treeDataCtx?.data;
+ const provider = treeProvider ?? treeDataCtx?.provider;
+ if (!treeData || !provider) return;
+
+ const id = `${Math.random()}`;
+ const newItem: TreeItem = {
+ index: id,
+ canMove: false,
+ isFolder,
+ children: [],
+ canRename: false,
+ data: {
+ parentIdx: item.index,
+ path: [],
+ id,
+ name: 'newDoc',
+ newFile: !isFolder,
+ newFolder: isFolder,
+ },
+ };
+ treeData[id] = newItem;
+ item.children?.unshift(id);
+
+ await provider.onDidChangeTreeDataEmitter.emit([item.index]);
+
+ treeRefCtx?.expandItem(item.index);
+ };
+};
+
+export const deleteDocItem = async (
+ treeProvider: StaticTreeDataProvider,
+ treeData: Record>,
+ deleteItems: {
+ parentItem: TreeItem;
+ idx: TreeItemIndex;
+ }[],
+) => {
+ deleteItems.forEach(({ parentItem, idx }) => {
+ if (!parentItem.children?.length) return;
+
+ parentItem.children = parentItem.children.filter((childIdx) => childIdx !== idx);
+ if (treeData[idx]) {
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
+ delete treeData[idx];
+ }
+ });
+
+ await treeProvider.onDidChangeTreeDataEmitter.emit(deleteItems.map(({ parentItem }) => parentItem.index));
+};
+
+export const useDeleteEffect = () => {
+ const { curPath } = useCurPath();
+
+ const { copyCutPaths } = useSelector(selectOperationMenu);
+ const isDirty = useSelector(selectCurDocDirty);
+
+ const dispatch = useDispatch();
+
+ const deleteTab = useDeleteTab();
+
+ return (deleteInfo: { filePath: string; isFile: boolean }[], force = false) => {
+ // clear the previous copy and cut
+ dispatch(
+ updateCopyCut({
+ copyCutPaths: copyCutPaths.filter((copyCutPath) => !deleteInfo.find((d) => d.filePath === copyCutPath)),
+ }),
+ );
+
+ // jump if the current doc is deleted or included in the deleted folder
+ if (deleteInfo.some(({ filePath, isFile }) => isPathsRelated(curPath, denormalizePath(filePath), isFile))) {
+ // clear global curDoc info
+ if (isDirty) {
+ dispatch(
+ updateCurDoc({
+ content: '',
+ isDirty: false,
+ contentIdent: '',
+ scrollTop: 0,
+ headings: [],
+ type: 'workspace',
+ }),
+ );
+ }
+ }
+
+ void deleteTab(
+ deleteInfo.map((d) => d.filePath),
+ { force },
+ );
+ };
+};
+
+export const useDeleteDoc = () => {
+ const [deleteDocMutation] = useDeleteDocMutation();
+ const treeDataCtx = useContext(TreeDataCtx);
+
+ const selectedItemIds = useSelector(selectSelectedItemIds);
+
+ const deleteEffect = useDeleteEffect();
+
+ return async (item: TreeItem) => {
+ if (!treeDataCtx) return;
+ const { provider, data: treeData } = treeDataCtx;
+
+ const deletedItems = selectedItemIds.includes(item.index as string)
+ ? selectedItemIds.map((id) => treeData[id])
+ : [item];
+ const isConfirm = await confirm({
+ message: `Are you sure to delete ${
+ deletedItems
+ .reduce((ret, i) => {
+ ret.push(i.data.name);
+ return ret;
+ }, [])
+ .join(', ') as string
+ }?`,
+ acceptLabel: 'Delete',
+ });
+ if (!isConfirm) return;
+
+ try {
+ const deletePayload = deletedItems.map((deletedItem) => {
+ const { isFolder } = deletedItem;
+ const { path } = deletedItem.data;
+ const deletedPath = normalizePath(path);
+
+ return { filePath: deletedPath, isFile: !isFolder };
+ });
+
+ await deleteDocMutation(deletePayload).unwrap();
+ deleteEffect(deletePayload, true);
+
+ await Promise.all(
+ deletedItems.map(async (i) => {
+ const parentItem = treeData[i.data.parentIdx];
+ if (!parentItem) return;
+
+ await deleteDocItem(provider, treeData, [{ parentItem, idx: i.index }]);
+ }),
+ );
+ Toast('deleted successfully!');
+ } catch (err) {
+ Toast.error((err as Error).message);
+ }
+ };
+};
+
+export const useRenameDoc = () => {
+ const treeDataCtx = useContext(TreeDataCtx);
+
+ return async (item: TreeItem) => {
+ item.data.rename = true;
+ // trigger to re-render as rename item component
+ await treeDataCtx?.provider.onDidChangeTreeDataEmitter.emit([item.index]);
+ };
+};
+
+export const useCopyCutDoc = () => {
+ const dispatch = useDispatch();
+ const selectedItemIds = useSelector(selectSelectedItemIds);
+
+ return (copyCutPaths: string[], isCopy: boolean) => {
+ dispatch(
+ updateCopyCut({
+ // if the copyCutPaths is not selected, then just copyCut the copyCutPaths
+ copyCutPaths: selectedItemIds.includes(copyCutPaths[0]) ? selectedItemIds : copyCutPaths,
+ isCopy,
+ }),
+ );
+ };
+};
+
+export const usePasteDoc = () => {
+ const treeDataCtx = useContext(TreeDataCtx);
+
+ const dispatch = useDispatch();
+ const { isCopy: globalIsCopy, copyCutPaths } = useSelector(selectOperationMenu);
+ const [getDocSubItems] = useLazyGetDocSubItemsQuery();
+
+ const [copyCutDoc] = useCopyCutDocMutation();
+
+ const updateSubDocItems = useUpdateSubDocItems();
+
+ const { navigate, curPath } = useCurPath();
+
+ return async ({
+ pasteParentPathArr,
+ providedTreeDataCtx,
+ providedIsCopy,
+ providedCopyCutPaths,
+ }: {
+ /** the path of the clicked item */
+ pasteParentPathArr: string[];
+ providedTreeDataCtx?: {
+ provider: StaticTreeDataProvider;
+ data: Record>;
+ };
+ providedIsCopy?: boolean;
+ /** normalized */
+ providedCopyCutPaths?: string[];
+ }) => {
+ const treeCtx = providedTreeDataCtx ?? treeDataCtx;
+ if (!treeCtx) return;
+ const { data: treeData, provider } = treeCtx;
+
+ const isCopy = providedIsCopy ?? globalIsCopy;
+
+ try {
+ const { data: pasteParentSubDocs, status } = await getDocSubItems({
+ folderDocPath: pasteParentPathArr.join('/'),
+ });
+ if (status !== QueryStatus.fulfilled) {
+ Toast.error('Failed to get sub doc items');
+ return;
+ }
+
+ const copyCutPayload = (providedCopyCutPaths ?? copyCutPaths)
+ .map((copyCutPath) => {
+ // file or dir
+ const copyCutDocName = treeData[copyCutPath].data.name;
+
+ const pasteParentPath = normalizePath(pasteParentPathArr);
+ const pasteDoc = treeData[pasteParentPath];
+ // click on file or not
+ const pastePath = pasteDoc
+ ? normalizePath(pasteParentPathArr.concat(copyCutDocName))
+ : // paster to root
+ copyCutDocName;
+
+ return {
+ copyCutPath,
+ pastePath,
+ isCopy,
+ isFile: !treeData[copyCutPath].isFolder,
+ };
+ })
+ .filter(({ pastePath, copyCutPath }) => {
+ // check if there is a repeat name
+ const hasDuplicatedName = pasteParentSubDocs.some((d) => d.name === treeData[copyCutPath].data.name);
+
+ if (hasDuplicatedName) {
+ Toast.warn(`${denormalizePath(pastePath).join('/') as string} already exist in this folder!`);
+ return false;
+ }
+ return true;
+ });
+
+ if (!copyCutPayload.length) return;
+
+ if (
+ !isCopy &&
+ !(await confirm({
+ message: `Are you sure to move ${
+ copyCutPayload
+ .reduce((ret, { copyCutPath }) => {
+ ret.push(denormalizePath(copyCutPath).join('/'));
+ return ret;
+ }, [])
+ .join(', ') as string
+ } to ${pasteParentPathArr.join('/') || 'root'}?`,
+ }))
+ ) {
+ return;
+ }
+
+ await copyCutDoc(copyCutPayload).unwrap();
+
+ // if it is cut and current path is included in it, redirect
+ const curDocPayload = copyCutPayload.find(({ copyCutPath, isFile }) =>
+ isPathsRelated(curPath, denormalizePath(copyCutPath), isFile),
+ );
+ if (!isCopy && curDocPayload) {
+ // if it is a file, direct to the paste path
+ if (curDocPayload.isFile) {
+ void navigate(`/article/${curDocPayload.pastePath as string}`);
+ } else {
+ const curFile = curPath.slice(
+ curPath.length - (curPath.length - denormalizePath(curDocPayload.copyCutPath).length),
+ );
+ void navigate(`/article/${normalizePath([curDocPayload.pastePath, ...curFile]) as string}`);
+ }
+ }
+
+ copyCutPayload.forEach(({ copyCutPath, isCopy: isCopyInPayload }) => {
+ if (!isCopyInPayload) {
+ const copyCutItem = treeData[copyCutPath];
+ if (copyCutItem) {
+ const copyCutParentItem = treeData[copyCutItem.data.parentIdx];
+ if (copyCutParentItem) {
+ deleteSubDocItem(copyCutParentItem, copyCutItem.index, treeData);
+ }
+ }
+ }
+ });
+ const pasteParentItem = treeData[pasteParentPathArr.length ? normalizePath(pasteParentPathArr) : 'root'];
+ void updateSubDocItems(pasteParentItem, treeData, provider);
+
+ Toast('updated successfully!');
+ return true;
+ } catch (err) {
+ Toast.error((err as Error).message);
+ } finally {
+ // clear the previous copy and cut
+ dispatch(
+ updateCopyCut({
+ isCopy: false,
+ copyCutPaths: [],
+ }),
+ );
+ }
+ };
+};
+
+export const useDropDoc = () => {
+ const [getDocSubItems] = useLazyGetDocSubItemsQuery();
+
+ const pasteDoc = usePasteDoc();
+
+ return async ({
+ items,
+ target,
+ treeData,
+ treeProvider,
+ }: {
+ items: TreeItem[];
+ target: DraggingPosition;
+ treeData: Record>;
+ treeProvider: StaticTreeDataProvider;
+ }) => {
+ const targetItemIdx = target.targetType === 'between-items' ? target.parentItem : target.targetItem;
+ const targetItem = treeData[targetItemIdx];
+
+ const isConfirm = await confirm({
+ message: 'Are you sure to move the items?',
+ });
+ if (!isConfirm) {
+ // reorder the moved items
+ await Promise.all(
+ items.map(async (item) => {
+ // original parent
+ const parentIdx = item.data.parentIdx;
+ const parentItem = treeData[parentIdx];
+ if (parentItem?.children) {
+ // make sure the order
+ const { data: subDocItems, status } = await getDocSubItems({
+ folderDocPath: parentItem.data.path.join('/'),
+ });
+ if (status !== QueryStatus.fulfilled) {
+ Toast.error('Failed to get sub doc items');
+ return;
+ }
+ parentItem.children = subDocItems.map((d) => normalizePath(d.path));
+ }
+ targetItem?.children?.splice(targetItem?.children?.indexOf(item.index), 1);
+ }),
+ );
+ return;
+ }
+
+ await pasteDoc({
+ pasteParentPathArr: targetItem.data.path,
+ providedTreeDataCtx: {
+ data: treeData,
+ provider: treeProvider,
+ },
+ providedIsCopy: false,
+ providedCopyCutPaths: items.map((item) => normalizePath(item.data.path)),
+ });
+ };
+};
+
+interface CreateNewDocProps {
+ /** the new item */
+ item: TreeItem;
+ arrow?: ReactNode;
+ style?: React.CSSProperties;
+}
+export const CreateNewDocItem: FC = ({ item, arrow, style }) => {
+ const { isFolder } = item;
+ const [newFileName, setNewFileName] = useState('');
+
+ const treeDataCtx = useContext(TreeDataCtx);
+
+ const [createDoc] = useCreateDocMutation();
+ const updateSubDocItems = useUpdateSubDocItems();
+ const navigate = useNavigate();
+
+ const inputRef = useRef(null);
+
+ const onChange = (e: React.ChangeEvent) => {
+ setNewFileName(e.target.value);
+ };
+ const onBlur = async () => {
+ if (!treeDataCtx) return;
+
+ const { provider, data: treeData } = treeDataCtx;
+ const parentItem = treeData[item.data.parentIdx];
+ if (!parentItem) return;
+
+ if (newFileName.trim()) {
+ const norPath = normalizePath(parentItem.data.path.concat(newFileName));
+
+ // check if there is a repeat name
+ // TODO: currently not support the same name of folder and file in the same dir
+ // may need to add a extra mark for doc idx keys
+ if (parentItem.children?.some((idx) => normalizePath(treeData[idx].data.path) === norPath)) {
+ Toast.warn('name already exist in this folder!');
+ return;
+ }
+
+ try {
+ await createDoc({ filePath: norPath, isFile: !isFolder }).unwrap();
+
+ // direct to this new doc if it is a file
+ if (!isFolder) void navigate(`/article/${norPath as string}`);
+
+ Toast('created successfully!');
+ } catch (err) {
+ Toast.error((err as Error).message);
+ }
+ }
+
+ await deleteDocItem(provider, treeData, [{ parentItem, idx: item.index }]);
+ await updateSubDocItems(parentItem, treeData, provider);
+ };
+
+ useEffect(() => {
+ if (inputRef.current) {
+ inputRef.current.focus();
+ }
+ }, []);
+
+ return (
+
+ {isFolder ? arrow : }
+ void onBlur()}
+ />
+
+ );
+};
+
+interface RenameDocProps {
+ /** the rename item */
+ item: TreeItem;
+ arrow?: ReactNode;
+ style?: React.CSSProperties;
+}
+export const RenameDocItem: FC = ({ item, arrow, style }) => {
+ const { isFolder } = item;
+ const [newFileName, setNewFileName] = useState(item.data.name);
+
+ const [modifyName] = useModifyDocNameMutation();
+ const updateSubDocItems = useUpdateSubDocItems();
+ const renameTab = useRenameTab();
+
+ const treeDataCtx = useContext(TreeDataCtx);
+
+ const inputRef = useRef(null);
+ const skipOnBlur = useRef(false);
+
+ const onChange = (e: React.ChangeEvent) => {
+ setNewFileName(e.target.value);
+ };
+
+ const onBlur = () => {
+ if (!treeDataCtx) return;
+
+ // wait for the click event to be triggered
+ setTimeout(() => {
+ if (skipOnBlur.current) {
+ skipOnBlur.current = false;
+ return;
+ }
+
+ const { provider } = treeDataCtx;
+ item.data.rename = false;
+ void provider.onDidChangeTreeDataEmitter.emit([item.index]);
+ }, 100);
+ };
+
+ const confirmRename = async () => {
+ if (newFileName.trim() === item.data.name) {
+ onBlur();
+ return;
+ }
+
+ skipOnBlur.current = true;
+
+ const { path, parentIdx } = item.data;
+
+ if (!treeDataCtx) return;
+ const { data: treeData, provider } = treeDataCtx;
+ const parentItem = treeData[parentIdx];
+ if (!parentItem) return;
+
+ const modifyPath = normalizePath(path);
+ const newPath = normalizePath(path.slice(0, -1).concat(newFileName));
+
+ if (parentItem.children?.some((idx) => normalizePath(treeData[idx].data.path) === newPath)) {
+ Toast.warn('the name is repeated!');
+ return;
+ }
+
+ try {
+ await modifyName({
+ filePath: modifyPath,
+ name: newFileName,
+ isFile: !isFolder,
+ }).unwrap();
+
+ void updateSubDocItems(parentItem, treeData, provider);
+
+ renameTab(normalizePath(path), newPath, !isFolder);
+
+ Toast('rename successfully!');
+ } catch (err) {
+ Toast.error((err as Error).message);
+ }
+ };
+
+ useEffect(() => {
+ if (inputRef.current) {
+ inputRef.current.focus();
+ }
+ }, []);
+
+ return (
+
+ {isFolder ? arrow : }
+
+ void confirmRename()}>
+
+ );
+};
diff --git a/client/src/components/Menu/renderer.tsx b/client/src/components/Menu/renderer.tsx
new file mode 100644
index 0000000..5265ae7
--- /dev/null
+++ b/client/src/components/Menu/renderer.tsx
@@ -0,0 +1,45 @@
+import { TreeRenderProps } from 'react-complex-tree';
+
+import { MenuItem } from './MenuItem';
+import { TreeItemData } from './type';
+
+export const renderItemArrow: TreeRenderProps['renderItemArrow'] = ({ context }) => {
+ return (
+
+
+
+ );
+};
+
+export const renderDragBetweenLine: TreeRenderProps['renderDragBetweenLine'] = ({
+ draggingPosition,
+ lineProps,
+}) => (
+
+);
+
+export const createRenderItem = (): TreeRenderProps['renderItem'] => {
+ return (props) => {
+ const { id } = props.item.data;
+
+ return (
+
+
+ {props.children}
+
+ );
+ };
+};
diff --git a/client/src/components/Menu/type.ts b/client/src/components/Menu/type.ts
new file mode 100644
index 0000000..71e4ffb
--- /dev/null
+++ b/client/src/components/Menu/type.ts
@@ -0,0 +1,23 @@
+import { createContext } from 'react';
+import { StaticTreeDataProvider, TreeEnvironmentRef, TreeItem, TreeItemIndex, TreeRef } from 'react-complex-tree';
+
+import { DocTreeNode } from '@/redux-api/docsApiType';
+
+export interface MetaData {
+ newFile?: boolean;
+ newFolder?: boolean;
+ rename?: boolean;
+}
+
+export type TreeItemData = MetaData & Pick & { parentIdx: TreeItemIndex };
+
+export const TreeDataCtx = createContext<{
+ provider: StaticTreeDataProvider;
+ data: Record>;
+} | null>(null);
+
+export const TreeRefCtx = createContext(null);
+export const TreeEnvRefCtx = createContext(null);
+export const MenuCtx = createContext<{
+ isEnterMenu: boolean;
+}>({ isEnterMenu: false });
diff --git a/client/src/components/OpenTab/OpenTab.less b/client/src/components/OpenTab/OpenTab.less
deleted file mode 100644
index e9a2d23..0000000
--- a/client/src/components/OpenTab/OpenTab.less
+++ /dev/null
@@ -1,50 +0,0 @@
-@import url("../../utils/utils.less");
-
-.open-tab-container {
- width: 100%;
- height: 100%;
- display: flex;
- align-items: flex-end;
- background-color: @backgroundColor;
- .open-tab {
- width: 10rem;
- height: 2.5rem;
- display: flex;
- justify-content: space-between;
- align-items: center;
- // background-color: #fff;
- padding: 0.5rem;
- cursor: pointer;
- .tab-name {
- max-width: 8rem;
- .text-overflow-omit();
- font-weight: bold;
- }
- .close-tag {
- cursor: pointer;
- position: relative;
- visibility: hidden;
- font-size: 20px;
- &:hover {
- &::after {
- content: "";
- position: absolute;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.2);
- border-radius: 5px;
- }
- }
- }
- &:hover {
- .close-tag {
- visibility: visible;
- }
- }
- }
- .active-tab {
- background-color: @boxColor;
- }
-}
diff --git a/client/src/components/OpenTab/OpenTab.scss b/client/src/components/OpenTab/OpenTab.scss
new file mode 100644
index 0000000..5f59c94
--- /dev/null
+++ b/client/src/components/OpenTab/OpenTab.scss
@@ -0,0 +1,99 @@
+@use "../../utils/utils.scss" as *;
+
+.open-tab-wrapper {
+ width: 100%;
+ overflow: hidden;
+ border-bottom: 1px solid $shadowColor;
+}
+
+.open-tab-scroller {
+ width: 100%;
+ border-bottom: 1px solid $shadowColor;
+ .p-scrollpanel-content {
+ padding: 0;
+ width: 100%;
+ }
+}
+
+.open-tab-container {
+ width: fit-content;
+ display: flex;
+ background-color: $backgroundColor;
+ color: $headerTextColor;
+ .tab-operations {
+ height: 100%;
+ width: 30px;
+ @include flex-center;
+ position: sticky;
+ left: 0;
+ box-shadow: 1px 0 0 0 $shadowColor;
+ background-color: $backgroundColor;
+ cursor: pointer;
+ }
+ .open-tab {
+ border-right: 1px solid $shadowColor;
+ width: fit-content;
+ height: 100%;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0.5rem;
+ cursor: pointer;
+ .tab-name {
+ margin: 0 0.5rem;
+ .tab-subtitle {
+ display: block;
+ font-size: 0.7em;
+ opacity: 0.6;
+ }
+ }
+ &.dirty .tab-name {
+ &::before {
+ content: "";
+ display: inline-block;
+ width: 6px;
+ height: 6px;
+ margin-right: 6px;
+ border-radius: 50%;
+ background-color: currentColor;
+ opacity: 0.9;
+ vertical-align: middle;
+ }
+ }
+ &.not-found-tab {
+ .tab-name {
+ text-decoration: line-through
+ }
+ }
+ .internal-icon {
+ font-size: 12px;
+ margin-right: 0.5rem;
+ }
+ .close-tag {
+ cursor: pointer;
+ position: relative;
+ visibility: hidden;
+ font-size: 20px;
+ &:hover {
+ &::after {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ background-color: rgba(0, 0, 0, 0.2);
+ border-radius: 5px;
+ }
+ }
+ }
+ &:hover {
+ .close-tag {
+ visibility: visible;
+ }
+ }
+ }
+ .active-tab {
+ background-color: $boxColor;
+ }
+}
diff --git a/client/src/components/OpenTab/OpenTab.tsx b/client/src/components/OpenTab/OpenTab.tsx
index c9431fa..67e966d 100644
--- a/client/src/components/OpenTab/OpenTab.tsx
+++ b/client/src/components/OpenTab/OpenTab.tsx
@@ -1,73 +1,156 @@
-import React, { useEffect } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
-import { useHistory } from 'react-router-dom';
+import { ContextMenu } from 'primereact/contextmenu';
+import { MenuItem } from 'primereact/menuitem';
+import { ScrollPanel } from 'primereact/scrollpanel';
+import { useEffect, useRef } from 'react';
+import { useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
-import { useGetNorDocsQuery } from '@/redux-api/docsApi';
-import { selectCurTabs, Tab, updateTabs } from '@/redux-feature/curDocSlice';
-import { useDeleteTab, useSaveDoc } from '@/utils/hooks/reduxHooks';
-import './OpenTab.less';
+import type { RootState } from '@/store';
+
+import { Icon } from '@/components/Icon/Icon';
+import { useGetSettingsQuery } from '@/redux-api/settings';
+import { selectCurTabs, Tab } from '@/redux-feature/curDocSlice';
+import { selectServerStatus, ServerStatus } from '@/redux-feature/globalOptsSlice';
+import { useDeleteTab } from '@/utils/hooks/reduxHooks';
+import Toast from '@/utils/Toast';
+import { denormalizePath, getDraftKey } from '@/utils/utils';
+
+import './OpenTab.scss';
+
+function getDisambiguations(tabs: Tab[]): Map {
+ const result = new Map();
+ const groups = new Map();
+
+ for (const tab of tabs) {
+ if (tab.type !== 'workspace') continue;
+ const parts = denormalizePath(tab.ident);
+ const baseName = parts[parts.length - 1];
+ if (!groups.has(baseName)) groups.set(baseName, []);
+ groups.get(baseName)!.push({ ident: tab.ident, parts });
+ }
+
+ for (const [, group] of groups) {
+ if (group.length <= 1) continue;
+ for (let depth = 2; depth <= Math.max(...group.map((g) => g.parts.length)); depth++) {
+ const suffixes = group.map((g) => g.parts.slice(-depth).slice(0, -1).join('/'));
+ if (new Set(suffixes).size === group.length) {
+ group.forEach((g, i) => result.set(g.ident, suffixes[i]));
+ break;
+ }
+ }
+ }
+
+ return result;
+}
// eslint-disable-next-line @typescript-eslint/naming-convention
export default function OpenTab() {
const curTabs = useSelector(selectCurTabs);
+ const { data: settings } = useGetSettingsQuery();
+ const draftKeys = useSelector((state: RootState) => Object.keys(state.drafts));
+ const serverStatus = useSelector(selectServerStatus);
- const { data: norDocs = {}, isSuccess } = useGetNorDocsQuery();
+ const cm = useRef(null);
- const router = useHistory();
-
- const dispatch = useDispatch();
+ const navigate = useNavigate();
const deleteTab = useDeleteTab();
- const saveDoc = useSaveDoc();
- useEffect(() => {
- if (!isSuccess) return;
+ const disambiguations = getDisambiguations(curTabs);
- const newTabs: Tab[] = [];
+ const activeTabRef = useRef(null);
+ const activeIdent = curTabs.find((t) => t.active)?.ident;
- curTabs.forEach(({ path, ...rest }) => {
- if (norDocs[path]) newTabs.push({ path, ...rest });
- });
+ const closeSavedTabs = () => {
+ const savedTabs = curTabs.filter((t) => !draftKeys.includes(getDraftKey(settings?.docRootPath, t.ident)));
+ void deleteTab(savedTabs.map((t) => t.ident));
+ };
- // select first file to be displayed
- const availablePaths = Object.keys(norDocs).filter((path) => norDocs[path].doc.isFile);
- if (newTabs.length === 0 && availablePaths.length !== 0) {
- newTabs.push({ path: availablePaths[0], active: true, scroll: 0 });
- router.push(`/article/${availablePaths[0]}`);
- }
+ const closeAllTabs = () => {
+ void deleteTab(curTabs.map((t) => t.ident));
+ };
- if (curTabs.length !== newTabs.length) {
- dispatch(updateTabs(newTabs));
- }
+ const items: MenuItem[] = [
+ { label: 'Close Saved', command: closeSavedTabs },
+ { label: 'Close All', command: closeAllTabs },
+ ];
- // eslint-disable-next-line
- }, [norDocs, dispatch, updateTabs]);
+ useEffect(() => {
+ activeTabRef.current?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
+ }, [activeIdent]);
return (
-
- {curTabs.map(({ path, active }) => (
-
{
- // auto save when switch
- saveDoc();
- router.push(`/article/${path as string}`);
- }}
- >
-
{`${path.split('-').slice(-1)[0] as string}.md`}
-
+
+
+
+ {
e.stopPropagation();
- deleteTab(path);
+ cm.current?.show(e);
}}
- >
- ×
-
+ />
- ))}
-
+ {curTabs.map(({ ident, active, type, title }) => {
+ const draftKey = getDraftKey(settings?.docRootPath, ident);
+ const isDirty = draftKeys.includes(draftKey);
+
+ const TabTitle = type === 'workspace' ? denormalizePath(ident).join('/') : title ?? ident;
+
+ const tabName = type === 'workspace' ? denormalizePath(ident).slice(-1)[0] : title ?? ident;
+ const subtitle = disambiguations.get(ident) ?? '';
+
+ const notFoundTab = serverStatus === ServerStatus.CANNOT_CONNECT && type === 'workspace';
+
+ const toDoc = () => {
+ if (notFoundTab) {
+ Toast.error(`Can not found the content of "${ident}".`);
+ return;
+ }
+
+ if (type === 'workspace') {
+ void navigate(`/article/${ident}`);
+ } else if (type === 'draft') {
+ void navigate(`/draft/${ident}`);
+ } else if (type === 'internal') {
+ void navigate(`/internal/${ident}`);
+ }
+ };
+
+ return (
+ {
+ toDoc();
+ }}
+ >
+
+ {type === 'internal' && }
+ {`${tabName}.md`}
+ {subtitle && {subtitle} }
+
+ {
+ e.stopPropagation();
+ void deleteTab([ident]);
+ }}
+ />
+
+ );
+ })}
+
+
);
}
diff --git a/client/src/components/OperationMenu/CreateDoc.tsx b/client/src/components/OperationMenu/CreateDoc.tsx
deleted file mode 100644
index 7c0f807..0000000
--- a/client/src/components/OperationMenu/CreateDoc.tsx
+++ /dev/null
@@ -1,73 +0,0 @@
-/* eslint-disable @typescript-eslint/no-magic-numbers */
-import React, { useState } from 'react';
-import { useHistory } from 'react-router-dom';
-
-import { useCreateDocMutation, useGetNorDocsQuery } from '@/redux-api/docsApi';
-import Toast from '@/utils/Toast';
-
-interface CreateDocProps {
- isFile: boolean;
- clickOnFile: boolean;
- path: string[];
-}
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function CreateDoc({
- isFile, // create a file or folder
- clickOnFile,
- path, // path that is clicked
-}: CreateDocProps) {
- const routerHistory = useHistory();
-
- const [inputName, setInputName] = useState('');
-
- const { data: norDocs = {} } = useGetNorDocsQuery();
- const [createDoc] = useCreateDocMutation();
-
- const createDocConfirm = async () => {
- // remove the last path if it is the clicked file name
- // add the new file name
- const convertedPath = clickOnFile
- ? path
- .slice(0, path.length - 1)
- .concat(inputName)
- .join('-')
- : path.concat(inputName).join('-');
-
- // check if there is a repeat name
- if (norDocs[convertedPath]) {
- Toast('name already exist in this folder!', 'WARNING', 3000);
- return;
- }
-
- try {
- await createDoc({ path: convertedPath, isFile: isFile }).unwrap();
- // hidden
- document.body.click();
-
- // direct to this new doc if it is a file
- if (isFile) routerHistory.push(`/article/${convertedPath}`);
-
- Toast('created successfully!', 'SUCCESS');
- } catch {
- Toast('failed to create...', 'ERROR');
- }
- };
-
- return (
-
- {
- setInputName(e.target.value);
- }}
- value={inputName}
- className="input"
- placeholder={`${isFile ? 'file name' : 'group name'}`}
- />
- void createDocConfirm()}>
- confirm
-
-
- );
-}
diff --git a/client/src/components/OperationMenu/ModifyName.tsx b/client/src/components/OperationMenu/ModifyName.tsx
deleted file mode 100644
index fe3c4bb..0000000
--- a/client/src/components/OperationMenu/ModifyName.tsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import React, { useCallback, useState } from 'react';
-
-import { useModifyDocNameMutation } from '@/redux-api/docsApi';
-import { DOC } from '@/redux-api/docsApiType';
-import { useModifyNameHandler } from '@/utils/hooks/docHooks';
-import Toast from '@/utils/Toast';
-
-interface ModifyNameProps {
- isFile: boolean;
- path: string[];
- siblings: DOC[];
-}
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function ModifyName({
- isFile, // a file or folder
- path, // path that is clicked with original name
- siblings, // for repeated name checking
-}: ModifyNameProps) {
- // initialized as the original name
- const [newName, setNewName] = useState(path.slice(-1)[0]);
-
- const [modifyName] = useModifyDocNameMutation();
- const modifyNameHandler = useModifyNameHandler();
-
- const modifyConfirm = useCallback(async () => {
- // original path that is being modified
- const modifyPath = path.join('-');
- const newPath = path.slice(0, -1).concat(newName).join('-');
-
- if (siblings.some((doc) => doc.path.join('-') === newPath)) {
- Toast('the name is repeated!', 'WARNING');
- return;
- }
-
- try {
- await modifyName({
- modifyPath,
- newName,
- isFile,
- }).unwrap();
-
- modifyNameHandler(path, newPath, isFile);
-
- Toast('modified successfully!', 'SUCCESS');
- } catch {
- Toast('failed to modify...', 'ERROR');
- }
- }, [path, isFile, siblings, newName, modifyNameHandler, modifyName]);
-
- return (
-
- {
- setNewName(e.target.value.replaceAll('-', '_'));
- }}
- value={newName}
- className="input"
- placeholder="new name"
- />
- void modifyConfirm()}>
- confirm
-
-
- );
-}
diff --git a/client/src/components/OperationMenu/OperationMenu.tsx b/client/src/components/OperationMenu/OperationMenu.tsx
deleted file mode 100644
index 6ad834d..0000000
--- a/client/src/components/OperationMenu/OperationMenu.tsx
+++ /dev/null
@@ -1,270 +0,0 @@
-/* eslint-disable @typescript-eslint/no-magic-numbers */
-import React, { useCallback, useEffect, useMemo, useState } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
-
-import CreateDoc from './CreateDoc';
-import ModifyName from './ModifyName';
-import Modal from '../../utils/Modal/Modal';
-
-import { useGetNorDocsQuery, useDeleteDocMutation, useCopyCutDocMutation } from '@/redux-api/docsApi';
-import { updateCopyCut, selectOperationMenu } from '@/redux-feature/operationMenuSlice';
-import { useDeleteHandler, useCopyCutHandler } from '@/utils/hooks/docHooks';
-import Toast from '@/utils/Toast';
-import './OperationMenu.less';
-
-interface Props {
- xPos: number;
- yPos: number;
- path: string[];
-}
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-function OperationMenu({ xPos, yPos, path }: Props) {
- const { data: norDocs = {} } = useGetNorDocsQuery();
-
- const copyCutHandler = useCopyCutHandler();
- const deleteHandler = useDeleteHandler();
-
- const { doc: norCurDoc, parent: curDocParent } = norDocs[path.join('-')] ?? {};
-
- const { copyPath, cutPath } = useSelector(selectOperationMenu);
-
- const [createFileShow, setCreateFileShow] = useState(false);
- const [createGroupShow, setCreateGroupShow] = useState(false);
- const [modifyNameShow, setModifyNameShow] = useState(false);
-
- const [deleteConfirmShow, setDeleteConfirmShow] = useState(false);
-
- const dispatch = useDispatch();
-
- const [deleteDocMutation] = useDeleteDocMutation();
- const [copyCutDoc] = useCopyCutDocMutation();
-
- const showManager = useMemo(
- () => ({
- createFile: setCreateFileShow,
- createGroup: setCreateGroupShow,
- modifyName: setModifyNameShow,
- }),
- [setCreateFileShow, setCreateGroupShow, setModifyNameShow],
- );
-
- // the click position is a file
- const clickOnFile = path.length === 0 ? false : norCurDoc.isFile;
-
- // show only one of the operations
- const showSelection = (key: keyof typeof showManager) => {
- Object.keys(showManager).forEach((item) => {
- if (item === key) showManager[item](true);
- else showManager[item as typeof key](false);
- });
- };
-
- // stop the menu propagating the click event
- const menuClick = (e: React.MouseEvent
) => {
- // for document.body only
- e.nativeEvent.stopImmediatePropagation();
- };
-
- /**
- * when click the copy, update the global copy path
- */
- const copyCutClick = (copyOrCut: 'COPY' | 'CUT') => {
- Toast('copying...', 'SUCCESS');
- // hidden the menu
- document.body.click();
-
- dispatch(
- updateCopyCut({
- copyPath: copyOrCut === 'COPY' ? path.join('-') : '',
- cutPath: copyOrCut === 'CUT' ? path.join('-') : '',
- }),
- );
- };
-
- const pasteClick = async () => {
- // hidden the menu
- document.body.click();
- // move the doc
-
- const copyCutPath = copyPath === '' ? cutPath : copyPath;
- const copyCutOnFile = norDocs[copyCutPath].doc.isFile;
- // file or dir
- const copyCutDocName = norDocs[copyCutPath].doc.name;
-
- // click on file or not
- const pastePath = norCurDoc
- ? norCurDoc.isFile
- ? path
- .slice(0, path.length - 1)
- .concat(copyCutDocName)
- .join('-')
- : path.concat(copyCutDocName).join('-')
- : copyCutDocName;
-
- // check if there is a repeat name
- if (norDocs[pastePath]) {
- Toast('name already exist in this folder!', 'WARNING', 3000);
- return;
- }
-
- try {
- await copyCutDoc({
- copyCutPath,
- pastePath: pastePath,
- isCopy: cutPath === '',
- isFile: copyCutOnFile,
- }).unwrap();
-
- copyCutHandler(copyCutPath, pastePath, copyPath === '', copyCutOnFile);
-
- Toast('updated!', 'SUCCESS');
- } catch {
- Toast('failed to copyCut...', 'ERROR');
- }
-
- // clear the previous copy and cut
- dispatch(
- updateCopyCut({
- copyPath: '',
- cutPath: '',
- }),
- );
- };
-
- const deleteDoc = async () => {
- try {
- await deleteDocMutation({
- path: path.join('-'),
- isFile: clickOnFile,
- }).unwrap();
- // hidden the menu
- document.body.click();
-
- Toast('deleted!', 'WARNING');
-
- // handle router issue
- deleteHandler(path.join('-'), clickOnFile);
- } catch {
- Toast('failed to delete...', 'ERROR');
- }
- };
-
- const hiddenAll = useCallback(() => {
- Object.keys(showManager).forEach((item) => {
- showManager[item as keyof typeof showManager](false);
- });
- }, [showManager]);
-
- useEffect(() => {
- // all hidden
- document.addEventListener('click', hiddenAll);
-
- return () => {
- document.removeEventListener('click', hiddenAll);
- };
- }, [hiddenAll]);
-
- useEffect(() => {
- // all hidden when click on other places
- hiddenAll();
- // eslint-disable-next-line
- }, [xPos, yPos]);
-
- // make show direction flexible to avoid overflow
- const menuPos =
- yPos + 300 >= document.body.clientHeight
- ? {
- left: xPos,
- bottom: document.body.clientHeight - yPos,
- }
- : {
- left: xPos,
- top: yPos,
- };
-
- return (
-
- {
- showSelection('createFile');
- }}
- >
- create new file
- {createFileShow && }
-
- {
- showSelection('createGroup');
- }}
- >
- create new group
- {createGroupShow && }
-
- {/* hidden when click from the root menu */}
- {
- copyCutClick('COPY');
- }}
- hidden={path.length === 0}
- >
- copy
-
- {/* hidden when click from the root menu */}
- {
- copyCutClick('CUT');
- }}
- hidden={path.length === 0}
- >
- cut
-
- {/* hidden when no copying or cutting*/}
- void pasteClick()} hidden={copyPath === '' && cutPath === ''}>
- paste
-
- {/* hidden when click from the root menu */}
- {
- showSelection('modifyName');
- }}
- hidden={path.length === 0}
- >
- rename
- {modifyNameShow && (
-
- )}
-
- {/* hidden when click from the root menu */}
- {
- setDeleteConfirmShow(true);
- }}
- hidden={path.length === 0}
- >
- delete
- {deleteConfirmShow && (
- void deleteDoc()}>
- {`Are you sure to delete the ${clickOnFile ? 'file' : 'group'}?`}
-
- )}
-
-
- );
-}
-
-export default React.memo(
- OperationMenu,
- // true will stop rendering
- (prevProps, nextProps) => prevProps.xPos === nextProps.xPos && prevProps.yPos === nextProps.yPos,
-);
diff --git a/client/src/components/OperationMenu/operationMenu.less b/client/src/components/OperationMenu/operationMenu.less
deleted file mode 100644
index 29c1c33..0000000
--- a/client/src/components/OperationMenu/operationMenu.less
+++ /dev/null
@@ -1,32 +0,0 @@
-@import url("../../utils/utils.less");
-
-.operation-menu {
- .operations {
- position: relative;
- .new-file-group-title {
- position: absolute;
- left: 110%;
- top: 0;
- padding: 0.3rem;
- background-color: #fff;
- border-radius: 5px;
- display: flex;
- justify-content: space-between;
- align-items: center;
- box-shadow: @shadow;
- input {
- margin: 0 0.3rem 0 0;
- width: 20rem;
- height: 2rem;
- font-size: 16px;
- &:focus {
- // box-shadow: 0 0 1px 2px rgba(0, 0, 0, 0.2);
- border-color: #62b5ec;
- }
- }
- button {
- background-color: @backgroundColor;
- }
- }
- }
-}
diff --git a/client/src/components/Outline/Outline.less b/client/src/components/Outline/Outline.less
deleted file mode 100644
index 78d16ee..0000000
--- a/client/src/components/Outline/Outline.less
+++ /dev/null
@@ -1,56 +0,0 @@
-@import url("../../utils/utils.less");
-
-.content-outline {
- width: fit-content;
- max-height: 34rem;
- white-space: nowrap;
- position: absolute;
- transition: @transition;
- z-index: 1500;
- background-color: #e6e6e6;
- border-radius: 5px;
- box-shadow: @shadow;
- padding: 0.5rem;
- .keywords-tags {
- max-width: 25rem;
- max-height: 8rem;
- display: flex;
- justify-content: flex-start;
- flex-wrap: wrap;
- overflow: auto;
- padding-right: 0.5rem;
- .keyword-anchor {
- padding: 0.5rem;
- margin: 0.3rem;
- background-color: #fff;
- font-size: 12px;
- font-weight: bold;
- border-radius: 5px;
- cursor: pointer;
- transform: scale(1);
- transition: @transition;
- color: black;
- &:hover {
- transform: scale(1.1);
- }
- }
- }
- .heading-anchors {
- width: 100%;
- max-height: 26rem;
- overflow: auto;
- padding-right: 0.5rem;
- .outline-title {
- min-width: 30rem;
- background-color: #fff;
- padding: 0.5rem;
- margin: 0.3rem 0;
- border-radius: 5px;
- cursor: pointer;
- opacity: 0.7;
- &:hover {
- opacity: 1;
- }
- }
- }
-}
diff --git a/client/src/components/Outline/Outline.scss b/client/src/components/Outline/Outline.scss
new file mode 100644
index 0000000..d7a95e3
--- /dev/null
+++ b/client/src/components/Outline/Outline.scss
@@ -0,0 +1,71 @@
+@use "@/utils/utils.scss" as *;
+
+$toolBarHeight: 32px;
+$outlineHeight: calc($editorHeight - $toolBarHeight - 0.5rem);
+
+.outline-wrapper {
+ width: 100%;
+ height: $outlineHeight;
+ overflow: hidden;
+}
+
+.outline-container {
+ height: $editorHeight;
+ overflow: hidden;
+ padding: 0;
+ .outlint-tool-bar {
+ height: 32px;
+ @include flex-center();
+ justify-content: flex-start;
+ padding: 0 0 0.5rem 0;
+ border-bottom: 1px solid $shadowColor;
+ }
+ .p-scrollpanel-content {
+ height: $outlineHeight;
+ overflow-x: hidden;
+ padding-left: 1rem;
+ }
+ .simple-tree-node {
+ .children {
+ border-left: 1px solid $shadowColor;
+ }
+ &.simple-tree-selected {
+ > .simple-tree-node-title {
+ color: $contentTextColor;
+ position: relative;
+ &::before {
+ position: absolute;
+ z-index: 100;
+ content: '';
+ top: 50%;
+ transform: translateY(-50%);
+ left: -1rem;
+ width: 3px;
+ height: 70%;
+ background-color: $anchorColor;
+ }
+ }
+ }
+ }
+ .outline-title {
+ @include flex-center();
+ margin-right: 20px;
+ .text {
+ flex: 1;
+ display: -webkit-box;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 1; /* line number, 1 for single line */
+ }
+ .p-tag {
+ font-size: 12px;
+ font-weight: normal;
+ width: 20px;
+ height: 20px;
+ margin-left: 5px;
+ border-radius: $borderRadius;
+ background: $shallowTextColor;
+ }
+ }
+}
diff --git a/client/src/components/Outline/Outline.tsx b/client/src/components/Outline/Outline.tsx
index cfe3d05..b3491a4 100644
--- a/client/src/components/Outline/Outline.tsx
+++ b/client/src/components/Outline/Outline.tsx
@@ -1,73 +1,158 @@
-/* eslint-disable @typescript-eslint/no-magic-numbers */
-import React, { useState, useRef } from 'react';
+import { ScrollPanel } from 'primereact/scrollpanel';
+import { Tag } from 'primereact/tag';
+import React, { FC, useCallback, useEffect, useImperativeHandle, useMemo, useReducer, useState } from 'react';
+import { useSelector } from 'react-redux';
-import OutlineContent from './OutlineContent';
-import './Outline.less';
+import { getTreeItem, SimpleTree, TreeNode } from '../SimpleTree/SimpleTree';
+
+import { Heading } from '@/redux-feature/curDocSlice';
+import { selectAnchor } from '@/redux-feature/globalOptsSlice';
+import { scrollToEditorAnchor, scrollToOutlineAnchor } from '@/utils/hooks/docHooks';
+import { updateLocationHash } from '@/utils/utils';
+import 'react-complex-tree/lib/style-modern.css';
+
+import './Outline.scss';
+
+export interface OutlineRef {
+ collapseAll: () => void;
+}
export interface OutlineProps {
- containerDom: HTMLElement;
- path: string[];
- posControl?: boolean;
+ headings: Heading[];
+ keywords?: string[];
+ onExpand?: (tree: TreeNode) => void;
+ ref?: React.RefObject;
}
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function Outline({ containerDom, path, posControl = true }: OutlineProps) {
- const [outlineShow, setOutlineShow] = useState(false);
- const timerRef = useRef();
- const [onOutline, setOnOutline] = useState(false);
- // if the mouse is on the outline, clear the timer
- if (onOutline && timerRef.current) clearTimeout(timerRef.current);
-
- const [outlinePos, setOutlinePos] = useState({
- x: 0,
- y: 0,
- });
-
- const showOutline = (e: React.MouseEvent) => {
- e.preventDefault();
- e.stopPropagation();
+/**
+ * sequential headings to tree structure
+ * @example
+ * ```ts
+ * headings = [
+ * {level: 1, text: 'Heading 1', id: 'heading-1'},
+ * {level: 2, text: 'Heading 2', id: 'heading-2'},
+ * {level: 1, text: 'Heading 3', id: 'heading-3'},
+ * ]
+ * ```
+ * @returns
+ * ```ts
+ * tree = [
+ * {level: 1, text: 'Heading 1', id: 'heading-1', children: [
+ * {level: 2, text: 'Heading 2', id: 'heading-2', children: []},
+ * ]},
+ * {level: 1, text: 'Heading 3', id: 'heading-3', children: []},
+ * ]
+ * ```
+ * */
+function headingsToTree(headings: Heading[]) {
+ const tree: TreeNode[] = [];
+ const parentHeadStack: TreeNode[] = [];
- const { clientX, clientY } = e;
+ for (const heading of headings) {
+ const { level, text, id } = heading;
+ const curNode: TreeNode = {
+ id,
+ level,
+ title: text,
+ parent: null,
+ children: [],
+ };
- if (posControl) {
- setOutlinePos({
- x: clientX,
- y: clientY,
- });
+ let lastParentHead: TreeNode | undefined = parentHeadStack[parentHeadStack.length - 1];
+ while (lastParentHead && lastParentHead.level >= level) {
+ parentHeadStack.pop();
+ lastParentHead = parentHeadStack[parentHeadStack.length - 1];
+ }
+
+ if (lastParentHead) {
+ lastParentHead.children.push(curNode);
+ curNode.parent = lastParentHead;
} else {
- setOutlinePos({ x: 0, y: 0 });
+ tree.push(curNode);
}
- setOutlineShow(true);
+ parentHeadStack.push(curNode);
+ }
+
+ return tree;
+}
+
+export const Outline: FC = ({ headings, onExpand, ref }) => {
+ const [, forceUpdate] = useReducer((x) => x + 1, 0);
+ const anchor = useSelector(selectAnchor);
+ const [selectedNode, setSelectedNode] = useState(null);
+
+ const treeData = useMemo(() => {
+ return headingsToTree(headings);
+ }, [headings]);
+
+ const selectToNode = useCallback(
+ (id: string) => {
+ const node = getTreeItem(treeData, id);
+ let parent = node?.parent;
+ while (parent) {
+ if (parent?.children?.length && parent.collapsed === true) {
+ parent.collapsed = false;
+ }
+ parent = parent.parent;
+ }
+ setSelectedNode(node ?? null);
+ },
+ [treeData],
+ );
+
+ const toAnchor = (e: React.MouseEvent, node: TreeNode) => {
+ e.stopPropagation();
+ const anchorHash = node.id.replace('outline-', '');
+ updateLocationHash(anchorHash);
+ scrollToEditorAnchor(anchorHash);
+
+ selectToNode(node.id);
};
- const mouseLeave = () => {
- timerRef.current = setTimeout(() => {
- setOutlineShow(false);
- }, 1000);
+ const collapse = (tree: TreeNode) => {
+ tree.collapsed = true;
+ tree.children.forEach((child) => {
+ collapse(child);
+ });
};
+ useImperativeHandle(ref, () => ({
+ collapseAll: () => {
+ treeData.forEach((treeNode) => {
+ collapse(treeNode);
+ });
+ forceUpdate();
+ },
+ }));
+
+ const getId = (node: TreeNode) => `outline-${node.id}`;
+
+ useEffect(() => {
+ selectToNode(anchor);
+ scrollToOutlineAnchor(anchor);
+ }, [anchor, selectToNode]);
+
return (
- // if it is rendered through reactDOM.render, the redux value will not be passed
- // as well as the router info
- <>
-
- {'segment'}
-
- {outlineShow && (
-
+ {treeData.map((treeNode) => (
+ (
+
+ )}
/>
- )}
- >
+ ))}
+
);
-}
+};
diff --git a/client/src/components/Outline/OutlineContainer.tsx b/client/src/components/Outline/OutlineContainer.tsx
new file mode 100644
index 0000000..6d59ec1
--- /dev/null
+++ b/client/src/components/Outline/OutlineContainer.tsx
@@ -0,0 +1,31 @@
+import { RefObject, useRef } from 'react';
+import { useSelector } from 'react-redux';
+
+import { Outline, OutlineRef } from './Outline';
+import { Icon } from '../Icon/Icon';
+
+import { selectCurHeadings } from '@/redux-feature/curDocSlice';
+
+export const OutlineContainer = () => {
+ const outlineRef = useRef(null);
+ const headings = useSelector(selectCurHeadings);
+
+ const onCallopseAll = () => {
+ outlineRef?.current?.collapseAll();
+ };
+
+ return (
+
+
+
+
+
} headings={headings} />
+
+ );
+};
diff --git a/client/src/components/Outline/OutlineContent.tsx b/client/src/components/Outline/OutlineContent.tsx
deleted file mode 100644
index 8b466bc..0000000
--- a/client/src/components/Outline/OutlineContent.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-/* eslint-disable @typescript-eslint/no-magic-numbers */
-/* eslint-disable @typescript-eslint/restrict-plus-operands */
-/* eslint-disable @typescript-eslint/no-empty-function */
-import React, { useEffect, useMemo } from 'react';
-import { createPortal } from 'react-dom';
-
-import PureOutline from './PureOutline';
-
-import type { OutlineProps } from './Outline';
-
-import { useGetDocQuery } from '@/redux-api/docsApi';
-
-// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
-type OutlineContentType = OutlineProps & {
- mousePos: { x: number; y: number };
- setOutlineShow?: React.Dispatch>;
- setOnOutline?: React.Dispatch>;
-};
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function OutlineContent({
- mousePos: { x, y },
- containerDom,
- setOutlineShow = () => {},
- setOnOutline = () => {},
- path,
-}: OutlineContentType) {
- const { data: curDoc = { headings: [] as string[], keywords: [] as string[] } } = useGetDocQuery(path.join('-'));
-
- const mouseEnterEvent = () => {
- setOnOutline(true);
- };
-
- const mouseLeaveEvent = () => {
- setOutlineShow(false);
- setOnOutline(false);
- };
-
- const contextEvent = (e: MouseEvent) => {
- e.preventDefault();
- // for native event
- e.stopImmediatePropagation();
- };
-
- const divDom = useMemo(() => {
- // eslint-disable-next-line @typescript-eslint/no-shadow
- const divDom = document.createElement('div');
-
- divDom.classList.add('content-outline');
-
- // show at proper position
- const left = x - containerDom.offsetLeft + containerDom.scrollLeft;
- const top = y - containerDom.offsetTop + containerDom.scrollTop;
-
- if (x + 350 >= document.body.clientWidth) {
- divDom.style.right = document.body.clientWidth - (x + containerDom.scrollLeft + 60) + 'px';
- } else {
- divDom.style.left = left + 'px';
- }
-
- if (y + 350 >= document.body.clientHeight) {
- divDom.style.bottom = document.body.clientHeight - (y + containerDom.scrollTop) + 'px';
- } else {
- divDom.style.top = top + 'px';
- }
- divDom.addEventListener('mouseenter', mouseEnterEvent);
- divDom.addEventListener('mouseleave', mouseLeaveEvent);
- divDom.addEventListener('contextmenu', contextEvent);
-
- return divDom;
- // eslint-disable-next-line
- }, []);
-
- useEffect(() => {
- containerDom?.appendChild(divDom);
- return () => {
- containerDom?.removeChild(divDom);
- divDom.removeEventListener('mouseenter', mouseEnterEvent);
- divDom.removeEventListener('mouseleave', mouseLeaveEvent);
- divDom.removeEventListener('contextmenu', contextEvent);
- };
- // eslint-disable-next-line
- }, [containerDom, divDom]);
-
- const { headings, keywords } = curDoc;
-
- const outlineDom =
- headings.length === 0 && keywords.length === 0 ? (
- no outline
- ) : (
-
- );
-
- return createPortal(outlineDom, divDom);
-}
diff --git a/client/src/components/Outline/PureOutline.tsx b/client/src/components/Outline/PureOutline.tsx
deleted file mode 100644
index 099477f..0000000
--- a/client/src/components/Outline/PureOutline.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import React from 'react';
-
-import { useEditorScrollToAnchor } from '@/utils/hooks/docHooks';
-import { updateLocationHash } from '@/utils/utils';
-
-export interface PureOutlineProps {
- headings: string[];
- keywords: string[];
- path: string[];
-}
-
-const headingSize = [
- {
- fontSize: '20px',
- fontWeight: 'bold',
- },
- {
- fontSize: '16px',
- fontWeight: 'bold',
- marginLeft: '1rem',
- },
- {
- fontSize: '14px',
- fontWeight: 'normal',
- marginLeft: '2rem',
- },
- {
- fontSize: '14px',
- fontWeight: 'normal',
- marginLeft: '3rem',
- },
-];
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function PureOutline({ headings, keywords, path = [] }: PureOutlineProps) {
- const scrollToAnchor = useEditorScrollToAnchor();
-
- const toAnchor = (e: React.MouseEvent, anchor: string) => {
- e.stopPropagation();
-
- const anchorDom = scrollToAnchor(anchor, path.join('-'));
- if (anchorDom) {
- // wait for after the scroll event to changed the hash
- setTimeout(() => {
- updateLocationHash(anchorDom.getAttribute('id') ?? '');
- // eslint-disable-next-line @typescript-eslint/no-magic-numbers
- }, 1000);
- }
- };
-
- return (
- <>
- {keywords.length !== 0 && (
-
- {keywords.map((keyword) => (
-
{
- toAnchor(e, keyword.replace(/\s/g, '-').toLowerCase());
- }}
- key={keyword}
- >
- {keyword}
-
- ))}
-
- )}
-
- {headings.length !== 0 && (
-
- {headings.map((title) => {
- const level = (title.match(/#+/gi) as string[])[0].length;
- const pureHeading = title.replace(/#+\s/g, '');
-
- return (
-
{
- toAnchor(e, pureHeading);
- }}
- style={{ ...(headingSize[level - 1] ?? {}), color: 'black' }}
- key={path.join('-') + title}
- >
- {pureHeading}
-
- );
- })}
-
- )}
- >
- );
-}
diff --git a/client/src/components/Settings/Settings.scss b/client/src/components/Settings/Settings.scss
new file mode 100644
index 0000000..74b9219
--- /dev/null
+++ b/client/src/components/Settings/Settings.scss
@@ -0,0 +1,58 @@
+@use '@/utils/utils.scss' as *;
+
+.settings-container {
+ width: 600px;
+ height: 500px;
+ .setting-item {
+ padding-top: 20px;
+ margin-bottom: 20px;
+ border-top: 1px solid $shadowColor;
+ }
+ .setting-label {
+ display: block;
+ font-size: 20px;
+ font-weight: bold;
+ margin-bottom: 10px;
+ }
+ .workspace-display {
+ font-size: 18px;
+ margin-bottom: 10px;
+ }
+ .p-chips {
+ width: 600px;
+ .p-chips-multiple-container {
+ padding: 5px 10px;
+ }
+ .p-chips-token {
+ font-size: 14px;
+ padding: 5px 10px;
+ }
+ }
+ .git-service-status {
+ margin-top: 10px;
+ .no-git-service, .git-service-status-active {
+ @include flex-center();
+ i {
+ margin-right: 5px;
+ color: red;
+ &.warn {
+ color: rgb(231, 231, 32);
+ }
+ }
+ justify-content: flex-start;
+ }
+ .git-service-status-active {
+ i {
+ color: green;
+ }
+ &-text {
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: bold;
+ &:hover {
+ color: $anchorColor;
+ }
+ }
+ }
+ }
+}
diff --git a/client/src/components/Settings/Settings.tsx b/client/src/components/Settings/Settings.tsx
new file mode 100644
index 0000000..966d458
--- /dev/null
+++ b/client/src/components/Settings/Settings.tsx
@@ -0,0 +1,120 @@
+import { Button } from 'primereact/button';
+import { Chips } from 'primereact/chips';
+import { InputText } from 'primereact/inputtext';
+import { FC, useEffect, useState } from 'react';
+
+import { FolderSelectorModal } from '@/components/FolderSelector/FolderSelector';
+import { useGetGitStatusQuery } from '@/redux-api/git';
+import { Settings } from '@/redux-api/settings';
+
+import './Settings.scss';
+
+export interface SettingsBoxProps {
+ settings: Settings;
+ onUpdateSettings?: (settings: Settings) => void;
+}
+
+export const SettingsBox: FC = ({ settings, onUpdateSettings }) => {
+ const [workspace, setWorkspace] = useState('');
+ const [ignoreDirs, setIgnoreDirs] = useState([]);
+ const [showFolderSelector, setShowFolderSelector] = useState(false);
+
+ const { data: { noGit: noGitSetup, remotes } = { noGit: true, remotes: [] }, isLoading: isLoadingGitStatus } =
+ useGetGitStatusQuery();
+
+ let gitInfoContent: React.ReactNode | null = null;
+ if (isLoadingGitStatus) {
+ gitInfoContent = ;
+ } else if (noGitSetup) {
+ gitInfoContent = (
+
+
+ No git service found
+
+ );
+ } else if (!remotes.length) {
+ gitInfoContent = (
+
+
+ Git remote not set
+
+ );
+ } else {
+ gitInfoContent = (
+
+
+ {
+ window.open(remotes[0].webUrl, '_blank');
+ }}
+ >
+ Git service
+
+
+ );
+ }
+
+ useEffect(() => {
+ setWorkspace(settings.docRootPath ?? '');
+ setIgnoreDirs(settings.ignoreDirs ?? []);
+ }, [settings]);
+
+ const handleModalHidden = () => {
+ setShowFolderSelector(false);
+ };
+
+ const updateSettings = (updatedSettings: Settings) => {
+ onUpdateSettings?.({ ...settings, ...updatedSettings });
+ };
+
+ const handleFolderSelectorConfirm = (selectedFolderPath: string) => {
+ setWorkspace(selectedFolderPath);
+ updateSettings({ docRootPath: selectedFolderPath });
+ };
+
+ return (
+
+
+ Workspace
+
+ {
+ setWorkspace(e.target.value);
+ updateSettings({ docRootPath: e.target.value });
+ }}
+ />
+ {
+ setShowFolderSelector(true);
+ }}
+ >
+
+
+
+
+
+
+
+ Ignore Directories
+ {
+ const newIgnoreDirs = e.value ?? [];
+ setIgnoreDirs(newIgnoreDirs);
+ updateSettings({ ignoreDirs: newIgnoreDirs });
+ }}
+ />
+
+
+ );
+};
diff --git a/client/src/components/SidePanel/SidePanel.less b/client/src/components/SidePanel/SidePanel.less
deleted file mode 100644
index 9a14992..0000000
--- a/client/src/components/SidePanel/SidePanel.less
+++ /dev/null
@@ -1,65 +0,0 @@
-@import url('../../utils/utils.less');
-@import url('../../components/Outline/Outline.less');
-
-.side-panel {
- display: flex;
- flex-direction: column;
- justify-content: flex-end;
- align-items: center;
- .operation-icon {
- width: 3rem;
- height: 3rem;
- border-radius: 50%;
- background-color: rgba(0, 0, 0, 0.2);
- display: flex;
- justify-content: center;
- align-items: center;
- cursor: pointer;
- box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.2);
- &:hover {
- background-color: @boxColor;
- }
- }
- .go-to-top {
- margin-bottom: 1.5rem;
- visibility: visible;
- transition: @transition;
- transition-duration: 0.3s;
- }
- .hidden {
- visibility: hidden;
- opacity: 0;
- }
- .side-outline {
- margin-bottom: 1.5rem;
- .hover-box();
- }
- .side-git {
- margin-bottom: 1.5rem;
- .hover-box();
- }
- .config-box {
- margin-bottom: 10rem;
- .hover-box();
- // .box {
- // transform: scale(1);
- // }
- }
- .hover-box {
- position: relative;
- .box {
- position: absolute;
- right: 130%;
- bottom: 50%;
- transition: @transition;
- transform: scale(0, 0);
- transform-origin: right bottom;
- cursor: default;
- }
- &:hover {
- .box {
- transform: scale(1);
- }
- }
- }
-}
diff --git a/client/src/components/SidePanel/SidePanel.tsx b/client/src/components/SidePanel/SidePanel.tsx
deleted file mode 100644
index 30f3b98..0000000
--- a/client/src/components/SidePanel/SidePanel.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import React, { useState } from 'react';
-import { useSelector } from 'react-redux';
-
-import ConfigBox from '../ConfigBox/ConfigBox';
-import GitBox from '../GitBox/GitBox';
-import PureOutline from '../Outline/PureOutline';
-
-import { useGetDocQuery } from '@/redux-api/docsApi';
-import { selectCurPath, selectCurScrollTop } from '@/redux-feature/curDocSlice';
-import ErrorBoundary from '@/utils/ErrorBoundary/ErrorBoundary';
-
-import './SidePanel.less';
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function SidePanel() {
- const curPath = useSelector(selectCurPath);
- const scrollTop = useSelector(selectCurScrollTop);
-
- const { data: curDoc = { headings: [] as string[], keywords: [] as string[] } } = useGetDocQuery(curPath);
-
- const { headings, keywords } = curDoc;
-
- const [configShow, setConfigShow] = useState(false);
-
- return (
-
- {
- const milkdownDom = document.getElementsByClassName('milkdown')[0];
- milkdownDom.scroll({ top: 0, behavior: 'smooth' });
- }}
- >
- north
-
-
-
- {
- setConfigShow(true);
- }}
- >
- settings
- {configShow && (
-
-
-
- )}
-
-
- );
-}
diff --git a/client/src/components/Sidebar/Sidebar.scss b/client/src/components/Sidebar/Sidebar.scss
new file mode 100644
index 0000000..06ed408
--- /dev/null
+++ b/client/src/components/Sidebar/Sidebar.scss
@@ -0,0 +1,28 @@
+@use "@/utils/utils.scss" as *;
+
+.sidebar-container {
+ width: 45px;
+ height: 100%;
+ background-color: $backgroundColor;
+ border: 1px solid transparent;
+ border-right-color: $shadowColor;
+ padding: 18px 0 0 0;
+ .icon-wrapper {
+ margin-bottom: 20px;
+ }
+}
+
+.sidebar-overlay {
+ &.p-overlaypanel {
+ background-color: $backgroundColor;
+ border-radius: $borderRadius;
+ box-shadow: $shadow;
+ transform: translate(40px, -32px);
+ .p-overlaypanel-content {
+ padding: 0;
+ }
+ &::before, &::after {
+ display: none;
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/components/Sidebar/Sidebar.tsx b/client/src/components/Sidebar/Sidebar.tsx
new file mode 100644
index 0000000..a636b77
--- /dev/null
+++ b/client/src/components/Sidebar/Sidebar.tsx
@@ -0,0 +1,150 @@
+import GitFlow from '@mui/icons-material/CommitOutlined';
+import MenuClose from '@mui/icons-material/MenuOpenOutlined';
+import MenuOpen from '@mui/icons-material/MenuOutlined';
+import SettingIcon from '@mui/icons-material/SettingsOutlined';
+import { Button } from 'primereact/button';
+import { Dialog } from 'primereact/dialog';
+import { OverlayPanel } from 'primereact/overlaypanel';
+import { FC, useEffect, useMemo, useRef, useState } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { useNavigate } from 'react-router-dom';
+
+import GitBox from '@/components/GitBox/GitBox';
+import { Icon } from '@/components/Icon/Icon';
+import { SettingsBox } from '@/components/Settings/Settings';
+import { Settings, useGetSettingsQuery, useUpdateSettingsMutation } from '@/redux-api/settings';
+import { updateTabs } from '@/redux-feature/curDocSlice';
+import { clearAllDrafts } from '@/redux-feature/draftsSlice';
+import { updateGlobalOpts, selectGlobalOpts, selectServerStatus, ServerStatus } from '@/redux-feature/globalOptsSlice';
+import ErrorBoundary from '@/utils/ErrorBoundary/ErrorBoundary';
+import Toast from '@/utils/Toast';
+import { isEqual } from '@/utils/utils';
+
+import './Sidebar.scss';
+
+export const Sidebar: FC = () => {
+ const [settingsShow, setSettingsShow] = useState(false);
+ const [settingsLoading, setSettingsLoading] = useState(false);
+ const [newSettings, setNewSettings] = useState({});
+
+ const { data: settings } = useGetSettingsQuery();
+ const [updateSettings] = useUpdateSettingsMutation();
+
+ const { menuCollapse } = useSelector(selectGlobalOpts);
+ const serverStatus = useSelector(selectServerStatus);
+ const isServerNotConnected = serverStatus === ServerStatus.CANNOT_CONNECT;
+ const dispatch = useDispatch();
+
+ const navigate = useNavigate();
+
+ const gitOverlayPanelRef = useRef(null);
+
+ const isSettingsChanged = useMemo(() => {
+ return !isEqual(newSettings, settings ?? {});
+ }, [newSettings, settings]);
+
+ useEffect(() => {
+ setNewSettings(settings ?? {});
+ }, [settings]);
+
+ const onClickGit = (event: React.MouseEvent) => {
+ gitOverlayPanelRef.current?.toggle(event);
+ };
+
+ const handleConfirmSettings = async () => {
+ try {
+ setSettingsLoading(true);
+
+ const isRootPathChanged = newSettings.docRootPath !== settings?.docRootPath;
+
+ await updateSettings(newSettings).unwrap();
+
+ Toast('Settings updated successfully');
+
+ if (isRootPathChanged) {
+ await navigate('/purePage');
+ dispatch(updateTabs([]));
+ // TODO: should save the tabs and drafts for each workspace
+ dispatch(clearAllDrafts());
+ }
+ } catch (e) {
+ Toast.error((e as Error).message);
+ } finally {
+ setSettingsLoading(false);
+ }
+ };
+
+ const onHideSettings = () => {
+ setSettingsShow(false);
+ setNewSettings(settings ?? {});
+ };
+
+ return (
+
+
}
+ footer={
+
+
+ {
+ void handleConfirmSettings();
+ }}
+ />
+
+ }
+ visible={settingsShow}
+ onHide={onHideSettings}
+ >
+
+
+
+
+
+ );
+};
diff --git a/client/src/components/SimpleTree/SimpleTree.scss b/client/src/components/SimpleTree/SimpleTree.scss
new file mode 100644
index 0000000..c339a44
--- /dev/null
+++ b/client/src/components/SimpleTree/SimpleTree.scss
@@ -0,0 +1,22 @@
+@use '@/utils/utils.scss' as *;
+
+.simple-tree-node {
+ &-title {
+ height: 32px;
+ cursor: pointer;
+ @include flex-center();
+ justify-content: flex-start;
+ color: $headerTextColor;
+ &:hover {
+ color: $contentTextColor;
+ }
+ .pi {
+ margin-left: 0.5rem;
+ cursor: pointer;
+ }
+ }
+ .children {
+ margin: 0;
+ padding-left: 1rem;
+ }
+}
diff --git a/client/src/components/SimpleTree/SimpleTree.tsx b/client/src/components/SimpleTree/SimpleTree.tsx
new file mode 100644
index 0000000..104b6d3
--- /dev/null
+++ b/client/src/components/SimpleTree/SimpleTree.tsx
@@ -0,0 +1,101 @@
+import { FC, useEffect, useState } from 'react';
+
+import './SimpleTree.scss';
+
+export interface TreeNode {
+ level: number;
+ title: string;
+ id: string;
+ children: TreeNode[];
+ parent: TreeNode | null;
+ /** if true, children of this tree node will be collapsed */
+ collapsed?: boolean;
+}
+
+export interface SimpleTreeProps {
+ value: TreeNode;
+ selectedNode?: TreeNode | null;
+ getId?: (node: TreeNode) => string;
+ renderTitle?: (tree: TreeNode) => React.ReactNode;
+ onClick?: (e: React.MouseEvent, node: TreeNode) => void;
+ onExpand?: (tree: TreeNode) => void;
+}
+
+export const SimpleTree: FC = ({ value, renderTitle, onClick, onExpand, getId, selectedNode }) => {
+ const [expand, setExpand] = useState(true);
+
+ useEffect(() => {
+ if (value.collapsed) {
+ setExpand(false);
+ } else if (value.collapsed === false) {
+ setExpand(true);
+ }
+ }, [value.collapsed]);
+
+ const childrenStyle: React.CSSProperties = {
+ height: expand ? 'auto' : 0,
+ display: expand ? 'block' : 'none',
+ overflow: expand ? 'visible' : 'hidden',
+ };
+
+ const isSelected = selectedNode?.id === value.id;
+
+ return (
+
+
{
+ onClick?.(e, value);
+ }}
+ >
+ {renderTitle ? renderTitle(value) : value.title}
+ {value.children.length > 0 && (
+ {
+ e.stopPropagation();
+ // toggle to expand state
+ if (!expand) {
+ onExpand?.(value);
+ value.collapsed = false;
+ } else {
+ value.collapsed = true;
+ }
+ setExpand(!expand);
+ }}
+ >
+ )}
+
+
+ {value.children.map((child) => (
+
+ ))}
+
+
+ );
+};
+
+export function getTreeItem(tree: TreeNode[], id: string): TreeNode | undefined {
+ for (const item of tree) {
+ if (item.id === id) {
+ return item;
+ }
+ const child = getTreeItem(item.children, id);
+ if (child) {
+ return child;
+ }
+ }
+}
diff --git a/client/src/components/SplitBar.tsx b/client/src/components/SplitBar.tsx
new file mode 100644
index 0000000..e0b085c
--- /dev/null
+++ b/client/src/components/SplitBar.tsx
@@ -0,0 +1,9 @@
+import { SplitProps } from '@uiw/react-split';
+
+export const SplitBar: SplitProps['renderBar'] = ({ onMouseDown, ...props }) => {
+ return (
+
+ );
+};
diff --git a/client/src/components/UploadImg/UploadImg.less b/client/src/components/UploadImg/UploadImg.scss
similarity index 100%
rename from client/src/components/UploadImg/UploadImg.less
rename to client/src/components/UploadImg/UploadImg.scss
diff --git a/client/src/components/UploadImg/UploadImg.tsx b/client/src/components/UploadImg/UploadImg.tsx
index b7ea582..0083e59 100644
--- a/client/src/components/UploadImg/UploadImg.tsx
+++ b/client/src/components/UploadImg/UploadImg.tsx
@@ -8,7 +8,7 @@ import Spinner from '@/utils/Spinner/Spinner';
import Toast from '@/utils/Toast';
import { getImgUrl } from '@/utils/utils';
-import './UploadImg.less';
+import './UploadImg.scss';
// eslint-disable-next-line @typescript-eslint/naming-convention
export default function UploadImg() {
@@ -72,7 +72,7 @@ export default function UploadImg() {
res();
});
}).catch((reason) => {
- Toast(String(reason), 'ERROR');
+ Toast.error(String(reason));
imgFile = null;
});
setIsFetching(false);
@@ -93,25 +93,17 @@ export default function UploadImg() {
const uploadImg = useCallback(async () => {
if (uploadFile.current == null) {
- Toast('please select or paste an image', 'WARNING');
+ Toast.warn('please select or paste an image');
return;
}
try {
- const resp = await uploadImgMutation({
+ await uploadImgMutation({
imgFile: uploadFile.current,
fileName: `${imgName}.${uploadFile.current.name.split('.')[1]}`,
}).unwrap();
-
- // eslint-disable-next-line @typescript-eslint/no-magic-numbers
- if (resp.err === 0 && resp.status === 200) {
- Toast(resp.message, 'SUCCESS');
- return;
- }
-
- Toast(resp.message, 'ERROR');
- } catch {
- Toast('failed to upload', 'ERROR');
+ } catch (err) {
+ Toast.error((err as Error).message);
}
}, [uploadFile, uploadImgMutation, imgName]);
diff --git a/client/src/constants.ts b/client/src/constants.ts
new file mode 100644
index 0000000..71d3f3c
--- /dev/null
+++ b/client/src/constants.ts
@@ -0,0 +1,5 @@
+export const APP_VERSION = __VERSION__;
+/** /repo-name/ or / */
+export const GITHUB_PAGES_BASE_PATH = __GITHUB_PAGES_BASE_PATH__;
+/** 3024 or 7024*/
+export const SERVER_PORT = __SERVER_PORT__;
diff --git a/client/src/index.tsx b/client/src/index.tsx
index e326cbb..407baf8 100644
--- a/client/src/index.tsx
+++ b/client/src/index.tsx
@@ -1,23 +1,23 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
+import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
-import App from './App';
-import * as serviceWorker from './serviceWorker';
+import { App } from './App';
import { store } from './store';
+import './theme.scss';
+import 'primeicons/primeicons.css';
+
const rootDom = document.getElementById('root');
-ReactDOM.render(
+// Base path for GitHub Pages (set via GITHUB_PAGES_BASE_PATH env var at build time)
+// This should match the publicPath in rsbuild.config.ts
+// eslint-disable-next-line @typescript-eslint/naming-convention
+const basePath = __GITHUB_PAGES_BASE_PATH__ || '';
+
+createRoot(rootDom!).render(
-
+
,
- rootDom,
);
-
-// If you want your app to work offline and load faster, you can change
-// unregister() to register() below. Note this comes with some pitfalls.
-// Learn more about service workers: https://bit.ly/CRA-PWA
-serviceWorker.unregister();
diff --git a/client/src/react-app-env.d.ts b/client/src/react-app-env.d.ts
deleted file mode 100644
index 6431bc5..0000000
--- a/client/src/react-app-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-///
diff --git a/client/src/redux-api/configApi.ts b/client/src/redux-api/configApi.ts
deleted file mode 100644
index b814a9f..0000000
--- a/client/src/redux-api/configApi.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { docsApi } from './docsApi';
-
-export interface ConfigType {
- docRootPath: string;
- ignoreDirs?: string[];
- region?: string;
- accessKeyId?: string;
- accessKeySecret?: string;
- bucket?: string;
-}
-
-const configApi = docsApi.injectEndpoints({
- endpoints: (builder) => ({
- getConfigs: builder.query<{ configs: ConfigType; err: 0 | 1; message: string }, void>({
- query: () => `/config/getConfigs`,
- providesTags: ['Configs'],
- keepUnusedDataFor: 300,
- }),
- updateConfigs: builder.mutation<{ err: 0 | 1; message: string }, ConfigType>({
- query: (configs) => {
- return {
- url: '/config/updateConfigs',
- method: 'POST',
- body: configs,
- };
- },
- invalidatesTags: ['Configs', 'Docs', 'GitStatus', 'ImgStore', 'Menu', 'NorDocs'],
- }),
- }),
-
- overrideExisting: false,
-});
-
-export const { useGetConfigsQuery, useUpdateConfigsMutation } = configApi;
diff --git a/client/src/redux-api/docs.ts b/client/src/redux-api/docs.ts
new file mode 100644
index 0000000..346192f
--- /dev/null
+++ b/client/src/redux-api/docs.ts
@@ -0,0 +1,145 @@
+/* eslint-disable @typescript-eslint/no-unsafe-return */
+import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
+
+import {
+ Article,
+ UpdateDocPayload,
+ CreateDocPayload,
+ DeleteDocPayload,
+ CopyCutDocPayload,
+ ModifyDocNamePayload,
+ CheckServerRes,
+ DocTreeNode,
+} from './docsApiType';
+import { transformResponse, transformErrorResponse } from './interceptor';
+
+const baseQuery = fetchBaseQuery({ baseUrl: `http://127.0.0.1:${__SERVER_PORT__}/api` });
+
+export const docsApi = createApi({
+ reducerPath: '/docs',
+ baseQuery: (args, api, extraOptions) => {
+ return baseQuery(args, api, extraOptions);
+ },
+ tagTypes: ['Menu', 'Article', 'GitStatus', 'ImgStore', 'ImgList', 'Configs'],
+
+ endpoints: (builder) => ({
+ checkServer: builder.query({
+ query: () => '/check',
+ transformErrorResponse,
+ transformResponse,
+ }),
+ getDocSubItems: builder.query({
+ query: (params = {}) => ({
+ url: '/docs/sub-items',
+ method: 'GET',
+ params: { ...params },
+ }),
+ providesTags: ['Menu'],
+ transformErrorResponse,
+ transformResponse,
+ }),
+ /**
+ * get one single doc
+ */
+ getDoc: builder.query({
+ query: (filePath) => `/docs/article?filePath=${filePath}`,
+ providesTags: (queryRet) => {
+ if (!queryRet) return [];
+ return [{ type: 'Article', filePath: queryRet.filePath }];
+ },
+ // the cached time when no subscribers
+ // 60s by default
+ keepUnusedDataFor: 60, // 300s 5min
+ // keepUnusedDataFor: 0, // no cache
+ transformErrorResponse,
+ transformResponse,
+ }),
+ /**
+ * create a file or folder
+ */
+ createDoc: builder.mutation({
+ query: (newDocInfo) => ({
+ url: '/docs/create',
+ method: 'POST',
+ body: newDocInfo,
+ }),
+ invalidatesTags: ['GitStatus'],
+ transformErrorResponse,
+ transformResponse,
+ }),
+ /** create a folder by absolute path */
+ createFolder: builder.mutation({
+ query: (createFolderInfo) => ({
+ url: '/docs/create-folder',
+ method: 'POST',
+ body: createFolderInfo,
+ }),
+ invalidatesTags: ['GitStatus', 'Menu'],
+ transformErrorResponse,
+ transformResponse,
+ }),
+ /**
+ * delete a file or folder
+ */
+ deleteDoc: builder.mutation({
+ query: (deleteInfo) => ({
+ url: '/docs/delete',
+ method: 'DELETE',
+ body: deleteInfo,
+ }),
+ invalidatesTags: ['GitStatus'],
+ transformErrorResponse,
+ transformResponse,
+ }),
+ copyCutDoc: builder.mutation({
+ query: (copyCutInfo) => ({
+ url: '/docs/copy-cut',
+ method: 'PATCH',
+ body: copyCutInfo,
+ }),
+ invalidatesTags: ['GitStatus'],
+ transformErrorResponse,
+ transformResponse,
+ }),
+ /**
+ * modify the doc name
+ */
+ modifyDocName: builder.mutation({
+ query: (modifyInfo) => ({
+ url: '/docs/update-name',
+ method: 'PATCH',
+ body: modifyInfo,
+ }),
+ invalidatesTags: ['GitStatus'],
+ transformErrorResponse,
+ transformResponse,
+ }),
+ /**
+ * update the content of a single doc
+ */
+ updateDoc: builder.mutation({
+ query: (updateDoc) => ({
+ url: '/docs/update',
+ method: 'PATCH',
+ body: updateDoc,
+ }),
+ // eslint-disable-next-line @typescript-eslint/naming-convention
+ invalidatesTags: (_, __, arg) => [{ type: 'Article', filePath: arg.filePath }, 'GitStatus'],
+ transformErrorResponse,
+ transformResponse,
+ }),
+ }),
+});
+
+export const {
+ useGetDocSubItemsQuery,
+ useLazyGetDocSubItemsQuery,
+ useGetDocQuery,
+ useUpdateDocMutation,
+ useCreateDocMutation,
+ useCreateFolderMutation,
+ useDeleteDocMutation,
+ useCopyCutDocMutation,
+ useModifyDocNameMutation,
+ useCheckServerQuery,
+} = docsApi;
diff --git a/client/src/redux-api/docsApi.ts b/client/src/redux-api/docsApi.ts
deleted file mode 100644
index 291cf2e..0000000
--- a/client/src/redux-api/docsApi.ts
+++ /dev/null
@@ -1,130 +0,0 @@
-import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
-
-// import { docNormalizer } from "@/utils/utils";
-import {
- GetDocsType,
- NormalizedDoc,
- GetDocType,
- UpdateDocPayload,
- CreateDocPayload,
- DeleteDocPayload,
- CopyCutDocPayload,
- ModifyDocNamePayload,
-} from './docsApiType';
-
-export const docsApi = createApi({
- reducerPath: '/docOperations',
- baseQuery: fetchBaseQuery({ baseUrl: '/' }),
- tagTypes: ['Docs', 'Menu', 'NorDocs', 'GitStatus', 'ImgStore', 'Configs'],
-
- endpoints: (builder) => ({
- /**
- * get the overall menu
- * */
- getDocMenu: builder.query({
- query: () => '/getDocs',
- providesTags: ['Menu'],
- keepUnusedDataFor: 60,
- }),
- /**
- * get the normalized docs
- * */
- getNorDocs: builder.query({
- query: () => '/getDocs/norDocs',
- providesTags: ['NorDocs'],
- keepUnusedDataFor: 60,
- }),
- /**
- * refresh the cache
- */
- refreshDocs: builder.mutation({
- query: () => ({
- url: '/menu/refresh',
- method: 'POST',
- }),
- invalidatesTags: ['Menu', 'NorDocs', 'Docs'],
- }),
- /**
- * get one single doc
- */
- getDoc: builder.query({
- query: (filePath) => `/getDocs/article?filePath=${filePath}`,
- providesTags: (queryRet = { content: '', filePath: '', headings: [], keywords: [] }) => [
- // specific to a certain doc
- { type: 'Docs', filePath: queryRet.filePath },
- ],
- // the cached time when no subscribers
- // 60s by default
- keepUnusedDataFor: 60, // 300s 5min
- }),
- /**
- * create a file or folder
- */
- createDoc: builder.mutation({
- query: (newDocInfo) => ({
- url: '/menu/createDoc',
- method: 'POST',
- body: newDocInfo,
- }),
- invalidatesTags: ['Menu', 'GitStatus', 'NorDocs'],
- }),
- /**
- * delete a file or folder
- */
- deleteDoc: builder.mutation({
- query: (deleteInfo) => ({
- url: '/menu/deleteDoc',
- method: 'DELETE',
- body: deleteInfo,
- }),
- invalidatesTags: ['Menu', 'GitStatus', 'NorDocs'],
- }),
- copyCutDoc: builder.mutation({
- query: (copyCutInfo) => ({
- url: '/menu/copyCutDoc',
- method: 'PATCH',
- body: copyCutInfo,
- }),
- invalidatesTags: ['Menu', 'GitStatus', 'NorDocs'],
- }),
- /**
- * modify the doc name
- */
- modifyDocName: builder.mutation({
- query: (modifyInfo) => ({
- url: '/editDoc/modifyName',
- method: 'PATCH',
- body: modifyInfo,
- }),
- invalidatesTags: ['Menu', 'GitStatus', 'NorDocs'],
- }),
- /**
- * update the content of a single doc
- */
- updateDoc: builder.mutation({
- query: (updateDoc) => ({
- url: '/editDoc',
- method: 'PATCH',
- body: updateDoc,
- }),
- // eslint-disable-next-line @typescript-eslint/naming-convention
- invalidatesTags: (_, __, arg) => [
- { type: 'Docs', filePath: arg.modifyPath },
- 'NorDocs', // for outline
- 'GitStatus',
- ],
- }),
- }),
-});
-
-export const {
- useGetDocMenuQuery,
- useGetNorDocsQuery,
- useGetDocQuery,
- useUpdateDocMutation,
- useCreateDocMutation,
- useDeleteDocMutation,
- useCopyCutDocMutation,
- useModifyDocNameMutation,
- useRefreshDocsMutation,
-} = docsApi;
diff --git a/client/src/redux-api/docsApiType.d.ts b/client/src/redux-api/docsApiType.d.ts
index e0e62a0..449b661 100644
--- a/client/src/redux-api/docsApiType.d.ts
+++ b/client/src/redux-api/docsApiType.d.ts
@@ -1,38 +1,11 @@
-export interface DOC {
- name: string;
+export interface DocTreeNode {
id: string;
+ name: string;
isFile: boolean;
- children: DOC[];
path: string[];
- headings: string[];
- keywords: string[];
}
-export type NormalizedDoc = Record<
- string,
- {
- doc: DOC;
- parent: DOC | DOC[];
- }
->;
-// export type NormalizedDoc = {
-// [path: string]: {
-// isFile: boolean;
-// siblings: string[];
-// children: string[];
-// name: string;
-// headings: string[];
-// keywords: string[];
-// };
-// };
-
-export type GetDocsType = DOC[];
-// export type GetDocsType = {
-// docs: DOC[];
-// norDocs: normalizedDoc;
-// };
-
-export interface GetDocType {
+export interface Article {
content: string;
filePath: string;
headings: string[];
@@ -40,26 +13,30 @@ export interface GetDocType {
}
export interface UpdateDocPayload {
- modifyPath: string;
- newContent: string;
+ filePath: string;
+ content: string;
}
export interface CreateDocPayload {
- path: string;
+ filePath: string;
isFile: boolean;
}
-export type DeleteDocPayload = CreateDocPayload;
+export type DeleteDocPayload = CreateDocPayload[];
-export interface CopyCutDocPayload {
+export type CopyCutDocPayload = {
copyCutPath: string;
pastePath: string;
isCopy: boolean;
isFile: boolean;
-}
+}[];
export interface ModifyDocNamePayload {
- modifyPath: string;
- newName: string;
+ filePath: string;
+ name: string;
isFile: boolean;
}
+
+export interface CheckServerRes {
+ version: string;
+}
diff --git a/client/src/redux-api/gitApi.ts b/client/src/redux-api/git.ts
similarity index 63%
rename from client/src/redux-api/gitApi.ts
rename to client/src/redux-api/git.ts
index 091ab40..237adc6 100644
--- a/client/src/redux-api/gitApi.ts
+++ b/client/src/redux-api/git.ts
@@ -1,17 +1,23 @@
-import { docsApi } from './docsApi';
+import { docsApi } from './docs';
+import { transformResponse, transformErrorResponse } from './interceptor';
export type StatusType = 'ADDED' | 'DELETED' | 'MODIFIED' | 'RENAME' | 'UNTRACKED';
export interface Change {
changePath: string;
status: StatusType;
}
+
+export interface RemoteInfo {
+ name: string;
+ url: string;
+ webUrl: string;
+}
export interface GitStatus {
- workSpace: Change[];
+ workspace: Change[];
staged: Change[];
changes: boolean;
noGit: boolean;
- err: 0 | 1;
- message: string;
+ remotes: RemoteInfo[];
}
export interface GitRestoreType {
staged: boolean;
@@ -24,53 +30,59 @@ export interface GitRestoreType {
const gitApi = docsApi.injectEndpoints({
endpoints: (builder) => ({
getGitStatus: builder.query({
- query: () => `/git/getStatus`,
+ query: () => `/git/status`,
providesTags: ['GitStatus'],
keepUnusedDataFor: 0, // no cache
+ transformResponse,
+ transformErrorResponse,
}),
- gitAdd: builder.mutation<{ err: 0 | 1; message: string }, string[]>({
+ gitAdd: builder.mutation({
query: (changePaths) => ({
url: '/git/add',
method: 'POST',
body: { changePaths },
}),
invalidatesTags: ['GitStatus'],
+ transformResponse,
+ transformErrorResponse,
}),
- gitRestore: builder.mutation<{ err: 0 | 1; message: string }, GitRestoreType>({
+ gitRestore: builder.mutation({
query: (restoreBody) => ({
url: '/git/restore',
method: 'POST',
body: restoreBody,
}),
- invalidatesTags: () => [
- 'NorDocs', // for outline
- 'GitStatus',
- 'Docs',
- 'Menu',
- ],
+ invalidatesTags: () => ['GitStatus', 'Menu', 'Article'],
+ transformResponse,
+ transformErrorResponse,
}),
- gitPull: builder.mutation<{ err: 0 | 1; message: string }, void>({
+ gitPull: builder.mutation({
query: () => ({
url: '/git/pull',
method: 'POST',
- // body: message,
}),
- invalidatesTags: ['GitStatus', 'Menu', 'Docs', 'NorDocs'],
+ invalidatesTags: ['GitStatus', 'Menu', 'Article'],
+ transformResponse,
+ transformErrorResponse,
}),
- gitCommit: builder.mutation<{ err: 0 | 1; message: string }, { title: string; body: string }>({
+ gitCommit: builder.mutation({
query: (message) => ({
url: '/git/commit',
method: 'POST',
body: message,
}),
invalidatesTags: ['GitStatus'],
+ transformResponse,
+ transformErrorResponse,
}),
- gitPush: builder.mutation<{ err: 0 | 1; message: string }, void>({
+ gitPush: builder.mutation({
query: () => ({
url: '/git/push',
method: 'POST',
}),
invalidatesTags: [],
+ transformResponse,
+ transformErrorResponse,
}),
}),
diff --git a/client/src/redux-api/imgStoreApi.ts b/client/src/redux-api/imgStoreApi.ts
index 2b8a849..830a1bd 100644
--- a/client/src/redux-api/imgStoreApi.ts
+++ b/client/src/redux-api/imgStoreApi.ts
@@ -1,4 +1,11 @@
-import { docsApi } from './docsApi';
+import { docsApi } from './docs';
+import { transformResponse, transformErrorResponse } from './interceptor';
+
+export interface ImgListItem {
+ fileName: string;
+ url: string;
+ createdTime: number;
+}
export interface ImgDataType {
/** object name on oss */
@@ -34,6 +41,22 @@ export interface RenameType {
const imgApi = docsApi.injectEndpoints({
endpoints: (builder) => ({
+ getImgList: builder.query({
+ query: () => '/imgs/list',
+ providesTags: ['ImgList'],
+ transformResponse,
+ transformErrorResponse,
+ }),
+ deleteWorkspaceImg: builder.mutation({
+ query: (fileName) => ({
+ url: '/imgs/delete',
+ method: 'DELETE',
+ body: { fileName },
+ }),
+ invalidatesTags: ['ImgList'],
+ transformResponse,
+ transformErrorResponse,
+ }),
getUploadHistory: builder.query<{ imgList: ImgDataType[]; err: 0 | 1; message: string }, void>({
query: () => `/imgStore/uploadHistory`,
providesTags: ['ImgStore'],
@@ -84,4 +107,11 @@ const imgApi = docsApi.injectEndpoints({
overrideExisting: false,
});
-export const { useGetUploadHistoryQuery, useUploadImgMutation, useDeleteImgMutation, useRenameImgMutation } = imgApi;
+export const {
+ useGetImgListQuery,
+ useDeleteWorkspaceImgMutation,
+ useGetUploadHistoryQuery,
+ useUploadImgMutation,
+ useDeleteImgMutation,
+ useRenameImgMutation,
+} = imgApi;
diff --git a/client/src/redux-api/interceptor.ts b/client/src/redux-api/interceptor.ts
new file mode 100644
index 0000000..01fa311
--- /dev/null
+++ b/client/src/redux-api/interceptor.ts
@@ -0,0 +1,11 @@
+import { FetchBaseQueryError } from '@reduxjs/toolkit/query';
+
+import { UnifyResponse } from '@/type';
+
+export const transformResponse = (response: UnifyResponse) => {
+ return response.data;
+};
+
+export const transformErrorResponse = (errorRes: FetchBaseQueryError) => {
+ return errorRes.data;
+};
diff --git a/client/src/redux-api/searchApi.ts b/client/src/redux-api/searchApi.ts
new file mode 100644
index 0000000..1406308
--- /dev/null
+++ b/client/src/redux-api/searchApi.ts
@@ -0,0 +1,51 @@
+import { docsApi } from './docs';
+import { transformResponse, transformErrorResponse } from './interceptor';
+
+export interface FileNameMatch {
+ name: string;
+ path: string[];
+}
+
+export interface LineMatch {
+ lineNumber: number;
+ lineContent: string;
+}
+
+export interface FileContentMatches {
+ name: string;
+ path: string[];
+ matches: LineMatch[];
+}
+
+const searchApi = docsApi.injectEndpoints({
+ endpoints: (builder) => ({
+ searchFiles: builder.query({
+ query: (q) => `/search/files?q=${encodeURIComponent(q)}`,
+ transformResponse,
+ transformErrorResponse,
+ }),
+ searchContent: builder.query<
+ FileContentMatches[],
+ {
+ q: string;
+ caseSensitive?: boolean;
+ includeFiles?: string;
+ excludeFiles?: string;
+ }
+ >({
+ query: ({ q, caseSensitive = false, includeFiles, excludeFiles }) => {
+ const params = new URLSearchParams();
+ params.set('q', q);
+ params.set('caseSensitive', String(caseSensitive));
+ if (includeFiles) params.set('includeFiles', includeFiles);
+ if (excludeFiles) params.set('excludeFiles', excludeFiles);
+ return `/search/content?${params.toString()}`;
+ },
+ transformResponse,
+ transformErrorResponse,
+ }),
+ }),
+ overrideExisting: false,
+});
+
+export const { useLazySearchFilesQuery, useLazySearchContentQuery } = searchApi;
diff --git a/client/src/redux-api/settings.ts b/client/src/redux-api/settings.ts
new file mode 100644
index 0000000..e378edc
--- /dev/null
+++ b/client/src/redux-api/settings.ts
@@ -0,0 +1,35 @@
+import { docsApi } from './docs';
+import { transformErrorResponse, transformResponse } from './interceptor';
+
+export interface Settings {
+ docRootPath?: string;
+ ignoreDirs?: string[];
+}
+
+const settingsApi = docsApi.injectEndpoints({
+ endpoints: (builder) => ({
+ getSettings: builder.query({
+ query: () => `/settings/`,
+ providesTags: ['Configs'],
+ keepUnusedDataFor: 300,
+ transformResponse,
+ transformErrorResponse,
+ }),
+ updateSettings: builder.mutation({
+ query: (settings) => {
+ return {
+ url: '/settings',
+ method: 'PATCH',
+ body: settings,
+ };
+ },
+ transformResponse,
+ transformErrorResponse,
+ invalidatesTags: ['Configs', 'Menu', 'GitStatus', 'ImgStore', 'Article'],
+ }),
+ }),
+
+ overrideExisting: false,
+});
+
+export const { useGetSettingsQuery, useUpdateSettingsMutation } = settingsApi;
diff --git a/client/src/redux-feature/curDocSlice.ts b/client/src/redux-feature/curDocSlice.ts
index d0d0c54..63fe4ec 100644
--- a/client/src/redux-feature/curDocSlice.ts
+++ b/client/src/redux-feature/curDocSlice.ts
@@ -4,49 +4,99 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { RootState } from '@/store';
import { localStore } from '@/utils/utils';
+export interface Heading {
+ text: string;
+ level: number;
+ id: string;
+}
+
+export type DocType = 'draft' | 'internal' | 'workspace';
+
export interface Tab {
- path: string;
active: boolean;
scroll: number;
+ type: DocType;
+ /**
+ * - workspace: the content path
+ * - draft: the id
+ * - internal: the id
+ */
+ ident: string;
+ title?: string;
}
-export interface CurDocUpdatePayLoad {
+export interface CurDocState {
+ /**
+ * - workspace: the content path
+ * - draft: the id
+ * - internal: the id
+ */
+ contentIdent: string;
+ /** for draft and internal */
+ title?: string;
content: string;
- contentPath: string;
isDirty: boolean;
- scrollTop?: number;
+ scrollTop: number;
+ headings: Heading[];
+ tabs: Tab[];
+ type: DocType;
}
+export type CurDocUpdatePayLoad = Partial & {
+ type: DocType;
+};
+
+const getDefaultValue = () => {
+ return {
+ content: '',
+ isDirty: false,
+ scrollTop: 0,
+ headings: [] as Heading[],
+ tabs: [],
+ type: 'workspace' as DocType,
+ contentIdent: '',
+ } as CurDocState;
+};
+
const initialTabs = JSON.parse(localStore('tabs').value ?? '[]') as Tab[];
export const curDocSlice = createSlice({
name: 'curDoc',
initialState: {
- content: '',
- contentPath: '',
- isDirty: false,
- scrollTop: 0,
+ ...getDefaultValue(),
tabs: initialTabs,
- },
+ } as CurDocState,
reducers: {
updateCurDoc: (state, action: PayloadAction) => {
- const { content, isDirty, contentPath, scrollTop } = action.payload;
+ const {
+ content = state.content,
+ isDirty = state.isDirty,
+ scrollTop = state.scrollTop,
+ headings = state.headings,
+ type = state.type,
+ contentIdent = state.contentIdent,
+ title = state.title,
+ } = action.payload;
+
// cant do this...
// state = action.payload;
+ state.type = type;
+ state.contentIdent = contentIdent;
+ state.title = title;
state.content = content;
state.isDirty = isDirty;
- state.contentPath = contentPath;
- if (scrollTop !== undefined) state.scrollTop = scrollTop;
+ state.headings = headings;
+ state.scrollTop = scrollTop;
// update active tab
// clear all first
state.tabs.forEach((tab) => (tab.active = false));
- const curTab = state.tabs.find((tab) => tab.path === contentPath);
+ const curTab = state.tabs.find((tab) => tab.ident === contentIdent) as Tab | undefined;
if (curTab) {
curTab.active = true;
} else {
- state.tabs.push({ path: contentPath, active: true, scroll: 0 });
+ state.tabs.push({ active: true, scroll: scrollTop, type, ident: contentIdent, title });
}
// update localStorage
@@ -63,7 +113,7 @@ export const curDocSlice = createSlice({
state.scrollTop = scrollTop;
// update the scroll record at tabs
- state.tabs.forEach((tab) => tab.path === state.contentPath && (tab.scroll = scrollTop));
+ state.tabs.forEach((tab) => tab.ident === state.contentIdent && (tab.scroll = scrollTop));
},
updateTabs: (state, action: PayloadAction) => {
@@ -71,18 +121,41 @@ export const curDocSlice = createSlice({
// update localStorage
const { setStore: storeTabs } = localStore('tabs');
storeTabs(JSON.stringify(state.tabs));
+
+ if (state.tabs.length === 0) {
+ Object.assign(state, getDefaultValue());
+ }
+ },
+
+ updateHeadings: (state, action: PayloadAction) => {
+ state.headings = action.payload;
+ },
+
+ clearCurDoc: (state) => {
+ Object.assign(state, {
+ ...getDefaultValue(),
+ tabs: state.tabs,
+ });
},
},
});
-export const { updateCurDoc, updateIsDirty, updateScrolling, updateTabs } = curDocSlice.actions;
+export const { updateCurDoc, updateIsDirty, updateScrolling, updateTabs, updateHeadings, clearCurDoc } =
+ curDocSlice.actions;
export const selectCurDoc = (state: RootState) => state.curDoc;
+export const selectCurHeadings = (state: RootState) => state.curDoc.headings;
export const selectCurContent = (state: RootState) => state.curDoc.content;
-export const selectCurPath = (state: RootState) => state.curDoc.contentPath;
+/**
+ * get the identifier of the current document
+ * for workspace, it is the content path
+ * for draft and internal, it is the id
+ */
+export const selectCurDocIdent = (state: RootState) => state.curDoc.contentIdent;
export const selectCurScrollTop = (state: RootState) => state.curDoc.scrollTop;
export const selectCurDocDirty = (state: RootState) => state.curDoc.isDirty;
export const selectCurTabs = (state: RootState) => state.curDoc.tabs;
export const selectCurActiveTab = (state: RootState) => state.curDoc.tabs.find((tab) => tab.active);
+export const selectCurDocType = (state: RootState) => state.curDoc.type;
export default curDocSlice.reducer;
diff --git a/client/src/redux-feature/draftsSlice.ts b/client/src/redux-feature/draftsSlice.ts
new file mode 100644
index 0000000..b52bd50
--- /dev/null
+++ b/client/src/redux-feature/draftsSlice.ts
@@ -0,0 +1,49 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+
+import type { Heading } from './curDocSlice';
+import type { RootState } from '@/store';
+
+export interface DraftEntry {
+ content: string;
+ headings?: Heading[];
+}
+
+export interface SetDraftPayload {
+ path: string;
+ content: string;
+ headings?: Heading[];
+}
+
+export type DraftsState = Record;
+
+const initialState: DraftsState = {};
+
+export const draftsSlice = createSlice({
+ name: 'drafts',
+ initialState,
+ reducers: {
+ setDraft: (state, action: PayloadAction) => {
+ const { path, content, headings } = action.payload;
+ state[path] = { content, headings };
+ },
+ clearDraft: (state, action: PayloadAction) => {
+ const path = action.payload;
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
+ delete state[path];
+ },
+ clearDrafts: (state, action: PayloadAction) => {
+ for (const path of action.payload) {
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
+ delete state[path];
+ }
+ },
+ clearAllDrafts: () => initialState,
+ },
+});
+
+export const { setDraft, clearDraft, clearDrafts, clearAllDrafts } = draftsSlice.actions;
+
+export const selectDraft = (path: string) => (state: RootState) => (state.drafts as DraftsState)[path];
+export const selectHasDraft = (path: string) => (state: RootState) => path in (state.drafts as DraftsState);
+
+export default draftsSlice.reducer;
diff --git a/client/src/redux-feature/globalOptsSlice.ts b/client/src/redux-feature/globalOptsSlice.ts
index 7c5ef5b..3424ad3 100644
--- a/client/src/redux-feature/globalOptsSlice.ts
+++ b/client/src/redux-feature/globalOptsSlice.ts
@@ -2,32 +2,46 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { RootState } from '@/store';
-import { localStore, changeTheme } from '@/utils/utils';
+import { localStore, changeTheme, Themes } from '@/utils/utils';
export interface GlobalOptsPayload {
- keys: ('anchor' | 'isDarkMode' | 'isEditorBlur' | 'menuCollapse' | 'mirrorCollapse' | 'readonly')[];
+ keys: (keyof GlobalOptsType)[];
values: (boolean | string)[];
}
+export enum ServerStatus {
+ RUNNING = 'RUNNING',
+ VERSION_MISMATCHE = 'VERSION_MISMATCHE',
+ CANNOT_CONNECT = 'CANNOT_CONNECT',
+}
+
export interface GlobalOptsType {
- isDarkMode: boolean;
+ theme: Themes;
readonly: boolean;
menuCollapse: boolean;
mirrorCollapse: boolean;
+ outlineCollapse: boolean;
isEditorBlur: boolean;
anchor: string;
+ narrowMode: boolean;
+ serverStatus: ServerStatus;
}
-const initialTheme = localStore('theme').value;
-changeTheme(initialTheme ? initialTheme : 'dark');
+const initialTheme = localStore('theme').value as Themes;
+changeTheme(initialTheme ? initialTheme : 'light');
+
+const initialNarrowMode = localStore('narrowMode').value;
const initialState: GlobalOptsType = {
- isDarkMode: initialTheme === 'dark' ? true : false,
+ theme: initialTheme ?? 'light',
readonly: true,
- menuCollapse: true,
+ menuCollapse: false,
mirrorCollapse: true,
+ outlineCollapse: false,
isEditorBlur: true,
anchor: '',
+ narrowMode: initialNarrowMode === 'true' ? true : false,
+ serverStatus: ServerStatus.RUNNING,
};
export const globalOptsSlice = createSlice({
@@ -42,32 +56,40 @@ export const globalOptsSlice = createSlice({
if (key === 'anchor') {
state[key] = values[idx] as string;
} else {
- state[key] = values[idx] as boolean;
+ state[key] = values[idx] as never;
}
- if (key === 'isDarkMode') {
+ if (key === 'theme') {
const { setStore: setTheme } = localStore('theme');
- changeTheme(!values[idx] ? 'light' : 'dark');
- setTheme(!values[idx] ? 'light' : 'dark');
+ changeTheme(values[idx] as Themes);
+ setTheme(values[idx] as Themes);
+ }
+
+ if (key === 'narrowMode') {
+ const { setStore: setNarrowMode } = localStore('narrowMode');
+ setNarrowMode(values[idx] ? 'true' : 'false');
}
}
},
+
+ updateServerStatus: (state, action: PayloadAction) => {
+ state.serverStatus = action.payload;
+ },
},
});
-export const { updateGlobalOpts } = globalOptsSlice.actions;
+export const { updateGlobalOpts, updateServerStatus } = globalOptsSlice.actions;
export const selectGlobalOpts = (state: RootState) => state.globalOpts;
-export const selectDocGlobalOpts = (state: RootState) => {
- const { isDarkMode, readonly, anchor } = state.globalOpts;
- return { isDarkMode, readonly, anchor };
-};
-
export const selectMenuCollapse = (state: RootState) => state.globalOpts.menuCollapse;
+export const selectMirrorCollapse = (state: RootState) => state.globalOpts.mirrorCollapse;
+export const selectOutlineCollapse = (state: RootState) => state.globalOpts.outlineCollapse;
-export const selectDarkMode = (state: RootState) => state.globalOpts.isDarkMode;
+export const selectTheme = (state: RootState) => state.globalOpts.theme;
export const selectReadonly = (state: RootState) => state.globalOpts.readonly;
export const selectAnchor = (state: RootState) => state.globalOpts.anchor;
+export const selectNarrowMode = (state: RootState) => state.globalOpts.narrowMode;
+export const selectServerStatus = (state: RootState) => state.globalOpts.serverStatus;
export default globalOptsSlice.reducer;
diff --git a/client/src/redux-feature/operationMenuSlice.ts b/client/src/redux-feature/operationMenuSlice.ts
index 804863e..0c74284 100644
--- a/client/src/redux-feature/operationMenuSlice.ts
+++ b/client/src/redux-feature/operationMenuSlice.ts
@@ -3,50 +3,45 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { RootState } from '@/store';
-export interface OperationMenuPayload {
- isShow: boolean;
- xPos: number;
- yPos: number;
- path: string[];
+export interface CopyCutPayload {
+ isCopy?: boolean;
+ /** nor paths */
+ copyCutPaths?: string[];
}
-export interface CopyCutPayload {
- copyPath: string;
- cutPath: string;
+export interface SelectedItemContext {
+ /** also is the doc nor path */
+ selectedItemIds: string[];
}
-const initialState = {
- isShow: false,
- xPos: 0,
- yPos: 0,
- path: [] as string[],
- copyPath: '',
- cutPath: '',
+const initialState: Required = {
+ isCopy: false,
+ copyCutPaths: [],
+ selectedItemIds: [],
};
export const operationMenuSlice = createSlice({
name: 'operationMenu',
initialState,
reducers: {
- updateOperationMenu: (state, action: PayloadAction) => {
- const { isShow, xPos, yPos, path } = action.payload;
-
- state.isShow = isShow;
- state.xPos = xPos;
- state.yPos = yPos;
- state.path = path;
- },
updateCopyCut: (state, action: PayloadAction) => {
- const { copyPath, cutPath } = action.payload;
-
- state.copyPath = copyPath;
- state.cutPath = cutPath;
+ const { isCopy, copyCutPaths } = action.payload;
+
+ if (isCopy !== undefined) {
+ state.isCopy = isCopy;
+ }
+ if (copyCutPaths !== undefined) {
+ state.copyCutPaths = copyCutPaths;
+ }
+ },
+ updateSelectedItems: (state, action: PayloadAction) => {
+ state.selectedItemIds = action.payload;
},
},
});
-export const { updateOperationMenu, updateCopyCut } = operationMenuSlice.actions;
+export const { updateCopyCut, updateSelectedItems } = operationMenuSlice.actions;
export const selectOperationMenu = (state: RootState) => state.operationMenu;
-
+export const selectSelectedItemIds = (state: RootState) => state.operationMenu.selectedItemIds;
export default operationMenuSlice.reducer;
diff --git a/client/src/serviceWorker.js b/client/src/serviceWorker.js
deleted file mode 100644
index bcc3ec1..0000000
--- a/client/src/serviceWorker.js
+++ /dev/null
@@ -1,128 +0,0 @@
-// This optional code is used to register a service worker.
-// register() is not called by default.
-
-// This lets the app load faster on subsequent visits in production, and gives
-// it offline capabilities. However, it also means that developers (and users)
-// will only see deployed updates on subsequent visits to a page, after all the
-// existing tabs open on the page have been closed, since previously cached
-// resources are updated in the background.
-
-// To learn more about the benefits of this model and instructions on how to
-// opt-in, read https://bit.ly/CRA-PWA
-
-const isLocalhost = Boolean(
- window.location.hostname === 'localhost' ||
- // [::1] is the IPv6 localhost address.
- window.location.hostname === '[::1]' ||
- // 127.0.0.1/8 is considered localhost for IPv4.
- window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/),
-);
-
-export function register(config) {
- if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
- // The URL constructor is available in all browsers that support SW.
- const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
- if (publicUrl.origin !== window.location.origin) {
- // Our service worker won't work if PUBLIC_URL is on a different origin
- // from what our page is served on. This might happen if a CDN is used to
- // serve assets; see https://github.com/facebook/create-react-app/issues/2374
- return;
- }
-
- window.addEventListener('load', () => {
- const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
-
- if (isLocalhost) {
- // This is running on localhost. Let's check if a service worker still exists or not.
- checkValidServiceWorker(swUrl, config);
-
- // Add some additional logging to localhost, pointing developers to the
- // service worker/PWA documentation.
- navigator.serviceWorker.ready.then(() => {
- console.log(
- 'This web app is being served cache-first by a service ' +
- 'worker. To learn more, visit https://bit.ly/CRA-PWA',
- );
- });
- } else {
- // Is not localhost. Just register service worker
- registerValidSW(swUrl, config);
- }
- });
- }
-}
-
-function registerValidSW(swUrl, config) {
- navigator.serviceWorker
- .register(swUrl)
- .then((registration) => {
- registration.onupdatefound = () => {
- const installingWorker = registration.installing;
- if (installingWorker == null) {
- return;
- }
- installingWorker.onstatechange = () => {
- if (installingWorker.state === 'installed') {
- if (navigator.serviceWorker.controller) {
- // At this point, the updated precached content has been fetched,
- // but the previous service worker will still serve the older
- // content until all client tabs are closed.
- console.log(
- 'New content is available and will be used when all ' +
- 'tabs for this page are closed. See https://bit.ly/CRA-PWA.',
- );
-
- // Execute callback
- if (config && config.onUpdate) {
- config.onUpdate(registration);
- }
- } else {
- // At this point, everything has been precached.
- // It's the perfect time to display a
- // "Content is cached for offline use." message.
- console.log('Content is cached for offline use.');
-
- // Execute callback
- if (config && config.onSuccess) {
- config.onSuccess(registration);
- }
- }
- }
- };
- };
- })
- .catch((error) => {
- console.error('Error during service worker registration:', error);
- });
-}
-
-function checkValidServiceWorker(swUrl, config) {
- // Check if the service worker can be found. If it can't reload the page.
- fetch(swUrl)
- .then((response) => {
- // Ensure service worker exists, and that we really are getting a JS file.
- const contentType = response.headers.get('content-type');
- if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) {
- // No service worker found. Probably a different app. Reload the page.
- navigator.serviceWorker.ready.then((registration) => {
- registration.unregister().then(() => {
- window.location.reload();
- });
- });
- } else {
- // Service worker found. Proceed as normal.
- registerValidSW(swUrl, config);
- }
- })
- .catch(() => {
- console.log('No internet connection found. App is running in offline mode.');
- });
-}
-
-export function unregister() {
- if ('serviceWorker' in navigator) {
- navigator.serviceWorker.ready.then((registration) => {
- registration.unregister();
- });
- }
-}
diff --git a/client/src/store.ts b/client/src/store.ts
index f95c255..09e1f4f 100644
--- a/client/src/store.ts
+++ b/client/src/store.ts
@@ -1,7 +1,8 @@
import { configureStore } from '@reduxjs/toolkit';
-import { docsApi } from './redux-api/docsApi';
+import { docsApi } from './redux-api/docs';
import curDocReducer from './redux-feature/curDocSlice';
+import draftsReducer from './redux-feature/draftsSlice';
import globalOptsReducer from './redux-feature/globalOptsSlice';
import operationMenuReducer from './redux-feature/operationMenuSlice';
@@ -9,6 +10,7 @@ export const store = configureStore({
reducer: {
globalOpts: globalOptsReducer,
curDoc: curDocReducer,
+ drafts: draftsReducer,
operationMenu: operationMenuReducer,
[docsApi.reducerPath]: docsApi.reducer,
},
diff --git a/client/src/theme.scss b/client/src/theme.scss
new file mode 100644
index 0000000..d8c1242
--- /dev/null
+++ b/client/src/theme.scss
@@ -0,0 +1,145 @@
+@use 'sass:color';
+
+body {
+ --font-family: 'Rubik', sans-serif;
+ --code-font-family: monospace;
+}
+
+.milkdown {
+ --crepe-font-title: 'Rubik', sans-serif;
+ --crepe-font-default: 'Rubik', sans-serif;
+ --crepe-font-code: 'Rubik', sans-serif;
+}
+
+.light {
+ --backgroundColor: white;
+ --boxColor: #f3f5f6;
+ --headerTextColor: #494E59;
+ --contentTextColor: black;
+ --shadowColor: rgba(74, 74, 78, .12);
+ --shallowTextColor: #9D9FA6;
+ --hoverBgcColor: #e9e7ea;
+ --selectedBgcColor: #D3D7DC;
+ --buttonBgcColor: oklch(90% .01 258.338);
+ --buttonHoverBgcColor: oklch(87.2% .01 258.338);
+ --anchorColor: #506cc9;
+ --hoverBorderColor: #3b82f6;
+
+ .milkdown {
+ --crepe-color-background: white;
+
+ --crepe-color-on-background: #1b1c1d;
+ --crepe-color-surface: #f8f9ff;
+ --crepe-color-surface-low: #f2f3fa;
+ --crepe-color-on-surface: #191c20;
+ --crepe-color-on-surface-variant: #43474e;
+ --crepe-color-outline: #1b1c1d;
+ --crepe-color-primary: #37618e;
+ --crepe-color-secondary: #d7e3f8;
+ --crepe-color-on-secondary: #101c2b;
+ --crepe-color-inverse: #2e3135;
+ --crepe-color-on-inverse: #eff0f7;
+ --crepe-color-inline-code: #ba1a1a;
+ --crepe-color-error: #ba1a1a;
+ --crepe-color-hover: #eceef4;
+ --crepe-color-selected: #bbbcbf;
+ --crepe-color-inline-area: #d8dae0;
+
+ --crepe-shadow-1:
+ 0px 1px 3px 1px rgba(0, 0, 0, 0.15), 0px 1px 2px 0px rgba(0, 0, 0, 0.3);
+ --crepe-shadow-2:
+ 0px 2px 6px 2px rgba(0, 0, 0, 0.15), 0px 1px 2px 0px rgba(0, 0, 0, 0.3);
+ }
+}
+
+.soft {
+ @import 'primereact/resources/themes/lara-light-blue/theme.css';
+ --backgroundColor: #e6e6e6;
+ --boxColor: #fff;
+ --headerTextColor: #494E59;
+ --contentTextColor: black;
+ --shadowColor: rgba(74, 74, 78, .12);
+ --shallowTextColor: #9D9FA6;
+ --hoverBgcColor: #D3D7DC;
+ --selectedBgcColor: #f3f5f6;
+ --buttonBgcColor: oklch(90% .01 258.338);
+ --buttonHoverBgcColor: oklch(87.2% .01 258.338);
+ --anchorColor: #506cc9;
+ --hoverBorderColor: #3b82f6;
+
+ .milkdown {
+ --crepe-color-background: #e6e6e6;
+
+ --crepe-color-on-background: #1b1c1d;
+ --crepe-color-surface: #f8f9ff;
+ --crepe-color-surface-low: #f2f3fa;
+ --crepe-color-on-surface: #191c20;
+ --crepe-color-on-surface-variant: #43474e;
+ --crepe-color-outline: #73777f;
+ --crepe-color-primary: #37618e;
+ --crepe-color-secondary: #d7e3f8;
+ --crepe-color-on-secondary: #101c2b;
+ --crepe-color-inverse: #2e3135;
+ --crepe-color-on-inverse: #eff0f7;
+ --crepe-color-inline-code: #ba1a1a;
+ --crepe-color-error: #ba1a1a;
+ --crepe-color-hover: #eceef4;
+ --crepe-color-selected: #bbbcbf;
+ --crepe-color-inline-area: #d8dae0;
+
+ --crepe-shadow-1:
+ 0px 1px 3px 1px rgba(0, 0, 0, 0.15), 0px 1px 2px 0px rgba(0, 0, 0, 0.3);
+ --crepe-shadow-2:
+ 0px 2px 6px 2px rgba(0, 0, 0, 0.15), 0px 1px 2px 0px rgba(0, 0, 0, 0.3);
+ }
+}
+
+.dark {
+ --backgroundColor: #252932;
+ --boxColor: #2E3440;
+ --headerTextColor: #D3D7DC;
+ --contentTextColor: #e9ebee;
+ --blockquoteColor: #2E3440;
+ --shadowColor: rgba(178, 178, 183, 0.12);
+ --hoverBgcColor: #65676a;
+ --selectedBgcColor: #959799;
+ --buttonBgcColor: oklch(37.3% .034 259.733);
+ --buttonHoverBgcColor: oklch(30.3% .034 259.733);
+ --anchorColor: #506cc9;
+ --hoverBorderColor: #3b82f6;
+
+ .milkdown {
+ --crepe-color-background: #252932;
+ --crepe-color-on-background: #f8f9ff;
+ --crepe-color-surface: #22262b;
+ --crepe-color-surface-low: #3c4044;
+ --crepe-color-on-surface: #e1e2e8;
+ --crepe-color-on-surface-variant: #c3c6cf;
+ --crepe-color-outline: #8d9199;
+ --crepe-color-primary: #a1c9fd;
+ --crepe-color-secondary: #3c4858;
+ --crepe-color-on-secondary: #d7e3f8;
+ --crepe-color-inverse: #e1e2e8;
+ --crepe-color-on-inverse: #2e3135;
+ --crepe-color-inline-code: #ffb4ab;
+ --crepe-color-error: #ffb4ab;
+ --crepe-color-hover: #1d2024;
+ --crepe-color-selected: #65676a;
+ --crepe-color-inline-area: #727a8433;
+
+ --crepe-shadow-1:
+ 0px 1px 2px 0px rgba(255, 255, 255, 0.3),
+ 0px 1px 3px 1px rgba(255, 255, 255, 0.15);
+ --crepe-shadow-2:
+ 0px 1px 2px 0px rgba(255, 255, 255, 0.3),
+ 0px 2px 6px 2px rgba(255, 255, 255, 0.15);
+ }
+}
+
+// .soft {
+// --backgroundColor: #252932;
+// --boxColor: #252932;
+// --headerTextColor: #fff;
+// --contentTextColor: #e6e6e6;
+// --blockquoteColor: #2E3440;
+// }
\ No newline at end of file
diff --git a/client/src/theme.ts b/client/src/theme.ts
deleted file mode 100644
index cce4a17..0000000
--- a/client/src/theme.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-export const themes = {
- light: {
- backgroundColor: '#e6e6e6',
- boxColor: '#fff',
- headerTextColor: '#494E59',
- contentTextColor: 'black',
- blockquoteColor: '#e6e6e6',
- },
- // dark: {
- // backgroundColor: "#252932",
- // boxColor: "#2E3440",
- // headerTextColor: "#D3D7DC",
- // contentTextColor: "#9D9FA6",
- // },
- dark: {
- backgroundColor: '#95a5a6',
- boxColor: '#7f8c8d',
- headerTextColor: 'black',
- contentTextColor: '#e6e6e6',
- blockquoteColor: '#2E3440',
- },
- soft: {
- backgroundColor: '#252932',
- boxColor: '#252932',
- headerTextColor: '#fff',
- contentTextColor: '#e6e6e6',
- blockquoteColor: '#2E3440',
- },
-};
diff --git a/client/src/type.d.ts b/client/src/type.d.ts
index 8c844b7..c800168 100644
--- a/client/src/type.d.ts
+++ b/client/src/type.d.ts
@@ -1,3 +1,18 @@
-interface Window {
- createObjectURL: (imgFile: File) => string;
+/* eslint-disable no-var */
+/* eslint-disable @typescript-eslint/naming-convention */
+declare global {
+ interface Window {
+ createObjectURL: (imgFile: File) => string;
+ }
+
+ // injected in build time
+ var __GITHUB_PAGES_BASE_PATH__: string;
+ var __VERSION__: string;
+ var __SERVER_PORT__: string;
+}
+
+export interface UnifyResponse {
+ data: T;
+ code: number;
+ message: string;
}
diff --git a/client/src/utils/ErrorBoundary/ErrorBoundary.tsx b/client/src/utils/ErrorBoundary/ErrorBoundary.tsx
index 7d03b3d..8930466 100644
--- a/client/src/utils/ErrorBoundary/ErrorBoundary.tsx
+++ b/client/src/utils/ErrorBoundary/ErrorBoundary.tsx
@@ -1,7 +1,7 @@
-import React, { Component, ErrorInfo, ReactChild } from 'react';
+import { Component, ErrorInfo, ReactNode } from 'react';
interface Props {
- children?: ReactChild;
+ children?: ReactNode;
displayInfo?: string;
log?: boolean;
}
diff --git a/client/src/utils/Modal/Modal.less b/client/src/utils/Modal/Modal.scss
similarity index 100%
rename from client/src/utils/Modal/Modal.less
rename to client/src/utils/Modal/Modal.scss
diff --git a/client/src/utils/Modal/Modal.tsx b/client/src/utils/Modal/Modal.tsx
index 6282d78..21175f7 100644
--- a/client/src/utils/Modal/Modal.tsx
+++ b/client/src/utils/Modal/Modal.tsx
@@ -1,10 +1,10 @@
import React, { useState, useEffect, useMemo } from 'react';
import { createPortal } from 'react-dom';
-import './Modal.less';
+import './Modal.scss';
export interface ModelProps {
- children: React.ReactChild | React.ReactChild[];
+ children: React.ReactNode | React.ReactNode[];
showControl: React.Dispatch>;
btnControl?: boolean;
iconControl?: boolean;
@@ -67,7 +67,7 @@ export default function Modal({
handleClose();
}}
>
- ×
+
)}
{children}
diff --git a/client/src/utils/ResizableBox/ResizableBox.less b/client/src/utils/ResizableBox/ResizableBox.less
deleted file mode 100644
index 22f7851..0000000
--- a/client/src/utils/ResizableBox/ResizableBox.less
+++ /dev/null
@@ -1,19 +0,0 @@
-.resizable-box {
- width: 100%;
- display: flex;
- .resiz-box {
- flex-shrink: 0;
- }
- .resize-bar {
- height: inherit;
- width: 1%;
- background-color: #fff;
- cursor: ew-resize;
- opacity: 0;
- transition: all 0.4s ease-in-out;
- &:hover {
- opacity: 0.7;
- background-color: #e6e6e6;
- }
- }
-}
diff --git a/client/src/utils/ResizableBox/ResizableBox.tsx b/client/src/utils/ResizableBox/ResizableBox.tsx
deleted file mode 100644
index 698b7a5..0000000
--- a/client/src/utils/ResizableBox/ResizableBox.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-/* eslint-disable @typescript-eslint/no-magic-numbers */
-import React, { useState, useRef, useLayoutEffect } from 'react';
-
-import ResizeBar from './ResizeBar';
-import './ResizableBox.less';
-
-export interface ResizableBoxProps {
- defaultWidth?: number[];
- children: React.ReactChild[];
- effects?: (((boxDom: HTMLDivElement) => void) | null)[];
- effectsDeps?: unknown[];
- boxStyles?: React.CSSProperties[];
- resizeBarStyle?: React.CSSProperties;
-}
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function ResizableBox({
- defaultWidth, // default width of the right box
- children,
- effects = [],
- effectsDeps = [],
- boxStyles = [],
- resizeBarStyle = {},
-}: ResizableBoxProps) {
- const containerRef = useRef(null);
- const boxRefs = useRef([]);
-
- const [widths, setWidths] = useState(
- // need to consider the resize bar width 1%
- defaultWidth?.map((width) => width - 0.01 / children.length) ??
- new Array(children.length).fill(0.99 / children.length),
- );
-
- const toStr = (n: number) => `${(n * 100).toFixed(2)}%`;
-
- // execute all the box effects
- useLayoutEffect(() => {
- effects.forEach((effect, idx) => {
- if (effect) {
- effect(boxRefs.current[idx]);
- }
- });
- // eslint-disable-next-line
- }, effectsDeps);
-
- if (boxStyles.length === 0) boxStyles = new Array(children.length).fill({}) as Record[];
-
- return (
-
- {children.map((box, idx) => (
-
- ref && (boxRefs.current[idx] = ref)}
- >
- {box}
-
-
- {idx !== children.length - 1 ? (
- {
- setWidths(newWidths);
- }}
- idx={idx + 1}
- widths={widths}
- style={resizeBarStyle}
- />
- ) : (
- ''
- )}
-
- ))}
-
- );
-}
diff --git a/client/src/utils/ResizableBox/ResizeBar.tsx b/client/src/utils/ResizableBox/ResizeBar.tsx
deleted file mode 100644
index 63d867a..0000000
--- a/client/src/utils/ResizableBox/ResizeBar.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import React, { useRef } from 'react';
-
-import { dragEventBinder } from '@/utils/utils';
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function ResizeBar({
- containerRef,
- widthChange,
- style = {},
- idx,
- widths,
-}: {
- containerRef: React.RefObject;
- widthChange: (widths: number[]) => void;
- style?: React.CSSProperties;
- idx: number;
- widths: number[];
-}) {
- const barRef = useRef(null);
-
- const dragStart = (e: React.MouseEvent) => {
- e.preventDefault();
-
- dragEventBinder((dragEvent) => {
- if (containerRef.current && barRef.current) {
- // the previous left side width of the current bar
- const prevLeftTotalWidthPercent = widths.slice(idx).reduce((total, width) => total + width, 0);
-
- // the distance from the current bar to the left side of the father container
- const offsetLeft = dragEvent.clientX - containerRef.current.offsetLeft;
-
- // the left side width of the current bar
- const leftTotalWidthPercent =
- 1 - offsetLeft / Number(getComputedStyle(containerRef.current).width.replace('px', ''));
-
- // get the diff
- const widthDiff = leftTotalWidthPercent - prevLeftTotalWidthPercent;
-
- // cur width is the right box of the current bar
- const curWidthPercent = widths[idx] + widthDiff;
- // last width is the left box of the current bar
- const lastWidthPercent = widths[idx - 1] - widthDiff;
-
- widthChange(
- widths.map((widthPercent, index) => {
- if (index === idx) {
- return curWidthPercent;
- }
-
- if (index === idx - 1) {
- return lastWidthPercent;
- }
-
- // only change the widths of the boxs aside the current bar
- // others remain the previous width
- return widthPercent;
- }),
- );
- }
- });
- };
-
- return (
-
- );
-}
diff --git a/client/src/utils/Spinner/Spinner.less b/client/src/utils/Spinner/Spinner.scss
similarity index 100%
rename from client/src/utils/Spinner/Spinner.less
rename to client/src/utils/Spinner/Spinner.scss
diff --git a/client/src/utils/Spinner/Spinner.tsx b/client/src/utils/Spinner/Spinner.tsx
index 62e9f72..4bc2c26 100644
--- a/client/src/utils/Spinner/Spinner.tsx
+++ b/client/src/utils/Spinner/Spinner.tsx
@@ -1,5 +1,5 @@
import React from 'react';
-import './Spinner.less';
+import './Spinner.scss';
// eslint-disable-next-line @typescript-eslint/naming-convention
export default function Spinner({ text = '', size = '5em' }) {
diff --git a/client/src/utils/Toast/ToastContainer.tsx b/client/src/utils/Toast/ToastContainer.tsx
deleted file mode 100644
index e138fac..0000000
--- a/client/src/utils/Toast/ToastContainer.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { nanoid } from '@reduxjs/toolkit';
-import React from 'react';
-
-import ToastItem from './ToastItem';
-import { Toast, ToastType } from './type';
-import './index.less';
-
-export default class ToastContainer extends React.Component {
- public state = {
- toastList: [] as Toast[],
- };
-
- public addToast(message: string, type: ToastType, duration: number) {
- const toastList = this.state.toastList.concat({
- id: nanoid(),
- type,
- message,
- duration,
- remove: (id) => {
- this.setState({
- toastList: this.state.toastList.filter((toast) => toast.id !== id),
- });
- },
- });
-
- this.setState({ toastList });
- }
-
- public render() {
- return (
-
- {this.state.toastList.map((toastInfo: Toast) => (
-
- ))}
-
- );
- }
-}
diff --git a/client/src/utils/Toast/ToastItem.tsx b/client/src/utils/Toast/ToastItem.tsx
deleted file mode 100644
index d7fcdcc..0000000
--- a/client/src/utils/Toast/ToastItem.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React, { useEffect, useState } from 'react';
-
-import { Toast } from './type';
-
-// eslint-disable-next-line @typescript-eslint/naming-convention
-export default function ToastItem({ message, duration, type, id, remove }: Toast) {
- const [isRemoving, setIsRemoving] = useState(false);
-
- useEffect(() => {
- const removeTimer = setTimeout(() => {
- // hidden animation then remove
- setIsRemoving(true);
- setTimeout(() => {
- if (remove) remove(id);
- // eslint-disable-next-line @typescript-eslint/no-magic-numbers
- }, 500);
- }, duration);
-
- return () => {
- clearTimeout(removeTimer);
- };
- // eslint-disable-next-line
- }, []);
- return (
- {message}
- );
-}
diff --git a/client/src/utils/Toast/index.less b/client/src/utils/Toast/index.less
deleted file mode 100644
index da7e6ed..0000000
--- a/client/src/utils/Toast/index.less
+++ /dev/null
@@ -1,45 +0,0 @@
-.toast-container {
- position: absolute;
- right: 0.8rem;
- bottom: 0.8rem;
- z-index: 1000;
- overflow-x: hidden;
- .toast-item {
- padding: 0.8rem;
- min-width: 10rem;
- height: 3rem;
- display: flex;
- align-items: center;
- text-align: center;
- border-radius: 0.5rem;
- margin-top: 0.5rem;
- user-select: none;
- font-weight: bold;
- font-size: 1rem;
- opacity: 0.6;
- transition: 0.5s ease-in-out;
- }
- .toast-show {
- animation: show 0.5s ease-in-out;
- }
- .toast-hide {
- transform: translateX(100%);
- }
- @keyframes show {
- from {
- transform: translateX(100%);
- }
- to {
- transform: translateX(0);
- }
- }
- .bgc-error {
- background-color: #e74c3c;
- }
- .bgc-success {
- background-color: #2ecc71;
- }
- .bgc-warning {
- background-color: #f1c40f;
- }
-}
diff --git a/client/src/utils/Toast/index.tsx b/client/src/utils/Toast/index.tsx
index ac846ca..43954be 100644
--- a/client/src/utils/Toast/index.tsx
+++ b/client/src/utils/Toast/index.tsx
@@ -1,19 +1,61 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
+import { MessageSeverity } from 'primereact/api';
+import { Toast as PrimeToast } from 'primereact/toast';
+import { FC, RefObject, useRef } from 'react';
+import { createRoot } from 'react-dom/client';
-import ToastContainer from './ToastContainer';
-import { ToastType } from './type';
+export const ERROR_DURATION = 3000;
+export const SUCCESS_DURATION = 1500;
+export const WARNING_DURATION = 1500;
+export const INFO_DURATION = 1500;
+export const SECONDARY_DURATION = 1500;
+export const CONTRAST_DURATION = 1500;
const toastRoot = document.createElement('div');
document.body.appendChild(toastRoot);
-// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-confusing-void-expression
-const toastContainerRef = ReactDOM.render( , toastRoot) as unknown as ToastContainer;
+let toastTopCenterRef: RefObject | null = null;
+const ToastContainer: FC = () => {
+ toastTopCenterRef = useRef(null);
-// eslint-disable-next-line @typescript-eslint/no-magic-numbers
-const Toast = (message: string, type: ToastType = 'SUCCESS', duration = 1500) => {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
- toastContainerRef.addToast(message, type, duration);
+ return (
+ <>
+
+ >
+ );
+};
+
+// eslint-disable-next-line @typescript-eslint/no-unsafe-return
+createRoot(toastRoot).render( );
+
+const Toast = (message: string, type: MessageSeverity = MessageSeverity.SUCCESS, duration?: number) => {
+ const defaultDurationMap: Record = {
+ [MessageSeverity.ERROR]: ERROR_DURATION,
+ [MessageSeverity.SUCCESS]: SUCCESS_DURATION,
+ [MessageSeverity.WARN]: WARNING_DURATION,
+ [MessageSeverity.INFO]: INFO_DURATION,
+ [MessageSeverity.SECONDARY]: SECONDARY_DURATION,
+ [MessageSeverity.CONTRAST]: CONTRAST_DURATION,
+ };
+ duration = duration ?? defaultDurationMap[type];
+
+ toastTopCenterRef?.current?.show({
+ severity: type,
+ life: duration,
+ detail: message,
+ });
+};
+
+Toast.success = (message: string, duration?: number) => {
+ Toast(message, MessageSeverity.SUCCESS, duration);
+};
+Toast.error = (message: string, duration?: number) => {
+ Toast(message, MessageSeverity.ERROR, duration);
+};
+Toast.warn = (message: string, duration?: number) => {
+ Toast(message, MessageSeverity.WARN, duration);
+};
+Toast.info = (message: string, duration?: number) => {
+ Toast(message, MessageSeverity.INFO, duration);
};
export default Toast;
diff --git a/client/src/utils/Toast/type.ts b/client/src/utils/Toast/type.ts
deleted file mode 100644
index 2c32765..0000000
--- a/client/src/utils/Toast/type.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export type ToastType = 'ERROR' | 'SUCCESS' | 'WARNING';
-
-export interface Toast {
- id: string;
- type: ToastType;
- message: string;
- duration: number;
- remove?: (id: string) => void;
-}
diff --git a/client/src/utils/hooks/docHooks.ts b/client/src/utils/hooks/docHooks.ts
index f9ddb85..52052e2 100644
--- a/client/src/utils/hooks/docHooks.ts
+++ b/client/src/utils/hooks/docHooks.ts
@@ -1,99 +1,26 @@
-import { useDispatch, useSelector } from 'react-redux';
-import { useHistory, useLocation } from 'react-router-dom';
+import { useNavigate, useLocation } from 'react-router-dom';
-import { useDeleteTab, useRenameTab, useSaveDoc } from './reduxHooks';
-import { getCurrentPath, isPathsRelated } from '../utils';
+import { useSaveDoc } from './reduxHooks';
+import { getCurrentPath, normalizePath, scrollToView } from '../utils';
-import { Change } from '@/redux-api/gitApi';
-import { updateCurDoc, selectCurDocDirty } from '@/redux-feature/curDocSlice';
-import { updateGlobalOpts } from '@/redux-feature/globalOptsSlice';
-import { updateCopyCut, selectOperationMenu } from '@/redux-feature/operationMenuSlice';
+import { useDeleteEffect } from '@/components/Menu/operations';
+import { Change } from '@/redux-api/git';
export const useCurPath = () => {
- const routerHistory = useHistory();
+ const navigate = useNavigate();
const { pathname } = useLocation();
return {
- routerHistory,
+ navigate,
curPath: getCurrentPath(pathname),
};
};
-export const useDeleteHandler = () => {
- const { curPath } = useCurPath();
-
- const { copyPath, cutPath } = useSelector(selectOperationMenu);
- const isDirty = useSelector(selectCurDocDirty);
-
- const dispatch = useDispatch();
-
- const deleteTab = useDeleteTab();
-
- return (deletedPath: string, isFile: boolean) => {
- if (deletedPath === copyPath || deletedPath === cutPath) {
- // clear the previous copy and cut
- dispatch(
- updateCopyCut({
- copyPath: '',
- cutPath: '',
- }),
- );
- }
-
- // jump if the current doc is deleted or included in the deleted folder
- if (isPathsRelated(curPath, deletedPath.split('-'), isFile)) {
- // clear global curDoc info
- if (isDirty) {
- dispatch(
- updateCurDoc({
- content: '',
- isDirty: false,
- contentPath: '',
- scrollTop: 0,
- }),
- );
- }
-
- deleteTab(curPath.join('-'));
- }
- };
-};
-
-export const useCopyCutHandler = () => {
- const { routerHistory, curPath } = useCurPath();
-
- return (copyCutPath: string, pastePath: string, isCut: boolean, isFile: boolean) => {
- // if it is cut and current path is included in it, redirect
- if (isCut && isPathsRelated(curPath, copyCutPath.split('-'), isFile)) {
- // if it is a file, direct to the paste path
- if (isFile) {
- routerHistory.push(`/article/${pastePath}`);
- } else {
- const curFile = curPath.slice(curPath.length - (curPath.length - copyCutPath.split('-').length)).join('-');
-
- routerHistory.push(`/article/${pastePath}-${curFile}`);
- }
- }
- };
-};
-
-export const useModifyNameHandler = () => {
- // const addTab = useAddTab();
- const renameTab = useRenameTab();
-
- return (modifiedPath: string[], newPath: string, isFile: boolean) => {
- // hidden the window
- document.body.click();
-
- renameTab(modifiedPath.join('-'), newPath, isFile);
- };
-};
-
/**
* handler for git restore at working space
*/
-export const useRestoreHandler = () => {
- const deleteHandler = useDeleteHandler();
+export const useRestoreEffects = () => {
+ const deleteHandler = useDeleteEffect();
return (staged: boolean, changes: Change[]) => {
// if it is in the working space and restored changes include untracked status
@@ -101,51 +28,51 @@ export const useRestoreHandler = () => {
if (!staged) {
for (const change of changes) {
if (change.status === 'UNTRACKED') {
- deleteHandler(change.changePath.replace('.md', '').replaceAll('/', '-'), true);
+ deleteHandler([{ filePath: normalizePath(change.changePath.replace('.md', '')), isFile: true }]);
}
}
}
};
};
-export const useEditorScrollToAnchor = () => {
- const { routerHistory, curPath } = useCurPath();
+export const getEditorScrollContainer = () => {
+ return document.querySelector('.editor-box .p-scrollpanel-content');
+};
+
+export const scrollToOutlineAnchor = (anchor: string) => {
+ const outline = document.getElementById(`outline-${anchor}`);
+ const scrollDom = document.querySelector('.outline-container .p-scrollpanel-content');
+ if (outline && scrollDom) {
+ scrollToView(scrollDom as HTMLElement, outline);
+ }
+};
+
+export const scrollToEditorAnchor = (anchor: string) => {
+ const dom = document.getElementById(anchor);
+ const scrollDom = getEditorScrollContainer();
+ if (dom && scrollDom) {
+ scrollDom.scrollTo({
+ top: dom.offsetTop,
+ behavior: 'auto',
+ });
+ }
+};
- const dispatch = useDispatch();
+export const useEditorScrollToAnchor = () => {
+ const { navigate, curPath } = useCurPath();
const saveDoc = useSaveDoc();
return (anchor: string, path = '') => {
// only do if path is provided
- if (path !== '' && curPath.join('-') !== path) {
- if (anchor !== '') {
- // tell the editor through global opts
- dispatch(updateGlobalOpts({ keys: ['anchor'], values: [anchor] }));
- }
-
+ if (path !== '' && normalizePath(curPath) !== path) {
void saveDoc();
-
- routerHistory.push(`/article/${path}`);
+ void navigate(`/article/${path}#${anchor}`);
return;
}
if (anchor !== '') {
- const dom = [...document.getElementsByClassName('heading')].find(
- (head) => (head as HTMLElement).innerText === anchor,
- );
- const strongDom = [...document.getElementsByClassName('strong')].find(
- (keyword) => (keyword as HTMLElement).innerText === anchor,
- );
-
- if (!dom && !strongDom) return;
-
- const parentDom = document.getElementsByClassName('milkdown')[0] as HTMLElement;
-
- parentDom.scroll({
- top: dom ? (dom as HTMLElement).offsetTop : (strongDom as HTMLElement).offsetTop,
- behavior: 'smooth',
- });
-
- return dom ?? strongDom;
+ scrollToEditorAnchor(anchor);
+ return;
}
};
};
diff --git a/client/src/utils/hooks/reduxHooks.ts b/client/src/utils/hooks/reduxHooks.ts
index 7c13df7..c9e5a76 100644
--- a/client/src/utils/hooks/reduxHooks.ts
+++ b/client/src/utils/hooks/reduxHooks.ts
@@ -1,38 +1,55 @@
+import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useCurPath } from './docHooks';
-import { isPathsRelated } from '../utils';
+import {
+ confirm,
+ denormalizePath,
+ getDraftKey,
+ isPathsRelated,
+ normalizePath,
+ Themes,
+ updateLocationHash,
+} from '../utils';
-import { useUpdateDocMutation } from '@/redux-api/docsApi';
+import type { RootState } from '@/store';
+
+import { APP_VERSION } from '@/constants';
+import { useCheckServerQuery, useUpdateDocMutation } from '@/redux-api/docs';
+import { useGetSettingsQuery } from '@/redux-api/settings';
import { selectCurDoc, selectCurTabs, updateIsDirty, updateTabs } from '@/redux-feature/curDocSlice';
-import { selectReadonly, selectDarkMode, updateGlobalOpts } from '@/redux-feature/globalOptsSlice';
+import { clearDraft, clearDrafts, DraftsState, selectHasDraft, setDraft } from '@/redux-feature/draftsSlice';
+import {
+ selectReadonly,
+ selectNarrowMode,
+ updateGlobalOpts,
+ updateServerStatus,
+ ServerStatus,
+} from '@/redux-feature/globalOptsSlice';
+import { store } from '@/store';
import Toast from '@/utils/Toast';
export const useSaveDoc = () => {
- const { isDirty, content, contentPath } = useSelector(selectCurDoc);
-
+ const { isDirty, content, contentIdent, type } = useSelector(selectCurDoc);
+ const { data: settings } = useGetSettingsQuery();
const dispatch = useDispatch();
- const [
- updateDoc,
- // { isLoading }
- ] = useUpdateDocMutation();
+ const [updateDoc] = useUpdateDocMutation();
return async () => {
if (!isDirty) return;
try {
- await updateDoc({
- modifyPath: contentPath,
- newContent: content,
- }).unwrap();
-
- // pop up to remind that is saved
- Toast('saved', 'SUCCESS');
-
- // after updated, it should not be dirty
- dispatch(updateIsDirty({ isDirty: false }));
+ if (type === 'workspace') {
+ await updateDoc({
+ filePath: contentIdent,
+ content,
+ }).unwrap();
+ Toast('saved successfully!');
+ dispatch(updateIsDirty({ isDirty: false }));
+ dispatch(clearDraft(getDraftKey(settings?.docRootPath, contentIdent)));
+ }
} catch (err) {
- Toast('Failed to save...', 'ERROR');
+ Toast.error((err as Error).message);
}
};
};
@@ -43,6 +60,8 @@ export const useSwitchReadonlyMode = () => {
const dispatch = useDispatch();
return () => {
+ // avoid re-anchor
+ updateLocationHash('');
dispatch(
updateGlobalOpts({
keys: ['readonly'],
@@ -52,16 +71,28 @@ export const useSwitchReadonlyMode = () => {
};
};
-export const useSwitchTheme = () => {
- const isDarkMode = useSelector(selectDarkMode);
+export const useSwitchNarrowMode = () => {
+ const narrowMode = useSelector(selectNarrowMode);
const dispatch = useDispatch();
return () => {
+ // avoid re-anchor
+ updateLocationHash('');
+ dispatch(updateGlobalOpts({ keys: ['narrowMode'], values: [!narrowMode] }));
+ };
+};
+
+export const useSwitchTheme = () => {
+ const dispatch = useDispatch();
+
+ return (theme: Themes) => {
+ // avoid re-anchor
+ updateLocationHash('');
dispatch(
updateGlobalOpts({
- keys: ['isDarkMode'],
- values: [!isDarkMode],
+ keys: ['theme'],
+ values: [theme],
}),
);
};
@@ -69,83 +100,168 @@ export const useSwitchTheme = () => {
export const useDeleteTab = () => {
const tabs = useSelector(selectCurTabs);
+ const { data: settings } = useGetSettingsQuery();
const dispatch = useDispatch();
- const { routerHistory: router, curPath } = useCurPath();
+ const { navigate, curPath } = useCurPath();
+ const hasDraftFor = (path: string) => selectHasDraft(getDraftKey(settings?.docRootPath, path))(store.getState());
- return (deletePath: string) => {
- dispatch(
- updateTabs(
- tabs.filter((tab, idx) => {
- // handle curDoc
- if (deletePath === curPath.join('-')) {
- // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
- if (idx !== tabs.length - 1) router.push(`/article/${tabs[idx + 1].path as string}`);
- // only one tab
- else if (idx === 0) router.push('/purePage');
- else router.push(`/article/${tabs[idx - 1].path as string}`);
- }
- return tab.path !== deletePath;
- }),
- ),
- );
+ return async (deletePaths: string[], options: { force?: boolean } = {}) => {
+ const hasUnsaved = deletePaths.some((p) => hasDraftFor(p));
+ if (hasUnsaved && !options.force) {
+ const message =
+ deletePaths.length === 1
+ ? 'This document has unsaved changes. Close anyway?'
+ : 'Some documents have unsaved changes. Close anyway?';
+ const confirmed = await confirm({ message });
+ if (!confirmed) return;
+ }
+
+ let curPathIncluded = false;
+ const newTabs = tabs.filter((tab) => {
+ for (const deletePath of deletePaths) {
+ if (deletePath === normalizePath(curPath)) {
+ curPathIncluded = true;
+ }
+ if (tab.ident === deletePath) return false;
+ }
+ return true;
+ });
+
+ dispatch(updateTabs(newTabs));
+ if (!options.force) {
+ dispatch(clearDrafts(deletePaths.map((p) => getDraftKey(settings?.docRootPath, p))));
+ }
+
+ if (curPathIncluded) {
+ if (newTabs.length === 0) {
+ void navigate('/purePage');
+ } else {
+ const lastTab = newTabs[newTabs.length - 1];
+ if (lastTab.type === 'workspace') {
+ void navigate(`/article/${lastTab.ident}`);
+ } else if (lastTab.type === 'internal') {
+ void navigate(`/internal/${lastTab.ident}`);
+ }
+ }
+ }
};
};
export const useAddTab = () => {
const tabs = useSelector(selectCurTabs);
const dispatch = useDispatch();
- const { routerHistory: router, curPath } = useCurPath();
+ const { navigate, curPath } = useCurPath();
return (addPath: string) => {
dispatch(
updateTabs(
tabs.concat({
active: true,
- path: addPath,
+ ident: addPath,
scroll: 0,
+ type: 'workspace',
}),
),
);
- if (curPath.join('-') !== addPath) router.push(`/article/${addPath}`);
+ if (normalizePath(curPath) !== addPath) void navigate(`/article/${addPath}`);
};
};
export const useRenameTab = () => {
- const { routerHistory, curPath } = useCurPath();
+ const { navigate, curPath } = useCurPath();
const tabs = useSelector(selectCurTabs);
+ const { data: settings } = useGetSettingsQuery();
const dispatch = useDispatch();
return (oldPath: string, newPath: string, isFile: boolean) => {
- const oldPathArr = oldPath.split('-');
+ const oldPathArr = denormalizePath(oldPath);
+ const renames: { oldPath: string; newPath: string }[] = [];
- dispatch(
- updateTabs(
- tabs.map(({ path, ...rest }) => {
- const pathArr = path.split('-');
-
- if (!isPathsRelated(pathArr, oldPathArr, isFile)) return { path, ...rest };
+ const newTabs = tabs
+ .filter((t) => t.type === 'workspace')
+ .map(({ ident: path, ...rest }) => {
+ const pathArr = denormalizePath(path);
- // modified path is or includes the current path
- const curFile = pathArr.slice(pathArr.length - (pathArr.length - oldPathArr.length)).join('-');
+ if (!isPathsRelated(pathArr, oldPathArr, isFile)) return { ident: path, ...rest };
- // current file is modified
- if (curFile.trim() === '') {
- if (path === curPath.join('-')) {
- routerHistory.push(`/article/${newPath}`);
- }
+ const curFile = pathArr.slice(pathArr.length - (pathArr.length - oldPathArr.length)).join('/');
+ const docPath = path;
- return { path: newPath, ...rest };
+ if (curFile.trim() === '') {
+ if (path === normalizePath(curPath)) {
+ void navigate(`/article/${newPath}`);
}
+ renames.push({ oldPath: docPath, newPath });
+ return { ident: newPath, ...rest };
+ }
- // current file is included the modified path
- if (path === curPath.join('-')) {
- routerHistory.push(`/article/${newPath}-${curFile as string}`);
- }
+ if (path === normalizePath(curPath)) {
+ void navigate(`/article/${normalizePath([newPath, curFile])}`);
+ }
+ const newDocPath = normalizePath([newPath, curFile]);
+ renames.push({ oldPath: docPath, newPath: newDocPath });
+ return { ident: newDocPath, ...rest };
+ });
- return { path: `${newPath}-${curFile as string}`, ...rest };
- }),
- ),
- );
+ dispatch(updateTabs(newTabs));
+
+ const state = store.getState() as RootState;
+ const drafts = state.drafts as DraftsState;
+ for (const { oldPath: op, newPath: np } of renames) {
+ const oldKey = getDraftKey(settings?.docRootPath, op);
+ const draft = drafts[oldKey];
+ if (draft) {
+ dispatch(setDraft({ path: getDraftKey(settings?.docRootPath, np), ...draft }));
+ dispatch(clearDraft(oldKey));
+ }
+ }
};
};
+
+export function useCheckServer() {
+ const res = useCheckServerQuery();
+ const { data: serverCheckRes, isLoading, isSuccess, error } = res;
+ const dispatch = useDispatch();
+
+ useEffect(() => {
+ if (!isLoading && !isSuccess) {
+ dispatch(updateServerStatus(ServerStatus.CANNOT_CONNECT));
+
+ dispatch(
+ updateGlobalOpts({
+ keys: ['menuCollapse', 'mirrorCollapse'],
+ values: [true, true],
+ }),
+ );
+
+ if (!error) return;
+
+ Toast.error('Cannot connect to server');
+ console.error(error);
+ } else if (isSuccess) {
+ if (APP_VERSION !== serverCheckRes?.version) {
+ dispatch(updateServerStatus(ServerStatus.VERSION_MISMATCHE));
+ }
+ }
+ }, [isSuccess, error, isLoading]);
+
+ return res;
+}
+
+export function useWarnUnsavedOnUnload() {
+ const hasDrafts = useSelector((state: RootState) => Object.keys(state.drafts).length > 0);
+
+ useEffect(() => {
+ if (!hasDrafts) return;
+
+ const handler = (e: BeforeUnloadEvent) => {
+ e.preventDefault();
+ };
+
+ window.addEventListener('beforeunload', handler);
+ return () => {
+ window.removeEventListener('beforeunload', handler);
+ };
+ }, [hasDrafts]);
+}
diff --git a/client/src/utils/hooks/tools.ts b/client/src/utils/hooks/tools.ts
index e0d9c43..3079023 100644
--- a/client/src/utils/hooks/tools.ts
+++ b/client/src/utils/hooks/tools.ts
@@ -117,7 +117,7 @@ export const useShortCut = () => {
break;
}
- case 'r': {
+ case 't': {
e.preventDefault();
readonlySwitch();
diff --git a/client/src/utils/utils.less b/client/src/utils/utils.less
deleted file mode 100644
index 92e47fb..0000000
--- a/client/src/utils/utils.less
+++ /dev/null
@@ -1,95 +0,0 @@
-.shadow-box {
- box-shadow: 0 0 3px 3px rgba(0, 0, 0, 0.1);
-}
-.global-background-color {
- background-color: #95a5a6;
-}
-.text-overflow-omit {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-@backgroundColor: var(--backgroundColor, #95a5a6);
-@boxColor: var(--boxColor, #7f8c8d);
-@headerTextColor: var(--headerTextColor, black);
-@contentTextColor: var(--contentTextColor, #e6e6e6);
-@blockquoteColor: var(--blockquoteColor, #2e3440);
-
-@shadow: 0 0 5px rgba(0, 0, 0, 0.5);
-@transition: all 0.3s ease-in-out;
-
-.btn(@width, @height, @color, @hover-color) {
- width: @width;
- height: @height;
- padding: 10px;
- background-color: @color;
- border-radius: 5px;
- outline: none;
- cursor: pointer;
- border: 0;
- &:hover {
- background-color: @hover-color;
- }
- &:focus {
- outline: none;
- }
-}
-
-.img-error {
- display: inline-block;
- transform: scale(1);
- content: "";
- color: transparent;
- &::before {
- content: "";
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- height: 100%;
- background: #f5f5f5 url(../imgs/logo.svg) no-repeat center / 50% 50%;
- }
- &::after {
- content: attr(alt);
- position: absolute;
- left: 0;
- bottom: 0;
- width: 100%;
- line-height: 2;
- background-color: rgba(0, 0, 0, 0.5);
- color: white;
- font-size: 12px;
- text-align: center;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
-}
-
-.img-loading {
- &::before {
- content: "";
- position: absolute;
- top: 50%;
- left: 50%;
- width: 22px;
- height: 22px;
- margin-left: -11px;
- margin-top: -11px;
- background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABAUlEQVQ4T6WSzyrEYRSGn2crF6GmJJTsLLGyEVM2lAshdriUsbAwsWeWlpJ/Kck1KNtXM330G8b84due8z7fed9z5J/P3/RJNoCDUt9XT3r1/gAkmVSfktwB00V0r84MBCS5AJaAc6A2EiDJOPBW+WUb2KtaSDKr3lYn6bKQ5AxYBS7V5WHy/QIkWSmCV/VhGHG7pwNIsg6cFlFdbfbZzlRHqI9VwCbQKKIt9bgPYKEArqqAMWCriBrq+0gWPpuTzAG7wKF68x2SpKY+99tC2/sa0FTr1cYkE8ALMK9ef9a+r7F9RDvAkdpK0ip+F0vY/SfoMXIXYOApDxvcrxn8BfABIiRjEYfmQAcAAAAASUVORK5CYII=")
- no-repeat;
- background-position: center center;
- background-size: contain;
- animation: loading 0.5s infinite linear;
- }
-}
-
-@keyframes loading {
- from {
- transform: rotate(0);
- }
- to {
- transform: rotate(360deg);
- }
-}
diff --git a/client/src/utils/utils.scss b/client/src/utils/utils.scss
new file mode 100644
index 0000000..94f81ff
--- /dev/null
+++ b/client/src/utils/utils.scss
@@ -0,0 +1,138 @@
+@mixin shadow-box($v1: 0, $v2: 0, $v3: 1px, $v4: 1px) {
+ box-shadow: $v1 $v2 $v3 $v4 $shadowColor;
+}
+@mixin text-overflow-omit($line: 1) {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: $line;
+ -webkit-box-orient: vertical;
+}
+
+@mixin flex-center {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+$footerHeight: 26px;
+$headerHeight: 54px;
+$tabHeight: 45px;
+$editorHeight: calc(100vh - $footerHeight - $headerHeight - $tabHeight);
+
+$backgroundColor: var(--backgroundColor, #95a5a6);
+$boxColor: var(--boxColor, #7f8c8d);
+$headerTextColor: var(--headerTextColor, black);
+$contentTextColor: var(--contentTextColor, #e6e6e6);
+$blockquoteColor: var(--blockquoteColor, #2e3440);
+$shadowColor: var(--shadowColor, rgba(74, 74, 78, .12));
+$shallowTextColor: var(--shallowTextColor, #9D9FA6);
+$hoverBgcColor: var(--hoverBgcColor, #D3D7DC);;
+$borderRadius: 5px;
+$shadow: 0 0 5px $shadowColor;
+$transition: all 0.2s ease-in-out;
+$selectedBgcColor: var(--selectedBgcColor);
+$buttonBgcColor: var(--buttonBgcColor);
+$buttonHoverBgcColor: var(--buttonHoverBgcColor);
+$anchorColor: var(--anchorColor);
+$hoverBorderColor: var(--hoverBorderColor);
+
+@mixin btn($width, $height, $color, $hover-color){
+ width: $width;
+ height: $height;
+ padding: 10px;
+ background-color: $color;
+ border-radius: 5px;
+ outline: none;
+ cursor: pointer;
+ border: 0;
+ &:hover {
+ background-color: $hover-color;
+ }
+ &:focus {
+ outline: none;
+ }
+}
+
+.img-error {
+ display: inline-block;
+ transform: scale(1);
+ content: "";
+ color: transparent;
+ &::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ background: #f5f5f5 url(../imgs/logo.svg) no-repeat center / 50% 50%;
+ }
+ &::after {
+ content: attr(alt);
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ line-height: 2;
+ background-color: rgba(0, 0, 0, 0.5);
+ color: white;
+ font-size: 12px;
+ text-align: center;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+}
+
+.img-loading {
+ &::before {
+ content: "";
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 22px;
+ height: 22px;
+ margin-left: -11px;
+ margin-top: -11px;
+ background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABAUlEQVQ4T6WSzyrEYRSGn2crF6GmJJTsLLGyEVM2lAshdriUsbAwsWeWlpJ/Kck1KNtXM330G8b84due8z7fed9z5J/P3/RJNoCDUt9XT3r1/gAkmVSfktwB00V0r84MBCS5AJaAc6A2EiDJOPBW+WUb2KtaSDKr3lYn6bKQ5AxYBS7V5WHy/QIkWSmCV/VhGHG7pwNIsg6cFlFdbfbZzlRHqI9VwCbQKKIt9bgPYKEArqqAMWCriBrq+0gWPpuTzAG7wKF68x2SpKY+99tC2/sa0FTr1cYkE8ALMK9ef9a+r7F9RDvAkdpK0ip+F0vY/SfoMXIXYOApDxvcrxn8BfABIiRjEYfmQAcAAAAASUVORK5CYII=")
+ no-repeat;
+ background-position: center center;
+ background-size: contain;
+ animation: loading 0.5s infinite linear;
+ }
+}
+
+@keyframes loading {
+ from {
+ transform: rotate(0);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+@mixin icon-btn($size: 16px) {
+ cursor: pointer;
+ user-select: none;
+ position: relative;
+ color: $headerTextColor;
+ width: $size;
+ height: $size;
+ z-index: 1;
+ &:hover {
+ &::after {
+ content: '';
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 150%;
+ height: 150%;
+ border-radius: $borderRadius;
+ background-color: $hoverBgcColor;
+ z-index: -1;
+ }
+ }
+}
diff --git a/client/src/utils/utils.ts b/client/src/utils/utils.ts
index 9def6a8..8816ed2 100644
--- a/client/src/utils/utils.ts
+++ b/client/src/utils/utils.ts
@@ -1,5 +1,19 @@
+import { confirmDialog, ConfirmDialogProps } from 'primereact/confirmdialog';
+
+import { GITHUB_PAGES_BASE_PATH } from '@/constants';
+
/* eslint-disable @typescript-eslint/no-magic-numbers */
-import { themes } from '@/theme';
+export const normalizePath = (pathArr: string[] | string): string =>
+ typeof pathArr === 'string' ? encodeURIComponent(pathArr) : encodeURIComponent(pathArr.join('/'));
+
+export const denormalizePath = (pathStr: string) => decodeURIComponent(pathStr).split('/');
+
+/**
+ * Draft key for per-workspace draft isolation.
+ * Uses docRootPath + doc path so different workspaces do not share drafts.
+ */
+export const getDraftKey = (docRootPath: string | undefined, docPath: string): string =>
+ [docRootPath ?? '', docPath].filter(Boolean).join('|');
export const localStore = (key: string) => {
const value = window.localStorage.getItem(key);
@@ -13,12 +27,15 @@ export const localStore = (key: string) => {
/**
* get the doc path based on the current router pathname
+ * @example / -> []
+ * /article -> []
+ * /article/xx%2Fyy%2Fz -> ['xx', 'yy', 'z']
*/
export const getCurrentPath = (pathname: string) => {
const paths = pathname.split('/');
if (paths.length === 3) {
- return paths[2].split('-');
+ return denormalizePath(paths[2]);
} else {
return [];
}
@@ -28,10 +45,10 @@ export const isPathsRelated = (curPath: string[], path: string[], clickOnFile: b
// same file
// or the current path is included in the path
if (
- curPath.join('-') === path.join('-') ||
+ normalizePath(curPath) === normalizePath(path) ||
(!clickOnFile &&
curPath.length > path.length &&
- curPath.slice(0, curPath.length - (curPath.length - path.length)).join('-') === path.join('-'))
+ normalizePath(curPath.slice(0, curPath.length - (curPath.length - path.length))) === normalizePath(path))
) {
return true;
}
@@ -49,38 +66,6 @@ export const dragEventBinder = (callback: (e: MouseEvent) => void) => {
document.addEventListener('mouseup', mouseupEvent);
};
-export const smoothCollapse = (isCollapse: boolean, collapseCallbacks?: () => void, openCallbacks?: () => void) => {
- return (boxDom: HTMLDivElement) => {
- // only called when switching the collapse state
- if (isCollapse) {
- // when collapsing, add transition immediately
- if (!boxDom) return;
- boxDom.style.transition = 'all 0.4s ease-in-out';
-
- // wait for the collapsing finishing then execute the below callbacks
- if (!collapseCallbacks) return;
-
- const timer = setTimeout(() => {
- collapseCallbacks();
- clearTimeout(timer);
- }, 500);
- } else {
- // when to open the box, execute the below callbacks immediately
- if (openCallbacks) {
- openCallbacks();
- }
-
- // when opening the box, after finishing the transition (wati >= 0.4s)
- // remove the transition for the dragging
- const timer = setTimeout(() => {
- if (boxDom) boxDom.style.transition = 'none';
-
- clearTimeout(timer);
- }, 500);
- }
- };
-};
-
// eslint-disable-next-line @typescript-eslint/ban-types
export const throttle = (fn: Function, delay: number) => {
let startTime = Date.now();
@@ -163,12 +148,34 @@ export const hightLight = (word: string, inputs: string[], color = 'rgb(188, 54,
return word.replace(reg, (matchWord) => `${matchWord} `);
};
-export const changeTheme = (themeName: string) => {
- const theme = themes[themeName as keyof typeof themes];
+const PRIME_THEME_LINK_ID = 'prime-theme';
- for (const themeKey in theme) {
- document.body.style.setProperty(`--${themeKey}`, theme[themeKey as keyof typeof theme]);
+function getPrimeThemeLink(): HTMLLinkElement {
+ let link = document.getElementById(PRIME_THEME_LINK_ID) as HTMLLinkElement | null;
+ if (!link) {
+ link = document.createElement('link');
+ link.id = PRIME_THEME_LINK_ID;
+ link.rel = 'stylesheet';
+ document.head.appendChild(link);
}
+ return link;
+}
+
+export type Themes = 'dark' | 'light' | 'soft';
+export const changeTheme = (themeName: Themes) => {
+ const allThemes = ['light', 'dark', 'soft'];
+ document.documentElement.classList.add(themeName);
+ allThemes
+ .filter((theme) => theme !== themeName)
+ .forEach((theme) => {
+ document.documentElement.classList.remove(theme);
+ });
+
+ // Switch PrimeReact theme: dark -> lara-dark-blue, light/soft -> lara-light-blue
+ const primeTheme = themeName === 'dark' ? 'lara-dark-blue' : 'lara-light-blue';
+ const link = getPrimeThemeLink();
+ const basePath = GITHUB_PAGES_BASE_PATH.replace(/\/$/, '');
+ link.href = `${basePath}/themes/${primeTheme}/theme.css`;
};
export const scrollToBottomListener = (container: HTMLElement, callback: () => void, bias = 3) => {
@@ -213,7 +220,7 @@ export const dateFormat = (date: Date, format = 'YYYY-MM-DD HH:mm:ss') => {
return format;
};
-export const isEqual = (obj1: Record, obj2: Record) => {
+export const isEqual = (obj1: O, obj2: O) => {
function isObject(obj: unknown) {
return typeof obj === 'object' && obj != null;
}
@@ -232,7 +239,7 @@ export const isEqual = (obj1: Record, obj2: Record, obj2[key] as Record);
+ const res = isEqual(obj1[key] as O, obj2[key] as O);
if (!res) return false;
}
@@ -240,7 +247,79 @@ export const isEqual = (obj1: Record, obj2: Record bottomBorder) {
+ scrollContainer.scrollTo({
+ top: offsetTop,
+ });
+ }
+}
+
export function updateLocationHash(hash: string) {
const location = window.location.toString().split('#')[0];
history.replaceState(null, '', `${location}#${hash}`);
}
+
+export const nextTick = (fn: () => Promise | void, time = 0) => {
+ setTimeout(() => {
+ void fn();
+ }, time);
+};
+
+export async function waitAndCheck(isHit: () => boolean, wait = 50, maxTry = 10) {
+ return new Promise((res) => {
+ let tryCount = 0;
+ const act = () => {
+ if (isHit()) {
+ res(true);
+ return;
+ }
+ if (tryCount <= maxTry) {
+ tryCount++;
+ setTimeout(act, wait);
+ } else {
+ res(false);
+ }
+ };
+ act();
+ });
+}
+
+/** with the ConfirmDialog component declared in App */
+export const confirm = async (props: ConfirmDialogProps) => {
+ return new Promise((resolve) => {
+ confirmDialog({
+ header: 'Confirmation',
+ acceptLabel: 'Confirm',
+ rejectLabel: 'Cancel',
+ ...props,
+ accept: () => {
+ resolve(true);
+ },
+ reject: () => {
+ resolve(false);
+ },
+ });
+ });
+};
+
+export function uid(len = 5) {
+ return Math.random()
+ .toString(36)
+ .substring(2, len + 2);
+}
+
+export function getServerDownloadUrl(appVersion: string) {
+ const isMacos = window.navigator.userAgent.includes('Mac');
+ return `https://github.com/s-elo/Markdown-editor/releases/download/v${appVersion}/mds-${
+ isMacos ? 'macos' : 'windows'
+ }.zip`;
+}
diff --git a/client/tsconfig.json b/client/tsconfig.json
index 5f0507d..7f17644 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -4,7 +4,7 @@
"sourceMap": true,
"noImplicitAny": true,
"module": "esnext",
- "target": "es6",
+ "target": "esnext",
"moduleResolution": "node",
"lib": ["esnext", "dom", "DOM.Iterable"],
"skipLibCheck": true,
@@ -19,8 +19,8 @@
"allowJs": true,
"paths": {
"@/*": ["./src/*"]
- }
+ },
+ "types": ["@types/node"]
},
- "include": ["./src/**/*"],
- "exclude": ["build", "node_modules"]
+ "include": ["./src/**/*", "./rsbuild.config.ts"]
}
diff --git a/client/yarn.lock b/client/yarn.lock
deleted file mode 100644
index 4ed231c..0000000
--- a/client/yarn.lock
+++ /dev/null
@@ -1,11414 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
-"@ampproject/remapping@^2.1.0":
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
- integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
- dependencies:
- "@jridgewell/gen-mapping" "^0.1.0"
- "@jridgewell/trace-mapping" "^0.3.9"
-
-"@apideck/better-ajv-errors@^0.3.1":
- version "0.3.4"
- resolved "https://registry.yarnpkg.com/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.4.tgz#f89924dd4efd04a51835db7eb549a7177e0ca727"
- integrity sha512-Ic2d8ZT6HJiSikGVQvSklaFyw1OUv4g8sDOxa0PXSlbmN/3gL5IO1WYY9DOwTDqOFmjWoqG1yaaKnPDqYCE9KA==
- dependencies:
- json-schema "^0.4.0"
- jsonpointer "^5.0.0"
- leven "^3.1.0"
-
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
- integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
- dependencies:
- "@babel/highlight" "^7.16.7"
-
-"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.10":
- version "7.17.10"
- resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.10.tgz#711dc726a492dfc8be8220028b1b92482362baab"
- integrity sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==
-
-"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.2.tgz#87b2fcd7cce9becaa7f5acebdc4f09f3dd19d876"
- integrity sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==
- dependencies:
- "@ampproject/remapping" "^2.1.0"
- "@babel/code-frame" "^7.16.7"
- "@babel/generator" "^7.18.2"
- "@babel/helper-compilation-targets" "^7.18.2"
- "@babel/helper-module-transforms" "^7.18.0"
- "@babel/helpers" "^7.18.2"
- "@babel/parser" "^7.18.0"
- "@babel/template" "^7.16.7"
- "@babel/traverse" "^7.18.2"
- "@babel/types" "^7.18.2"
- convert-source-map "^1.7.0"
- debug "^4.1.0"
- gensync "^1.0.0-beta.2"
- json5 "^2.2.1"
- semver "^6.3.0"
-
-"@babel/eslint-parser@^7.16.3":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.18.2.tgz#e14dee36c010edfb0153cf900c2b0815e82e3245"
- integrity sha512-oFQYkE8SuH14+uR51JVAmdqwKYXGRjEXx7s+WiagVjqQ+HPE+nnwyF2qlVG8evUsUHmPcA+6YXMEDbIhEyQc5A==
- dependencies:
- eslint-scope "^5.1.1"
- eslint-visitor-keys "^2.1.0"
- semver "^6.3.0"
-
-"@babel/generator@^7.18.2", "@babel/generator@^7.7.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d"
- integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==
- dependencies:
- "@babel/types" "^7.18.2"
- "@jridgewell/gen-mapping" "^0.3.0"
- jsesc "^2.5.1"
-
-"@babel/helper-annotate-as-pure@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862"
- integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==
- dependencies:
- "@babel/types" "^7.16.7"
-
-"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b"
- integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==
- dependencies:
- "@babel/helper-explode-assignable-expression" "^7.16.7"
- "@babel/types" "^7.16.7"
-
-"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10", "@babel/helper-compilation-targets@^7.18.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b"
- integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ==
- dependencies:
- "@babel/compat-data" "^7.17.10"
- "@babel/helper-validator-option" "^7.16.7"
- browserslist "^4.20.2"
- semver "^6.3.0"
-
-"@babel/helper-create-class-features-plugin@^7.17.12", "@babel/helper-create-class-features-plugin@^7.18.0":
- version "7.18.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz#fac430912606331cb075ea8d82f9a4c145a4da19"
- integrity sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.16.7"
- "@babel/helper-environment-visitor" "^7.16.7"
- "@babel/helper-function-name" "^7.17.9"
- "@babel/helper-member-expression-to-functions" "^7.17.7"
- "@babel/helper-optimise-call-expression" "^7.16.7"
- "@babel/helper-replace-supers" "^7.16.7"
- "@babel/helper-split-export-declaration" "^7.16.7"
-
-"@babel/helper-create-regexp-features-plugin@^7.16.7", "@babel/helper-create-regexp-features-plugin@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz#bb37ca467f9694bbe55b884ae7a5cc1e0084e4fd"
- integrity sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.16.7"
- regexpu-core "^5.0.1"
-
-"@babel/helper-define-polyfill-provider@^0.3.1":
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665"
- integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==
- dependencies:
- "@babel/helper-compilation-targets" "^7.13.0"
- "@babel/helper-module-imports" "^7.12.13"
- "@babel/helper-plugin-utils" "^7.13.0"
- "@babel/traverse" "^7.13.0"
- debug "^4.1.1"
- lodash.debounce "^4.0.8"
- resolve "^1.14.2"
- semver "^6.1.2"
-
-"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd"
- integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==
-
-"@babel/helper-explode-assignable-expression@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a"
- integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==
- dependencies:
- "@babel/types" "^7.16.7"
-
-"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9":
- version "7.17.9"
- resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12"
- integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==
- dependencies:
- "@babel/template" "^7.16.7"
- "@babel/types" "^7.17.0"
-
-"@babel/helper-hoist-variables@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246"
- integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==
- dependencies:
- "@babel/types" "^7.16.7"
-
-"@babel/helper-member-expression-to-functions@^7.17.7":
- version "7.17.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4"
- integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==
- dependencies:
- "@babel/types" "^7.17.0"
-
-"@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437"
- integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==
- dependencies:
- "@babel/types" "^7.16.7"
-
-"@babel/helper-module-transforms@^7.18.0":
- version "7.18.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd"
- integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==
- dependencies:
- "@babel/helper-environment-visitor" "^7.16.7"
- "@babel/helper-module-imports" "^7.16.7"
- "@babel/helper-simple-access" "^7.17.7"
- "@babel/helper-split-export-declaration" "^7.16.7"
- "@babel/helper-validator-identifier" "^7.16.7"
- "@babel/template" "^7.16.7"
- "@babel/traverse" "^7.18.0"
- "@babel/types" "^7.18.0"
-
-"@babel/helper-optimise-call-expression@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2"
- integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==
- dependencies:
- "@babel/types" "^7.16.7"
-
-"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.17.12", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96"
- integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==
-
-"@babel/helper-remap-async-to-generator@^7.16.8":
- version "7.16.8"
- resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3"
- integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.16.7"
- "@babel/helper-wrap-function" "^7.16.8"
- "@babel/types" "^7.16.8"
-
-"@babel/helper-replace-supers@^7.16.7", "@babel/helper-replace-supers@^7.18.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.2.tgz#41fdfcc9abaf900e18ba6e5931816d9062a7b2e0"
- integrity sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q==
- dependencies:
- "@babel/helper-environment-visitor" "^7.18.2"
- "@babel/helper-member-expression-to-functions" "^7.17.7"
- "@babel/helper-optimise-call-expression" "^7.16.7"
- "@babel/traverse" "^7.18.2"
- "@babel/types" "^7.18.2"
-
-"@babel/helper-simple-access@^7.17.7", "@babel/helper-simple-access@^7.18.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9"
- integrity sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ==
- dependencies:
- "@babel/types" "^7.18.2"
-
-"@babel/helper-skip-transparent-expression-wrappers@^7.16.0":
- version "7.16.0"
- resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09"
- integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==
- dependencies:
- "@babel/types" "^7.16.0"
-
-"@babel/helper-split-export-declaration@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b"
- integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==
- dependencies:
- "@babel/types" "^7.16.7"
-
-"@babel/helper-validator-identifier@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
- integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
-
-"@babel/helper-validator-option@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23"
- integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==
-
-"@babel/helper-wrap-function@^7.16.8":
- version "7.16.8"
- resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200"
- integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==
- dependencies:
- "@babel/helper-function-name" "^7.16.7"
- "@babel/template" "^7.16.7"
- "@babel/traverse" "^7.16.8"
- "@babel/types" "^7.16.8"
-
-"@babel/helpers@^7.18.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384"
- integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg==
- dependencies:
- "@babel/template" "^7.16.7"
- "@babel/traverse" "^7.18.2"
- "@babel/types" "^7.18.2"
-
-"@babel/highlight@^7.16.7":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351"
- integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==
- dependencies:
- "@babel/helper-validator-identifier" "^7.16.7"
- chalk "^2.0.0"
- js-tokens "^4.0.0"
-
-"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.7", "@babel/parser@^7.18.0":
- version "7.18.4"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.4.tgz#6774231779dd700e0af29f6ad8d479582d7ce5ef"
- integrity sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==
-
-"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz#1dca338caaefca368639c9ffb095afbd4d420b1e"
- integrity sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz#0d498ec8f0374b1e2eb54b9cb2c4c78714c77753"
- integrity sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
- "@babel/plugin-proposal-optional-chaining" "^7.17.12"
-
-"@babel/plugin-proposal-async-generator-functions@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.17.12.tgz#094a417e31ce7e692d84bab06c8e2a607cbeef03"
- integrity sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-remap-async-to-generator" "^7.16.8"
- "@babel/plugin-syntax-async-generators" "^7.8.4"
-
-"@babel/plugin-proposal-class-properties@^7.16.0", "@babel/plugin-proposal-class-properties@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4"
- integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw==
- dependencies:
- "@babel/helper-create-class-features-plugin" "^7.17.12"
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-proposal-class-static-block@^7.18.0":
- version "7.18.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.0.tgz#7d02253156e3c3793bdb9f2faac3a1c05f0ba710"
- integrity sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA==
- dependencies:
- "@babel/helper-create-class-features-plugin" "^7.18.0"
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/plugin-syntax-class-static-block" "^7.14.5"
-
-"@babel/plugin-proposal-decorators@^7.16.4":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.2.tgz#dbe4086d2d42db489399783c3aa9272e9700afd4"
- integrity sha512-kbDISufFOxeczi0v4NQP3p5kIeW6izn/6klfWBrIIdGZZe4UpHR+QU03FAoWjGGd9SUXAwbw2pup1kaL4OQsJQ==
- dependencies:
- "@babel/helper-create-class-features-plugin" "^7.18.0"
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-replace-supers" "^7.18.2"
- "@babel/helper-split-export-declaration" "^7.16.7"
- "@babel/plugin-syntax-decorators" "^7.17.12"
- charcodes "^0.2.0"
-
-"@babel/plugin-proposal-dynamic-import@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2"
- integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.16.7"
- "@babel/plugin-syntax-dynamic-import" "^7.8.3"
-
-"@babel/plugin-proposal-export-namespace-from@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz#b22864ccd662db9606edb2287ea5fd1709f05378"
- integrity sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
-
-"@babel/plugin-proposal-json-strings@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz#f4642951792437233216d8c1af370bb0fbff4664"
- integrity sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/plugin-syntax-json-strings" "^7.8.3"
-
-"@babel/plugin-proposal-logical-assignment-operators@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz#c64a1bcb2b0a6d0ed2ff674fd120f90ee4b88a23"
- integrity sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
-
-"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0", "@babel/plugin-proposal-nullish-coalescing-operator@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be"
- integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
-
-"@babel/plugin-proposal-numeric-separator@^7.16.0", "@babel/plugin-proposal-numeric-separator@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9"
- integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.16.7"
- "@babel/plugin-syntax-numeric-separator" "^7.10.4"
-
-"@babel/plugin-proposal-object-rest-spread@^7.18.0":
- version "7.18.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.0.tgz#79f2390c892ba2a68ec112eb0d895cfbd11155e8"
- integrity sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw==
- dependencies:
- "@babel/compat-data" "^7.17.10"
- "@babel/helper-compilation-targets" "^7.17.10"
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
- "@babel/plugin-transform-parameters" "^7.17.12"
-
-"@babel/plugin-proposal-optional-catch-binding@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf"
- integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.16.7"
- "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
-
-"@babel/plugin-proposal-optional-chaining@^7.16.0", "@babel/plugin-proposal-optional-chaining@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174"
- integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
- "@babel/plugin-syntax-optional-chaining" "^7.8.3"
-
-"@babel/plugin-proposal-private-methods@^7.16.0", "@babel/plugin-proposal-private-methods@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz#c2ca3a80beb7539289938da005ad525a038a819c"
- integrity sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A==
- dependencies:
- "@babel/helper-create-class-features-plugin" "^7.17.12"
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-proposal-private-property-in-object@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz#b02efb7f106d544667d91ae97405a9fd8c93952d"
- integrity sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.16.7"
- "@babel/helper-create-class-features-plugin" "^7.17.12"
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
-
-"@babel/plugin-proposal-unicode-property-regex@^7.17.12", "@babel/plugin-proposal-unicode-property-regex@^7.4.4":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz#3dbd7a67bd7f94c8238b394da112d86aaf32ad4d"
- integrity sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A==
- dependencies:
- "@babel/helper-create-regexp-features-plugin" "^7.17.12"
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-syntax-async-generators@^7.8.4":
- version "7.8.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
- integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-bigint@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea"
- integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3":
- version "7.12.13"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10"
- integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-syntax-class-static-block@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406"
- integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-syntax-decorators@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.17.12.tgz#02e8f678602f0af8222235271efea945cfdb018a"
- integrity sha512-D1Hz0qtGTza8K2xGyEdVNCYLdVHukAcbQr4K3/s6r/esadyEriZovpJimQOpu8ju4/jV8dW/1xdaE0UpDroidw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-syntax-dynamic-import@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
- integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-export-namespace-from@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a"
- integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.3"
-
-"@babel/plugin-syntax-flow@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.17.12.tgz#23d852902acd19f42923fca9d0f196984d124e73"
- integrity sha512-B8QIgBvkIG6G2jgsOHQUist7Sm0EBLDCx8sen072IwqNuzMegZNXrYnSv77cYzA8mLDZAfQYqsLIhimiP1s2HQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-syntax-import-assertions@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.17.12.tgz#58096a92b11b2e4e54b24c6a0cc0e5e607abcedd"
- integrity sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-syntax-import-meta@^7.8.3":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51"
- integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==
- dependencies:
- "@babel/helper-plugin-utils" "^7.10.4"
-
-"@babel/plugin-syntax-json-strings@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
- integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-jsx@^7.12.13", "@babel/plugin-syntax-jsx@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.17.12.tgz#834035b45061983a491f60096f61a2e7c5674a47"
- integrity sha512-spyY3E3AURfxh/RHtjx5j6hs8am5NbUBGfcZ2vB3uShSpZdQyXSf5rR5Mk76vbtlAZOelyVQ71Fg0x9SG4fsog==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
- integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
- dependencies:
- "@babel/helper-plugin-utils" "^7.10.4"
-
-"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
- integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
- integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
- dependencies:
- "@babel/helper-plugin-utils" "^7.10.4"
-
-"@babel/plugin-syntax-object-rest-spread@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
- integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-optional-catch-binding@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
- integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-optional-chaining@^7.8.3":
- version "7.8.3"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
- integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.8.0"
-
-"@babel/plugin-syntax-private-property-in-object@^7.14.5":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad"
- integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3":
- version "7.14.5"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c"
- integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-syntax-typescript@^7.17.12", "@babel/plugin-syntax-typescript@^7.7.2":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.17.12.tgz#b54fc3be6de734a56b87508f99d6428b5b605a7b"
- integrity sha512-TYY0SXFiO31YXtNg3HtFwNJHjLsAyIIhAhNWkQ5whPPS7HWUFlg9z0Ta4qAQNjQbP1wsSt/oKkmZ/4/WWdMUpw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-arrow-functions@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45"
- integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-async-to-generator@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.17.12.tgz#dbe5511e6b01eee1496c944e35cdfe3f58050832"
- integrity sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ==
- dependencies:
- "@babel/helper-module-imports" "^7.16.7"
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-remap-async-to-generator" "^7.16.8"
-
-"@babel/plugin-transform-block-scoped-functions@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620"
- integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.16.7"
-
-"@babel/plugin-transform-block-scoping@^7.17.12":
- version "7.18.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.4.tgz#7988627b3e9186a13e4d7735dc9c34a056613fb9"
- integrity sha512-+Hq10ye+jlvLEogSOtq4mKvtk7qwcUQ1f0Mrueai866C82f844Yom2cttfJdMdqRLTxWpsbfbkIkOIfovyUQXw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-classes@^7.17.12":
- version "7.18.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.4.tgz#51310b812a090b846c784e47087fa6457baef814"
- integrity sha512-e42NSG2mlKWgxKUAD9EJJSkZxR67+wZqzNxLSpc51T8tRU5SLFHsPmgYR5yr7sdgX4u+iHA1C5VafJ6AyImV3A==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.16.7"
- "@babel/helper-environment-visitor" "^7.18.2"
- "@babel/helper-function-name" "^7.17.9"
- "@babel/helper-optimise-call-expression" "^7.16.7"
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-replace-supers" "^7.18.2"
- "@babel/helper-split-export-declaration" "^7.16.7"
- globals "^11.1.0"
-
-"@babel/plugin-transform-computed-properties@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz#bca616a83679698f3258e892ed422546e531387f"
- integrity sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-destructuring@^7.18.0":
- version "7.18.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.0.tgz#dc4f92587e291b4daa78aa20cc2d7a63aa11e858"
- integrity sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241"
- integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==
- dependencies:
- "@babel/helper-create-regexp-features-plugin" "^7.16.7"
- "@babel/helper-plugin-utils" "^7.16.7"
-
-"@babel/plugin-transform-duplicate-keys@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz#a09aa709a3310013f8e48e0e23bc7ace0f21477c"
- integrity sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-exponentiation-operator@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b"
- integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==
- dependencies:
- "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7"
- "@babel/helper-plugin-utils" "^7.16.7"
-
-"@babel/plugin-transform-flow-strip-types@^7.16.0":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.17.12.tgz#5e070f99a4152194bd9275de140e83a92966cab3"
- integrity sha512-g8cSNt+cHCpG/uunPQELdq/TeV3eg1OLJYwxypwHtAWo9+nErH3lQx9CSO2uI9lF74A0mR0t4KoMjs1snSgnTw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/plugin-syntax-flow" "^7.17.12"
-
-"@babel/plugin-transform-for-of@^7.18.1":
- version "7.18.1"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.1.tgz#ed14b657e162b72afbbb2b4cdad277bf2bb32036"
- integrity sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-function-name@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf"
- integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==
- dependencies:
- "@babel/helper-compilation-targets" "^7.16.7"
- "@babel/helper-function-name" "^7.16.7"
- "@babel/helper-plugin-utils" "^7.16.7"
-
-"@babel/plugin-transform-literals@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz#97131fbc6bbb261487105b4b3edbf9ebf9c830ae"
- integrity sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-member-expression-literals@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384"
- integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.16.7"
-
-"@babel/plugin-transform-modules-amd@^7.18.0":
- version "7.18.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.0.tgz#7ef1002e67e36da3155edc8bf1ac9398064c02ed"
- integrity sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA==
- dependencies:
- "@babel/helper-module-transforms" "^7.18.0"
- "@babel/helper-plugin-utils" "^7.17.12"
- babel-plugin-dynamic-import-node "^2.3.3"
-
-"@babel/plugin-transform-modules-commonjs@^7.18.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.2.tgz#1aa8efa2e2a6e818b6a7f2235fceaf09bdb31e9e"
- integrity sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ==
- dependencies:
- "@babel/helper-module-transforms" "^7.18.0"
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-simple-access" "^7.18.2"
- babel-plugin-dynamic-import-node "^2.3.3"
-
-"@babel/plugin-transform-modules-systemjs@^7.18.0":
- version "7.18.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.4.tgz#3d6fd9868c735cce8f38d6ae3a407fb7e61e6d46"
- integrity sha512-lH2UaQaHVOAeYrUUuZ8i38o76J/FnO8vu21OE+tD1MyP9lxdZoSfz+pDbWkq46GogUrdrMz3tiz/FYGB+bVThg==
- dependencies:
- "@babel/helper-hoist-variables" "^7.16.7"
- "@babel/helper-module-transforms" "^7.18.0"
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-validator-identifier" "^7.16.7"
- babel-plugin-dynamic-import-node "^2.3.3"
-
-"@babel/plugin-transform-modules-umd@^7.18.0":
- version "7.18.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.0.tgz#56aac64a2c2a1922341129a4597d1fd5c3ff020f"
- integrity sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA==
- dependencies:
- "@babel/helper-module-transforms" "^7.18.0"
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-named-capturing-groups-regex@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz#9c4a5a5966e0434d515f2675c227fd8cc8606931"
- integrity sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA==
- dependencies:
- "@babel/helper-create-regexp-features-plugin" "^7.17.12"
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-new-target@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.17.12.tgz#10842cd605a620944e81ea6060e9e65c265742e3"
- integrity sha512-CaOtzk2fDYisbjAD4Sd1MTKGVIpRtx9bWLyj24Y/k6p4s4gQ3CqDGJauFJxt8M/LEx003d0i3klVqnN73qvK3w==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-object-super@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94"
- integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.16.7"
- "@babel/helper-replace-supers" "^7.16.7"
-
-"@babel/plugin-transform-parameters@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz#eb467cd9586ff5ff115a9880d6fdbd4a846b7766"
- integrity sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-property-literals@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55"
- integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.16.7"
-
-"@babel/plugin-transform-react-constant-elements@^7.12.1":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.17.12.tgz#cc580857696b6dd9e5e3d079e673d060a0657f37"
- integrity sha512-maEkX2xs2STuv2Px8QuqxqjhV2LsFobT1elCgyU5704fcyTu9DyD/bJXxD/mrRiVyhpHweOQ00OJ5FKhHq9oEw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340"
- integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.16.7"
-
-"@babel/plugin-transform-react-jsx-development@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8"
- integrity sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==
- dependencies:
- "@babel/plugin-transform-react-jsx" "^7.16.7"
-
-"@babel/plugin-transform-react-jsx@^7.16.7", "@babel/plugin-transform-react-jsx@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.12.tgz#2aa20022709cd6a3f40b45d60603d5f269586dba"
- integrity sha512-Lcaw8bxd1DKht3thfD4A12dqo1X16he1Lm8rIv8sTwjAYNInRS1qHa9aJoqvzpscItXvftKDCfaEQzwoVyXpEQ==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.16.7"
- "@babel/helper-module-imports" "^7.16.7"
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/plugin-syntax-jsx" "^7.17.12"
- "@babel/types" "^7.17.12"
-
-"@babel/plugin-transform-react-pure-annotations@^7.16.7":
- version "7.18.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.18.0.tgz#ef82c8e310913f3522462c9ac967d395092f1954"
- integrity sha512-6+0IK6ouvqDn9bmEG7mEyF/pwlJXVj5lwydybpyyH3D0A7Hftk+NCTdYjnLNZksn261xaOV5ksmp20pQEmc2RQ==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.16.7"
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-regenerator@^7.18.0":
- version "7.18.0"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.0.tgz#44274d655eb3f1af3f3a574ba819d3f48caf99d5"
- integrity sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- regenerator-transform "^0.15.0"
-
-"@babel/plugin-transform-reserved-words@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz#7dbd349f3cdffba751e817cf40ca1386732f652f"
- integrity sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-runtime@^7.16.4":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.2.tgz#04637de1e45ae8847ff14b9beead09c33d34374d"
- integrity sha512-mr1ufuRMfS52ttq+1G1PD8OJNqgcTFjq3hwn8SZ5n1x1pBhi0E36rYMdTK0TsKtApJ4lDEdfXJwtGobQMHSMPg==
- dependencies:
- "@babel/helper-module-imports" "^7.16.7"
- "@babel/helper-plugin-utils" "^7.17.12"
- babel-plugin-polyfill-corejs2 "^0.3.0"
- babel-plugin-polyfill-corejs3 "^0.5.0"
- babel-plugin-polyfill-regenerator "^0.3.0"
- semver "^6.3.0"
-
-"@babel/plugin-transform-shorthand-properties@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a"
- integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.16.7"
-
-"@babel/plugin-transform-spread@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz#c112cad3064299f03ea32afed1d659223935d1f5"
- integrity sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
-
-"@babel/plugin-transform-sticky-regex@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660"
- integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.16.7"
-
-"@babel/plugin-transform-template-literals@^7.18.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.2.tgz#31ed6915721864847c48b656281d0098ea1add28"
- integrity sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-typeof-symbol@^7.17.12":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz#0f12f57ac35e98b35b4ed34829948d42bd0e6889"
- integrity sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
-
-"@babel/plugin-transform-typescript@^7.17.12":
- version "7.18.4"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.4.tgz#587eaf6a39edb8c06215e550dc939faeadd750bf"
- integrity sha512-l4vHuSLUajptpHNEOUDEGsnpl9pfRLsN1XUoDQDD/YBuXTM+v37SHGS+c6n4jdcZy96QtuUuSvZYMLSSsjH8Mw==
- dependencies:
- "@babel/helper-create-class-features-plugin" "^7.18.0"
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/plugin-syntax-typescript" "^7.17.12"
-
-"@babel/plugin-transform-unicode-escapes@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3"
- integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==
- dependencies:
- "@babel/helper-plugin-utils" "^7.16.7"
-
-"@babel/plugin-transform-unicode-regex@^7.16.7":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2"
- integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==
- dependencies:
- "@babel/helper-create-regexp-features-plugin" "^7.16.7"
- "@babel/helper-plugin-utils" "^7.16.7"
-
-"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.2.tgz#f47d3000a098617926e674c945d95a28cb90977a"
- integrity sha512-PfpdxotV6afmXMU47S08F9ZKIm2bJIQ0YbAAtDfIENX7G1NUAXigLREh69CWDjtgUy7dYn7bsMzkgdtAlmS68Q==
- dependencies:
- "@babel/compat-data" "^7.17.10"
- "@babel/helper-compilation-targets" "^7.18.2"
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-validator-option" "^7.16.7"
- "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.17.12"
- "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.17.12"
- "@babel/plugin-proposal-async-generator-functions" "^7.17.12"
- "@babel/plugin-proposal-class-properties" "^7.17.12"
- "@babel/plugin-proposal-class-static-block" "^7.18.0"
- "@babel/plugin-proposal-dynamic-import" "^7.16.7"
- "@babel/plugin-proposal-export-namespace-from" "^7.17.12"
- "@babel/plugin-proposal-json-strings" "^7.17.12"
- "@babel/plugin-proposal-logical-assignment-operators" "^7.17.12"
- "@babel/plugin-proposal-nullish-coalescing-operator" "^7.17.12"
- "@babel/plugin-proposal-numeric-separator" "^7.16.7"
- "@babel/plugin-proposal-object-rest-spread" "^7.18.0"
- "@babel/plugin-proposal-optional-catch-binding" "^7.16.7"
- "@babel/plugin-proposal-optional-chaining" "^7.17.12"
- "@babel/plugin-proposal-private-methods" "^7.17.12"
- "@babel/plugin-proposal-private-property-in-object" "^7.17.12"
- "@babel/plugin-proposal-unicode-property-regex" "^7.17.12"
- "@babel/plugin-syntax-async-generators" "^7.8.4"
- "@babel/plugin-syntax-class-properties" "^7.12.13"
- "@babel/plugin-syntax-class-static-block" "^7.14.5"
- "@babel/plugin-syntax-dynamic-import" "^7.8.3"
- "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
- "@babel/plugin-syntax-import-assertions" "^7.17.12"
- "@babel/plugin-syntax-json-strings" "^7.8.3"
- "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
- "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
- "@babel/plugin-syntax-numeric-separator" "^7.10.4"
- "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
- "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
- "@babel/plugin-syntax-optional-chaining" "^7.8.3"
- "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
- "@babel/plugin-syntax-top-level-await" "^7.14.5"
- "@babel/plugin-transform-arrow-functions" "^7.17.12"
- "@babel/plugin-transform-async-to-generator" "^7.17.12"
- "@babel/plugin-transform-block-scoped-functions" "^7.16.7"
- "@babel/plugin-transform-block-scoping" "^7.17.12"
- "@babel/plugin-transform-classes" "^7.17.12"
- "@babel/plugin-transform-computed-properties" "^7.17.12"
- "@babel/plugin-transform-destructuring" "^7.18.0"
- "@babel/plugin-transform-dotall-regex" "^7.16.7"
- "@babel/plugin-transform-duplicate-keys" "^7.17.12"
- "@babel/plugin-transform-exponentiation-operator" "^7.16.7"
- "@babel/plugin-transform-for-of" "^7.18.1"
- "@babel/plugin-transform-function-name" "^7.16.7"
- "@babel/plugin-transform-literals" "^7.17.12"
- "@babel/plugin-transform-member-expression-literals" "^7.16.7"
- "@babel/plugin-transform-modules-amd" "^7.18.0"
- "@babel/plugin-transform-modules-commonjs" "^7.18.2"
- "@babel/plugin-transform-modules-systemjs" "^7.18.0"
- "@babel/plugin-transform-modules-umd" "^7.18.0"
- "@babel/plugin-transform-named-capturing-groups-regex" "^7.17.12"
- "@babel/plugin-transform-new-target" "^7.17.12"
- "@babel/plugin-transform-object-super" "^7.16.7"
- "@babel/plugin-transform-parameters" "^7.17.12"
- "@babel/plugin-transform-property-literals" "^7.16.7"
- "@babel/plugin-transform-regenerator" "^7.18.0"
- "@babel/plugin-transform-reserved-words" "^7.17.12"
- "@babel/plugin-transform-shorthand-properties" "^7.16.7"
- "@babel/plugin-transform-spread" "^7.17.12"
- "@babel/plugin-transform-sticky-regex" "^7.16.7"
- "@babel/plugin-transform-template-literals" "^7.18.2"
- "@babel/plugin-transform-typeof-symbol" "^7.17.12"
- "@babel/plugin-transform-unicode-escapes" "^7.16.7"
- "@babel/plugin-transform-unicode-regex" "^7.16.7"
- "@babel/preset-modules" "^0.1.5"
- "@babel/types" "^7.18.2"
- babel-plugin-polyfill-corejs2 "^0.3.0"
- babel-plugin-polyfill-corejs3 "^0.5.0"
- babel-plugin-polyfill-regenerator "^0.3.0"
- core-js-compat "^3.22.1"
- semver "^6.3.0"
-
-"@babel/preset-modules@^0.1.5":
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9"
- integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.0.0"
- "@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
- "@babel/plugin-transform-dotall-regex" "^7.4.4"
- "@babel/types" "^7.4.4"
- esutils "^2.0.2"
-
-"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.16.0":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.17.12.tgz#62adbd2d1870c0de3893095757ed5b00b492ab3d"
- integrity sha512-h5U+rwreXtZaRBEQhW1hOJLMq8XNJBQ/9oymXiCXTuT/0uOwpbT0gUt+sXeOqoXBgNuUKI7TaObVwoEyWkpFgA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-validator-option" "^7.16.7"
- "@babel/plugin-transform-react-display-name" "^7.16.7"
- "@babel/plugin-transform-react-jsx" "^7.17.12"
- "@babel/plugin-transform-react-jsx-development" "^7.16.7"
- "@babel/plugin-transform-react-pure-annotations" "^7.16.7"
-
-"@babel/preset-typescript@^7.16.0":
- version "7.17.12"
- resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.17.12.tgz#40269e0a0084d56fc5731b6c40febe1c9a4a3e8c"
- integrity sha512-S1ViF8W2QwAKUGJXxP9NAfNaqGDdEBJKpYkxHf5Yy2C4NPPzXGeR3Lhk7G8xJaaLcFTRfNjVbtbVtm8Gb0mqvg==
- dependencies:
- "@babel/helper-plugin-utils" "^7.17.12"
- "@babel/helper-validator-option" "^7.16.7"
- "@babel/plugin-transform-typescript" "^7.17.12"
-
-"@babel/runtime-corejs3@^7.10.2":
- version "7.18.3"
- resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.3.tgz#52f0241a31e0ec61a6187530af6227c2846bd60c"
- integrity sha512-l4ddFwrc9rnR+EJsHsh+TJ4A35YqQz/UqcjtlX2ov53hlJYG5CxtQmNZxyajwDVmCxwy++rtvGU5HazCK4W41Q==
- dependencies:
- core-js-pure "^3.20.2"
- regenerator-runtime "^0.13.4"
-
-"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
- version "7.18.3"
- resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4"
- integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==
- dependencies:
- regenerator-runtime "^0.13.4"
-
-"@babel/template@^7.16.7", "@babel/template@^7.3.3":
- version "7.16.7"
- resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
- integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==
- dependencies:
- "@babel/code-frame" "^7.16.7"
- "@babel/parser" "^7.16.7"
- "@babel/types" "^7.16.7"
-
-"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.7.2":
- version "7.18.2"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.2.tgz#b77a52604b5cc836a9e1e08dca01cba67a12d2e8"
- integrity sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==
- dependencies:
- "@babel/code-frame" "^7.16.7"
- "@babel/generator" "^7.18.2"
- "@babel/helper-environment-visitor" "^7.18.2"
- "@babel/helper-function-name" "^7.17.9"
- "@babel/helper-hoist-variables" "^7.16.7"
- "@babel/helper-split-export-declaration" "^7.16.7"
- "@babel/parser" "^7.18.0"
- "@babel/types" "^7.18.2"
- debug "^4.1.0"
- globals "^11.1.0"
-
-"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.17.12", "@babel/types@^7.18.0", "@babel/types@^7.18.2", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
- version "7.18.4"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354"
- integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==
- dependencies:
- "@babel/helper-validator-identifier" "^7.16.7"
- to-fast-properties "^2.0.0"
-
-"@bcoe/v8-coverage@^0.2.3":
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
- integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
-
-"@braintree/sanitize-url@^6.0.0":
- version "6.0.4"
- resolved "https://npm.shopee.io/@braintree%2fsanitize-url/-/sanitize-url-6.0.4.tgz#923ca57e173c6b232bbbb07347b1be982f03e783"
- integrity sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==
-
-"@codemirror/autocomplete@^0.19.0":
- version "0.19.15"
- resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-0.19.15.tgz#061f09063dc2a68668d85d7ac8430c7bc6df1a82"
- integrity sha512-GQWzvvuXxNUyaEk+5gawbAD8s51/v2Chb++nx0e2eGWrphWk42isBtzOMdc3DxrxrZtPZ55q2ldNp+6G8KJLIQ==
- dependencies:
- "@codemirror/language" "^0.19.0"
- "@codemirror/state" "^0.19.4"
- "@codemirror/text" "^0.19.2"
- "@codemirror/tooltip" "^0.19.12"
- "@codemirror/view" "^0.19.0"
- "@lezer/common" "^0.15.0"
-
-"@codemirror/basic-setup@^0.19.1":
- version "0.19.3"
- resolved "https://registry.yarnpkg.com/@codemirror/basic-setup/-/basic-setup-0.19.3.tgz#b83e3ee43f632c0305f97771767d84c753d41a6b"
- integrity sha512-2hfO+QDk/HTpQzeYk1NyL1G9D5L7Sj78dtaQP8xBU42DKU9+OBPF5MdjLYnxP0jKzm6IfQfsLd89fnqW3rBVfQ==
- dependencies:
- "@codemirror/autocomplete" "^0.19.0"
- "@codemirror/closebrackets" "^0.19.0"
- "@codemirror/commands" "^0.19.0"
- "@codemirror/comment" "^0.19.0"
- "@codemirror/fold" "^0.19.0"
- "@codemirror/gutter" "^0.19.0"
- "@codemirror/highlight" "^0.19.0"
- "@codemirror/history" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@codemirror/lint" "^0.19.0"
- "@codemirror/matchbrackets" "^0.19.0"
- "@codemirror/rectangular-selection" "^0.19.2"
- "@codemirror/search" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@codemirror/view" "^0.19.31"
-
-"@codemirror/closebrackets@^0.19.0":
- version "0.19.2"
- resolved "https://registry.yarnpkg.com/@codemirror/closebrackets/-/closebrackets-0.19.2.tgz#ff74dd78218cee57172623eb9ebf7b669fa6f4d4"
- integrity sha512-ClMPzPcPP0eQiDcVjtVPl6OLxgdtZSYDazsvT0AKl70V1OJva0eHgl4/6kCW3RZ0pb2n34i9nJz4eXCmK+TYDA==
- dependencies:
- "@codemirror/language" "^0.19.0"
- "@codemirror/rangeset" "^0.19.0"
- "@codemirror/state" "^0.19.2"
- "@codemirror/text" "^0.19.0"
- "@codemirror/view" "^0.19.44"
-
-"@codemirror/commands@^0.19.0":
- version "0.19.8"
- resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-0.19.8.tgz#1f99c1a8bf200d17c4d6997379099459f3678107"
- integrity sha512-65LIMSGUGGpY3oH6mzV46YWRrgao6NmfJ+AuC7jNz3K5NPnH6GCV1H5I6SwOFyVbkiygGyd0EFwrWqywTBD1aw==
- dependencies:
- "@codemirror/language" "^0.19.0"
- "@codemirror/matchbrackets" "^0.19.0"
- "@codemirror/state" "^0.19.2"
- "@codemirror/text" "^0.19.6"
- "@codemirror/view" "^0.19.22"
- "@lezer/common" "^0.15.0"
-
-"@codemirror/comment@^0.19.0":
- version "0.19.1"
- resolved "https://registry.yarnpkg.com/@codemirror/comment/-/comment-0.19.1.tgz#7def8345eeb9095ef1ef33676fbde1ab4fe33fad"
- integrity sha512-uGKteBuVWAC6fW+Yt8u27DOnXMT/xV4Ekk2Z5mRsiADCZDqYvryrJd6PLL5+8t64BVyocwQwNfz1UswYS2CtFQ==
- dependencies:
- "@codemirror/state" "^0.19.9"
- "@codemirror/text" "^0.19.0"
- "@codemirror/view" "^0.19.0"
-
-"@codemirror/fold@^0.19.0":
- version "0.19.4"
- resolved "https://registry.yarnpkg.com/@codemirror/fold/-/fold-0.19.4.tgz#f2a17e508378d5a83dc587ed6f1a635969219a2b"
- integrity sha512-0SNSkRSOa6gymD6GauHa3sxiysjPhUC0SRVyTlvL52o0gz9GHdc8kNqNQskm3fBtGGOiSriGwF/kAsajRiGhVw==
- dependencies:
- "@codemirror/gutter" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@codemirror/rangeset" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@codemirror/view" "^0.19.22"
-
-"@codemirror/gutter@^0.19.0", "@codemirror/gutter@^0.19.4":
- version "0.19.9"
- resolved "https://registry.yarnpkg.com/@codemirror/gutter/-/gutter-0.19.9.tgz#bbb69f4d49570d9c1b3f3df5d134980c516cd42b"
- integrity sha512-PFrtmilahin1g6uL27aG5tM/rqR9DZzZYZsIrCXA5Uc2OFTFqx4owuhoU9hqfYxHp5ovfvBwQ+txFzqS4vog6Q==
- dependencies:
- "@codemirror/rangeset" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@codemirror/view" "^0.19.23"
-
-"@codemirror/highlight@^0.19.0", "@codemirror/highlight@^0.19.6", "@codemirror/highlight@^0.19.7":
- version "0.19.8"
- resolved "https://registry.yarnpkg.com/@codemirror/highlight/-/highlight-0.19.8.tgz#a95aee8ae4389b01f820aa79c48f7b4388087d92"
- integrity sha512-v/lzuHjrYR8MN2mEJcUD6fHSTXXli9C1XGYpr+ElV6fLBIUhMTNKR3qThp611xuWfXfwDxeL7ppcbkM/MzPV3A==
- dependencies:
- "@codemirror/language" "^0.19.0"
- "@codemirror/rangeset" "^0.19.0"
- "@codemirror/state" "^0.19.3"
- "@codemirror/view" "^0.19.39"
- "@lezer/common" "^0.15.0"
- style-mod "^4.0.0"
-
-"@codemirror/history@^0.19.0":
- version "0.19.2"
- resolved "https://registry.yarnpkg.com/@codemirror/history/-/history-0.19.2.tgz#25e3fda755f77ac1223a6ae6e9d7899f5919265e"
- integrity sha512-unhP4t3N2smzmHoo/Yio6ueWi+il8gm9VKrvi6wlcdGH5fOfVDNkmjHQ495SiR+EdOG35+3iNebSPYww0vN7ow==
- dependencies:
- "@codemirror/state" "^0.19.2"
- "@codemirror/view" "^0.19.0"
-
-"@codemirror/lang-cpp@^0.19.0":
- version "0.19.1"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-cpp/-/lang-cpp-0.19.1.tgz#7b25e85077bf695106d344a2bd790575093a34fb"
- integrity sha512-BGvZkfcqcalAwxocuE9DhH6gqflm5IjL/8mGTzc8bHzeP1N4innK8qo2G69ohEML4LDZv4WyXc3y4C9/zsGCGQ==
- dependencies:
- "@codemirror/highlight" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@lezer/cpp" "^0.15.0"
-
-"@codemirror/lang-css@^0.19.0":
- version "0.19.3"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-css/-/lang-css-0.19.3.tgz#7a17adf78c6fcdab4ad5ee4e360631c41e949e4a"
- integrity sha512-tyCUJR42/UlfOPLb94/p7dN+IPsYSIzHbAHP2KQHANj0I+Orqp+IyIOS++M8TuCX4zkWh9dvi8s92yy/Tn8Ifg==
- dependencies:
- "@codemirror/autocomplete" "^0.19.0"
- "@codemirror/highlight" "^0.19.6"
- "@codemirror/language" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@lezer/css" "^0.15.2"
-
-"@codemirror/lang-html@^0.19.0":
- version "0.19.4"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-html/-/lang-html-0.19.4.tgz#e6eec28462f18842a0e108732a214a7416b5e333"
- integrity sha512-GpiEikNuCBeFnS+/TJSeanwqaOfNm8Kkp9WpVNEPZCLyW1mAMCuFJu/3xlWYeWc778Hc3vJqGn3bn+cLNubgCA==
- dependencies:
- "@codemirror/autocomplete" "^0.19.0"
- "@codemirror/highlight" "^0.19.6"
- "@codemirror/lang-css" "^0.19.0"
- "@codemirror/lang-javascript" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@lezer/common" "^0.15.0"
- "@lezer/html" "^0.15.0"
-
-"@codemirror/lang-java@^0.19.0":
- version "0.19.1"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-java/-/lang-java-0.19.1.tgz#c6bafabf3e1951d7a6a5bd4670afd277b909608c"
- integrity sha512-yA3kcW2GgY0mC2a9dE+uRxGxPWeykfE/GqEPk4TSmhuU4ndmyDgM5QQP7pgnYSZmv2vKoyf4x7NMg8AF7lKXHQ==
- dependencies:
- "@codemirror/highlight" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@lezer/java" "^0.15.0"
-
-"@codemirror/lang-javascript@^0.19.0":
- version "0.19.7"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-javascript/-/lang-javascript-0.19.7.tgz#84581ef6abf2a16d78f017ffc96c2d6227de5eb5"
- integrity sha512-DL9f3JLqOEHH9cIwEqqjnP5bkjdVXeECksLtV+/MbPm+l4H+AG+PkwZaJQ2oR1GfPZKh8MVSIE94aGWNkJP8WQ==
- dependencies:
- "@codemirror/autocomplete" "^0.19.0"
- "@codemirror/highlight" "^0.19.7"
- "@codemirror/language" "^0.19.0"
- "@codemirror/lint" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@codemirror/view" "^0.19.0"
- "@lezer/javascript" "^0.15.1"
-
-"@codemirror/lang-json@^0.19.0":
- version "0.19.2"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-json/-/lang-json-0.19.2.tgz#b311a0c16382343261fdc3cbda72f09a61ade7db"
- integrity sha512-fgUWR58Is59P5D/tiazX6oTczioOCDYqjFT5PEBAmLBFMSsRqcnJE0xNO1snrhg7pWEFDq5wR/oN0eZhkeR6Gg==
- dependencies:
- "@codemirror/highlight" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@lezer/json" "^0.15.0"
-
-"@codemirror/lang-markdown@^0.19.0", "@codemirror/lang-markdown@^0.19.6":
- version "0.19.6"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-markdown/-/lang-markdown-0.19.6.tgz#761301d276fcfbdf88440f0333785efd71c2a4f5"
- integrity sha512-ojoHeLgv1Rfu0GNGsU0bCtXAIp5dy4VKjndHScITQdlCkS/+SAIfuoeowEx+nMAQwTxI+/9fQZ3xdZVznGFYug==
- dependencies:
- "@codemirror/highlight" "^0.19.0"
- "@codemirror/lang-html" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@codemirror/state" "^0.19.3"
- "@codemirror/view" "^0.19.0"
- "@lezer/common" "^0.15.0"
- "@lezer/markdown" "^0.15.0"
-
-"@codemirror/lang-php@^0.19.0":
- version "0.19.1"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-php/-/lang-php-0.19.1.tgz#ff17f844376988140fc01bddd5690c9644151010"
- integrity sha512-Q6djLACHu1J6XbnxWlEPCiyqqDrlZLi9QtjY6b9vqdkq/GOsNaXVv44nDY8DD6Bxi5yYRTJ3yh8XzsKuJgztjQ==
- dependencies:
- "@codemirror/highlight" "^0.19.0"
- "@codemirror/lang-html" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@lezer/common" "^0.15.0"
- "@lezer/php" "^0.15.0"
-
-"@codemirror/lang-python@^0.19.0":
- version "0.19.5"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-python/-/lang-python-0.19.5.tgz#52ad893c45d9a20fdd9841ebfe1d6de52e1dead1"
- integrity sha512-MQf7t0k6+i9KCzlFCI8EY+jjwyXLy5AwjmXsMyMCMbOw/97j70jFZYrs7Mm7RJakNE2rypWhnLGlyBTSYMqR5g==
- dependencies:
- "@codemirror/highlight" "^0.19.7"
- "@codemirror/language" "^0.19.0"
- "@lezer/python" "^0.15.0"
-
-"@codemirror/lang-rust@^0.19.0":
- version "0.19.2"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-rust/-/lang-rust-0.19.2.tgz#39e32e1a817fafdd8eac23a8904785cf0fc9cc29"
- integrity sha512-SEXsO7Qf2gktRvVhHMc0Mq4HzPBpFcQlrlcinafy6VFXavWs+QAIB8UAuLG/igOc3PrIHbZFlyEhVUIGstox8w==
- dependencies:
- "@codemirror/highlight" "^0.19.7"
- "@codemirror/language" "^0.19.0"
- "@lezer/rust" "^0.15.0"
-
-"@codemirror/lang-sql@^0.19.0":
- version "0.19.4"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-sql/-/lang-sql-0.19.4.tgz#2baa6f0c341cc6cd075a4f313ac78f3f822b3a39"
- integrity sha512-4FqLC8aNe1iCDyAWbJmSqa8K7rgz2xTwW36V35z4oiyLoyOLsCayKIwoQqp5DNIq2ckGCsyzotgxXKpgtg/pgg==
- dependencies:
- "@codemirror/autocomplete" "^0.19.0"
- "@codemirror/highlight" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@lezer/lr" "^0.15.0"
-
-"@codemirror/lang-wast@^0.19.0":
- version "0.19.0"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-wast/-/lang-wast-0.19.0.tgz#abd4534e206f97214969af6ae68b84636a6d169b"
- integrity sha512-mr/Bp4k8+fJ0P8/Q6L45pnX7/bDBk4VP8ahYrTdvHo+UaOqBBhBFtBqBikvX8ZDQiUTfuZ4tnJE2QtOvmFsuzg==
- dependencies:
- "@codemirror/highlight" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@lezer/lr" "^0.15.0"
-
-"@codemirror/lang-xml@^0.19.0":
- version "0.19.2"
- resolved "https://registry.yarnpkg.com/@codemirror/lang-xml/-/lang-xml-0.19.2.tgz#877bd064bcd396435c628e476bfccb22d4977a0a"
- integrity sha512-9VIjxvqcH1sk8bmYbxQon0lXhVZgdHdfjGes+e4Akgvb43aMBDNvIQVALwrCb+XMEHTxLUMQtrsBN0G64yCUXw==
- dependencies:
- "@codemirror/autocomplete" "^0.19.0"
- "@codemirror/highlight" "^0.19.6"
- "@codemirror/language" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@lezer/common" "^0.15.0"
- "@lezer/xml" "^0.15.0"
-
-"@codemirror/language-data@^0.19.2":
- version "0.19.2"
- resolved "https://registry.yarnpkg.com/@codemirror/language-data/-/language-data-0.19.2.tgz#39260c6d985f79701746632ab94933de6b20eed9"
- integrity sha512-O38TaBfzqs5vK8Z+ZlAmaGqciQxgtAXacOTSq22ZLrsKmYMbeFZNHCqDL6VMG2wOt1jtRnfJD56chONwaPRUVQ==
- dependencies:
- "@codemirror/lang-cpp" "^0.19.0"
- "@codemirror/lang-css" "^0.19.0"
- "@codemirror/lang-html" "^0.19.0"
- "@codemirror/lang-java" "^0.19.0"
- "@codemirror/lang-javascript" "^0.19.0"
- "@codemirror/lang-json" "^0.19.0"
- "@codemirror/lang-markdown" "^0.19.0"
- "@codemirror/lang-php" "^0.19.0"
- "@codemirror/lang-python" "^0.19.0"
- "@codemirror/lang-rust" "^0.19.0"
- "@codemirror/lang-sql" "^0.19.0"
- "@codemirror/lang-wast" "^0.19.0"
- "@codemirror/lang-xml" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@codemirror/legacy-modes" "^0.19.0"
- "@codemirror/stream-parser" "^0.19.0"
-
-"@codemirror/language@^0.19.0":
- version "0.19.10"
- resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-0.19.10.tgz#c3d1330fa5de778c6b6b5177af5572a3d9d596b5"
- integrity sha512-yA0DZ3RYn2CqAAGW62VrU8c4YxscMQn45y/I9sjBlqB1e2OTQLg4CCkMBuMSLXk4xaqjlsgazeOQWaJQOKfV8Q==
- dependencies:
- "@codemirror/state" "^0.19.0"
- "@codemirror/text" "^0.19.0"
- "@codemirror/view" "^0.19.0"
- "@lezer/common" "^0.15.5"
- "@lezer/lr" "^0.15.0"
-
-"@codemirror/legacy-modes@^0.19.0":
- version "0.19.1"
- resolved "https://registry.yarnpkg.com/@codemirror/legacy-modes/-/legacy-modes-0.19.1.tgz#7dc3b5df1842060648f75764ab6919fcfce3ea1a"
- integrity sha512-vYPLsD/ON+3SXhlGj9Qb3fpFNNU3Ya/AtDiv/g3OyqVzhh5vs5rAnOvk8xopGWRwppdhlNPD9VyXjiOmZUQtmQ==
- dependencies:
- "@codemirror/stream-parser" "^0.19.0"
-
-"@codemirror/lint@^0.19.0":
- version "0.19.6"
- resolved "https://registry.yarnpkg.com/@codemirror/lint/-/lint-0.19.6.tgz#0379688da3e16739db4a6304c73db857ca85d7ec"
- integrity sha512-Pbw1Y5kHVs2J+itQ0uez3dI4qY9ApYVap7eNfV81x1/3/BXgBkKfadaw0gqJ4h4FDG7OnJwb0VbPsjJQllHjaA==
- dependencies:
- "@codemirror/gutter" "^0.19.4"
- "@codemirror/panel" "^0.19.0"
- "@codemirror/rangeset" "^0.19.1"
- "@codemirror/state" "^0.19.4"
- "@codemirror/tooltip" "^0.19.16"
- "@codemirror/view" "^0.19.22"
- crelt "^1.0.5"
-
-"@codemirror/matchbrackets@^0.19.0":
- version "0.19.4"
- resolved "https://registry.yarnpkg.com/@codemirror/matchbrackets/-/matchbrackets-0.19.4.tgz#50b5188eb2d53f32598dca906bf5fd66626a9ebc"
- integrity sha512-VFkaOKPNudAA5sGP1zikRHCEKU0hjYmkKpr04pybUpQvfTvNJXlReCyP0rvH/1iEwAGPL990ZTT+QrLdu4MeEA==
- dependencies:
- "@codemirror/language" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@codemirror/view" "^0.19.0"
- "@lezer/common" "^0.15.0"
-
-"@codemirror/panel@^0.19.0":
- version "0.19.1"
- resolved "https://registry.yarnpkg.com/@codemirror/panel/-/panel-0.19.1.tgz#bf77d27b962cf16357139e50864d0eb69d634441"
- integrity sha512-sYeOCMA3KRYxZYJYn5PNlt9yNsjy3zTNTrbYSfVgjgL9QomIVgOJWPO5hZ2sTN8lufO6lw0vTBsIPL9MSidmBg==
- dependencies:
- "@codemirror/state" "^0.19.0"
- "@codemirror/view" "^0.19.0"
-
-"@codemirror/rangeset@^0.19.0", "@codemirror/rangeset@^0.19.1", "@codemirror/rangeset@^0.19.5":
- version "0.19.9"
- resolved "https://registry.yarnpkg.com/@codemirror/rangeset/-/rangeset-0.19.9.tgz#e80895de93c39dc7899f5be31d368c9d88aa4efc"
- integrity sha512-V8YUuOvK+ew87Xem+71nKcqu1SXd5QROMRLMS/ljT5/3MCxtgrRie1Cvild0G/Z2f1fpWxzX78V0U4jjXBorBQ==
- dependencies:
- "@codemirror/state" "^0.19.0"
-
-"@codemirror/rectangular-selection@^0.19.2":
- version "0.19.2"
- resolved "https://registry.yarnpkg.com/@codemirror/rectangular-selection/-/rectangular-selection-0.19.2.tgz#caa60114421b5c43244494c94203b65f666edb53"
- integrity sha512-AXK/p5eGwFJ9GJcLfntqN4dgY+XiIF7eHfXNQJX5HhQLSped2wJE6WuC1rMEaOlcpOqlb9mrNi/ZdUjSIj9mbA==
- dependencies:
- "@codemirror/state" "^0.19.0"
- "@codemirror/text" "^0.19.4"
- "@codemirror/view" "^0.19.48"
-
-"@codemirror/search@^0.19.0":
- version "0.19.10"
- resolved "https://registry.yarnpkg.com/@codemirror/search/-/search-0.19.10.tgz#4b0d91c53278db05088624ae04f164d66fd581cd"
- integrity sha512-qjubm69HJixPBWzI6HeEghTWOOD8NXiHOTRNvdizqs8xWRuFChq9zkjD3XiAJ7GXSTzCuQJnAP9DBBGCLq4ZIA==
- dependencies:
- "@codemirror/panel" "^0.19.0"
- "@codemirror/rangeset" "^0.19.0"
- "@codemirror/state" "^0.19.3"
- "@codemirror/text" "^0.19.0"
- "@codemirror/view" "^0.19.34"
- crelt "^1.0.5"
-
-"@codemirror/state@^0.19.0", "@codemirror/state@^0.19.2", "@codemirror/state@^0.19.3", "@codemirror/state@^0.19.4", "@codemirror/state@^0.19.9":
- version "0.19.9"
- resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-0.19.9.tgz#b797f9fbc204d6dc7975485e231693c09001b0dd"
- integrity sha512-psOzDolKTZkx4CgUqhBQ8T8gBc0xN5z4gzed109aF6x7D7umpDRoimacI/O6d9UGuyl4eYuDCZmDFr2Rq7aGOw==
- dependencies:
- "@codemirror/text" "^0.19.0"
-
-"@codemirror/stream-parser@^0.19.0":
- version "0.19.9"
- resolved "https://registry.yarnpkg.com/@codemirror/stream-parser/-/stream-parser-0.19.9.tgz#34955ea91a8047cf72abebd5ce28f0d332aeca48"
- integrity sha512-WTmkEFSRCetpk8xIOvV2yyXdZs3DgYckM0IP7eFi4ewlxWnJO/H4BeJZLs4wQaydWsAqTQoDyIwNH1BCzK5LUQ==
- dependencies:
- "@codemirror/highlight" "^0.19.0"
- "@codemirror/language" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@codemirror/text" "^0.19.0"
- "@lezer/common" "^0.15.0"
- "@lezer/lr" "^0.15.0"
-
-"@codemirror/text@^0.19.0", "@codemirror/text@^0.19.2", "@codemirror/text@^0.19.4", "@codemirror/text@^0.19.6":
- version "0.19.6"
- resolved "https://registry.yarnpkg.com/@codemirror/text/-/text-0.19.6.tgz#9adcbd8137f69b75518eacd30ddb16fd67bbac45"
- integrity sha512-T9jnREMIygx+TPC1bOuepz18maGq/92q2a+n4qTqObKwvNMg+8cMTslb8yxeEDEq7S3kpgGWxgO1UWbQRij0dA==
-
-"@codemirror/theme-one-dark@^0.19.1":
- version "0.19.1"
- resolved "https://registry.yarnpkg.com/@codemirror/theme-one-dark/-/theme-one-dark-0.19.1.tgz#648b9cbe37186a2b7bd2a83fb483dc7aa18ce218"
- integrity sha512-8gc4c2k2o/EhyHoWkghCxp5vyDT96JaFGtRy35PHwIom0LZdx7aU4AbDUnITvwiFB+0+i54VO+WQjBqgTyJvqg==
- dependencies:
- "@codemirror/highlight" "^0.19.0"
- "@codemirror/state" "^0.19.0"
- "@codemirror/view" "^0.19.0"
-
-"@codemirror/tooltip@^0.19.12", "@codemirror/tooltip@^0.19.16":
- version "0.19.16"
- resolved "https://registry.yarnpkg.com/@codemirror/tooltip/-/tooltip-0.19.16.tgz#6ba2c43f9d8e3d943d9d7bbae22bf800f7726a22"
- integrity sha512-zxKDHryUV5/RS45AQL+wOeN+i7/l81wK56OMnUPoTSzCWNITfxHn7BToDsjtrRKbzHqUxKYmBnn/4hPjpZ4WJQ==
- dependencies:
- "@codemirror/state" "^0.19.0"
- "@codemirror/view" "^0.19.0"
-
-"@codemirror/view@^0.19.0", "@codemirror/view@^0.19.22", "@codemirror/view@^0.19.23", "@codemirror/view@^0.19.31", "@codemirror/view@^0.19.34", "@codemirror/view@^0.19.39", "@codemirror/view@^0.19.44", "@codemirror/view@^0.19.45", "@codemirror/view@^0.19.48":
- version "0.19.48"
- resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-0.19.48.tgz#1c657e2b0f8ed896ac6448d6e2215ab115e2a0fc"
- integrity sha512-0eg7D2Nz4S8/caetCTz61rK0tkHI17V/d15Jy0kLOT8dTLGGNJUponDnW28h2B6bERmPlVHKh8MJIr5OCp1nGw==
- dependencies:
- "@codemirror/rangeset" "^0.19.5"
- "@codemirror/state" "^0.19.3"
- "@codemirror/text" "^0.19.0"
- style-mod "^4.0.0"
- w3c-keyname "^2.2.4"
-
-"@csstools/normalize.css@*":
- version "12.0.0"
- resolved "https://registry.yarnpkg.com/@csstools/normalize.css/-/normalize.css-12.0.0.tgz#a9583a75c3f150667771f30b60d9f059473e62c4"
- integrity sha512-M0qqxAcwCsIVfpFQSlGN5XjXWu8l5JDZN+fPt1LeW5SZexQTgnaEvgXAY+CeygRw0EeppWHi12JxESWiWrB0Sg==
-
-"@csstools/postcss-cascade-layers@^1.0.2":
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.2.tgz#7c48b5f773c4cdcdc6b57d6099fbdc2332e12219"
- integrity sha512-n5fSd3N/RTLjwC6TLnHjlVEt5tRg6S6Pu+LpRgXayX0QVJHvlMzE3+R12cd2F0we8WB4OE8o5r5iWgmBPpqUyQ==
- dependencies:
- "@csstools/selector-specificity" "^1.0.0"
- postcss-selector-parser "^6.0.10"
-
-"@csstools/postcss-color-function@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-color-function/-/postcss-color-function-1.1.0.tgz#229966327747f58fbe586de35daa139db3ce1e5d"
- integrity sha512-5D5ND/mZWcQoSfYnSPsXtuiFxhzmhxt6pcjrFLJyldj+p0ZN2vvRpYNX+lahFTtMhAYOa2WmkdGINr0yP0CvGA==
- dependencies:
- "@csstools/postcss-progressive-custom-properties" "^1.1.0"
- postcss-value-parser "^4.2.0"
-
-"@csstools/postcss-font-format-keywords@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.0.tgz#7e7df948a83a0dfb7eb150a96e2390ac642356a1"
- integrity sha512-oO0cZt8do8FdVBX8INftvIA4lUrKUSCcWUf9IwH9IPWOgKT22oAZFXeHLoDK7nhB2SmkNycp5brxfNMRLIhd6Q==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-"@csstools/postcss-hwb-function@^1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.1.tgz#5224db711ed09a965f85c80c18144ac1c2702fce"
- integrity sha512-AMZwWyHbbNLBsDADWmoXT9A5yl5dsGEBeJSJRUJt8Y9n8Ziu7Wstt4MC8jtPW7xjcLecyfJwtnUTNSmOzcnWeg==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-"@csstools/postcss-ic-unit@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.0.tgz#f484db59fc94f35a21b6d680d23b0ec69b286b7f"
- integrity sha512-i4yps1mBp2ijrx7E96RXrQXQQHm6F4ym1TOD0D69/sjDjZvQ22tqiEvaNw7pFZTUO5b9vWRHzbHzP9+UKuw+bA==
- dependencies:
- "@csstools/postcss-progressive-custom-properties" "^1.1.0"
- postcss-value-parser "^4.2.0"
-
-"@csstools/postcss-is-pseudo-class@^2.0.4":
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.4.tgz#6e8b49b96a7d3346d5316bd773dcff9c983b4183"
- integrity sha512-T2Tmr5RIxkCEXxHwMVyValqwv3h5FTJPpmU8Mq/HDV+TY6C9srVaNMiMG/sp0QaxUnVQQrnXsuLU+1g2zrLDcQ==
- dependencies:
- "@csstools/selector-specificity" "^1.0.0"
- postcss-selector-parser "^6.0.10"
-
-"@csstools/postcss-normalize-display-values@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.0.tgz#ce698f688c28517447aedf15a9037987e3d2dc97"
- integrity sha512-bX+nx5V8XTJEmGtpWTO6kywdS725t71YSLlxWt78XoHUbELWgoCXeOFymRJmL3SU1TLlKSIi7v52EWqe60vJTQ==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-"@csstools/postcss-oklab-function@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.0.tgz#e9a269487a292e0930760948e923e1d46b638ee6"
- integrity sha512-e/Q5HopQzmnQgqimG9v3w2IG4VRABsBq3itOcn4bnm+j4enTgQZ0nWsaH/m9GV2otWGQ0nwccYL5vmLKyvP1ww==
- dependencies:
- "@csstools/postcss-progressive-custom-properties" "^1.1.0"
- postcss-value-parser "^4.2.0"
-
-"@csstools/postcss-progressive-custom-properties@^1.1.0", "@csstools/postcss-progressive-custom-properties@^1.3.0":
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz#542292558384361776b45c85226b9a3a34f276fa"
- integrity sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-"@csstools/postcss-stepped-value-functions@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.0.tgz#f8ffc05e163ba7bcbefc5fdcaf264ce9fd408c16"
- integrity sha512-q8c4bs1GumAiRenmFjASBcWSLKrbzHzWl6C2HcaAxAXIiL2rUlUWbqQZUjwVG5tied0rld19j/Mm90K3qI26vw==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-"@csstools/postcss-trigonometric-functions@^1.0.0":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.1.tgz#e36e61f445614193dbf6d3a8408709b0cf184a6f"
- integrity sha512-G78CY/+GePc6dDCTUbwI6TTFQ5fs3N9POHhI6v0QzteGpf6ylARiJUNz9HrRKi4eVYBNXjae1W2766iUEFxHlw==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-"@csstools/postcss-unset-value@^1.0.1":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.1.tgz#2cc020785db5ec82cc9444afe4cdae2a65445f89"
- integrity sha512-f1G1WGDXEU/RN1TWAxBPQgQudtLnLQPyiWdtypkPC+mVYNKFKH/HYXSxH4MVNqwF8M0eDsoiU7HumJHCg/L/jg==
-
-"@csstools/selector-specificity@1.0.0", "@csstools/selector-specificity@^1.0.0":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-1.0.0.tgz#91c560df2ed8d9700e4c7ed4ac21a3a322c9d975"
- integrity sha512-RkYG5KiGNX0fJ5YoI0f4Wfq2Yo74D25Hru4fxTOioYdQvHBxcrrtTTyT5Ozzh2ejcNrhFy7IEts2WyEY7yi5yw==
-
-"@emotion/babel-plugin@^11.7.1":
- version "11.9.2"
- resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.9.2.tgz#723b6d394c89fb2ef782229d92ba95a740576e95"
- integrity sha512-Pr/7HGH6H6yKgnVFNEj2MVlreu3ADqftqjqwUvDy/OJzKFgxKeTQ+eeUf20FOTuHVkDON2iNa25rAXVYtWJCjw==
- dependencies:
- "@babel/helper-module-imports" "^7.12.13"
- "@babel/plugin-syntax-jsx" "^7.12.13"
- "@babel/runtime" "^7.13.10"
- "@emotion/hash" "^0.8.0"
- "@emotion/memoize" "^0.7.5"
- "@emotion/serialize" "^1.0.2"
- babel-plugin-macros "^2.6.1"
- convert-source-map "^1.5.0"
- escape-string-regexp "^4.0.0"
- find-root "^1.1.0"
- source-map "^0.5.7"
- stylis "4.0.13"
-
-"@emotion/cache@^11.7.1":
- version "11.7.1"
- resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.7.1.tgz#08d080e396a42e0037848214e8aa7bf879065539"
- integrity sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==
- dependencies:
- "@emotion/memoize" "^0.7.4"
- "@emotion/sheet" "^1.1.0"
- "@emotion/utils" "^1.0.0"
- "@emotion/weak-memoize" "^0.2.5"
- stylis "4.0.13"
-
-"@emotion/css@^11.1.3":
- version "11.9.0"
- resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.9.0.tgz#d5aeaca5ed19fc61cbdc9e032ad0b32fa6e366be"
- integrity sha512-S9UjCxSrxEHawOLnWw4upTwfYKb0gVQdatHejn3W9kPyXxmKv3HmjVfJ84kDLmdX8jR20OuDQwaJ4Um24qD9vA==
- dependencies:
- "@emotion/babel-plugin" "^11.7.1"
- "@emotion/cache" "^11.7.1"
- "@emotion/serialize" "^1.0.3"
- "@emotion/sheet" "^1.0.3"
- "@emotion/utils" "^1.0.0"
-
-"@emotion/hash@^0.8.0":
- version "0.8.0"
- resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
- integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
-
-"@emotion/memoize@^0.7.4", "@emotion/memoize@^0.7.5":
- version "0.7.5"
- resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50"
- integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
-
-"@emotion/serialize@^1.0.2", "@emotion/serialize@^1.0.3":
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-1.0.3.tgz#99e2060c26c6292469fb30db41f4690e1c8fea63"
- integrity sha512-2mSSvgLfyV3q+iVh3YWgNlUc2a9ZlDU7DjuP5MjK3AXRR0dYigCrP99aeFtaB2L/hjfEZdSThn5dsZ0ufqbvsA==
- dependencies:
- "@emotion/hash" "^0.8.0"
- "@emotion/memoize" "^0.7.4"
- "@emotion/unitless" "^0.7.5"
- "@emotion/utils" "^1.0.0"
- csstype "^3.0.2"
-
-"@emotion/sheet@^1.0.3", "@emotion/sheet@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.1.0.tgz#56d99c41f0a1cda2726a05aa6a20afd4c63e58d2"
- integrity sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==
-
-"@emotion/unitless@^0.7.5":
- version "0.7.5"
- resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed"
- integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==
-
-"@emotion/utils@^1.0.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.1.0.tgz#86b0b297f3f1a0f2bdb08eeac9a2f49afd40d0cf"
- integrity sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ==
-
-"@emotion/weak-memoize@^0.2.5":
- version "0.2.5"
- resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
- integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
-
-"@eslint/eslintrc@^1.3.0":
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
- integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==
- dependencies:
- ajv "^6.12.4"
- debug "^4.3.2"
- espree "^9.3.2"
- globals "^13.15.0"
- ignore "^5.2.0"
- import-fresh "^3.2.1"
- js-yaml "^4.1.0"
- minimatch "^3.1.2"
- strip-json-comments "^3.1.1"
-
-"@humanwhocodes/config-array@^0.9.2":
- version "0.9.5"
- resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7"
- integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==
- dependencies:
- "@humanwhocodes/object-schema" "^1.2.1"
- debug "^4.1.1"
- minimatch "^3.0.4"
-
-"@humanwhocodes/object-schema@^1.2.1":
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
- integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
-
-"@istanbuljs/load-nyc-config@^1.0.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
- integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==
- dependencies:
- camelcase "^5.3.1"
- find-up "^4.1.0"
- get-package-type "^0.1.0"
- js-yaml "^3.13.1"
- resolve-from "^5.0.0"
-
-"@istanbuljs/schema@^0.1.2":
- version "0.1.3"
- resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
- integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
-
-"@jest/console@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/console/-/console-27.5.1.tgz#260fe7239602fe5130a94f1aa386eff54b014bba"
- integrity sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==
- dependencies:
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- chalk "^4.0.0"
- jest-message-util "^27.5.1"
- jest-util "^27.5.1"
- slash "^3.0.0"
-
-"@jest/console@^28.1.0":
- version "28.1.0"
- resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.0.tgz#db78222c3d3b0c1db82f1b9de51094c2aaff2176"
- integrity sha512-tscn3dlJFGay47kb4qVruQg/XWlmvU0xp3EJOjzzY+sBaI+YgwKcvAmTcyYU7xEiLLIY5HCdWRooAL8dqkFlDA==
- dependencies:
- "@jest/types" "^28.1.0"
- "@types/node" "*"
- chalk "^4.0.0"
- jest-message-util "^28.1.0"
- jest-util "^28.1.0"
- slash "^3.0.0"
-
-"@jest/core@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.5.1.tgz#267ac5f704e09dc52de2922cbf3af9edcd64b626"
- integrity sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==
- dependencies:
- "@jest/console" "^27.5.1"
- "@jest/reporters" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- ansi-escapes "^4.2.1"
- chalk "^4.0.0"
- emittery "^0.8.1"
- exit "^0.1.2"
- graceful-fs "^4.2.9"
- jest-changed-files "^27.5.1"
- jest-config "^27.5.1"
- jest-haste-map "^27.5.1"
- jest-message-util "^27.5.1"
- jest-regex-util "^27.5.1"
- jest-resolve "^27.5.1"
- jest-resolve-dependencies "^27.5.1"
- jest-runner "^27.5.1"
- jest-runtime "^27.5.1"
- jest-snapshot "^27.5.1"
- jest-util "^27.5.1"
- jest-validate "^27.5.1"
- jest-watcher "^27.5.1"
- micromatch "^4.0.4"
- rimraf "^3.0.0"
- slash "^3.0.0"
- strip-ansi "^6.0.0"
-
-"@jest/environment@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-27.5.1.tgz#d7425820511fe7158abbecc010140c3fd3be9c74"
- integrity sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==
- dependencies:
- "@jest/fake-timers" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- jest-mock "^27.5.1"
-
-"@jest/fake-timers@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-27.5.1.tgz#76979745ce0579c8a94a4678af7a748eda8ada74"
- integrity sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==
- dependencies:
- "@jest/types" "^27.5.1"
- "@sinonjs/fake-timers" "^8.0.1"
- "@types/node" "*"
- jest-message-util "^27.5.1"
- jest-mock "^27.5.1"
- jest-util "^27.5.1"
-
-"@jest/globals@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-27.5.1.tgz#7ac06ce57ab966566c7963431cef458434601b2b"
- integrity sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==
- dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/types" "^27.5.1"
- expect "^27.5.1"
-
-"@jest/reporters@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-27.5.1.tgz#ceda7be96170b03c923c37987b64015812ffec04"
- integrity sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==
- dependencies:
- "@bcoe/v8-coverage" "^0.2.3"
- "@jest/console" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- chalk "^4.0.0"
- collect-v8-coverage "^1.0.0"
- exit "^0.1.2"
- glob "^7.1.2"
- graceful-fs "^4.2.9"
- istanbul-lib-coverage "^3.0.0"
- istanbul-lib-instrument "^5.1.0"
- istanbul-lib-report "^3.0.0"
- istanbul-lib-source-maps "^4.0.0"
- istanbul-reports "^3.1.3"
- jest-haste-map "^27.5.1"
- jest-resolve "^27.5.1"
- jest-util "^27.5.1"
- jest-worker "^27.5.1"
- slash "^3.0.0"
- source-map "^0.6.0"
- string-length "^4.0.1"
- terminal-link "^2.0.0"
- v8-to-istanbul "^8.1.0"
-
-"@jest/schemas@^28.0.2":
- version "28.0.2"
- resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.0.2.tgz#08c30df6a8d07eafea0aef9fb222c5e26d72e613"
- integrity sha512-YVDJZjd4izeTDkij00vHHAymNXQ6WWsdChFRK86qck6Jpr3DCL5W3Is3vslviRlP+bLuMYRLbdp98amMvqudhA==
- dependencies:
- "@sinclair/typebox" "^0.23.3"
-
-"@jest/source-map@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-27.5.1.tgz#6608391e465add4205eae073b55e7f279e04e8cf"
- integrity sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==
- dependencies:
- callsites "^3.0.0"
- graceful-fs "^4.2.9"
- source-map "^0.6.0"
-
-"@jest/test-result@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-27.5.1.tgz#56a6585fa80f7cdab72b8c5fc2e871d03832f5bb"
- integrity sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==
- dependencies:
- "@jest/console" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/istanbul-lib-coverage" "^2.0.0"
- collect-v8-coverage "^1.0.0"
-
-"@jest/test-result@^28.1.0":
- version "28.1.0"
- resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.0.tgz#fd149dee123510dd2fcadbbf5f0020f98ad7f12c"
- integrity sha512-sBBFIyoPzrZho3N+80P35A5oAkSKlGfsEFfXFWuPGBsW40UAjCkGakZhn4UQK4iQlW2vgCDMRDOob9FGKV8YoQ==
- dependencies:
- "@jest/console" "^28.1.0"
- "@jest/types" "^28.1.0"
- "@types/istanbul-lib-coverage" "^2.0.0"
- collect-v8-coverage "^1.0.0"
-
-"@jest/test-sequencer@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz#4057e0e9cea4439e544c6353c6affe58d095745b"
- integrity sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==
- dependencies:
- "@jest/test-result" "^27.5.1"
- graceful-fs "^4.2.9"
- jest-haste-map "^27.5.1"
- jest-runtime "^27.5.1"
-
-"@jest/transform@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-27.5.1.tgz#6c3501dcc00c4c08915f292a600ece5ecfe1f409"
- integrity sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==
- dependencies:
- "@babel/core" "^7.1.0"
- "@jest/types" "^27.5.1"
- babel-plugin-istanbul "^6.1.1"
- chalk "^4.0.0"
- convert-source-map "^1.4.0"
- fast-json-stable-stringify "^2.0.0"
- graceful-fs "^4.2.9"
- jest-haste-map "^27.5.1"
- jest-regex-util "^27.5.1"
- jest-util "^27.5.1"
- micromatch "^4.0.4"
- pirates "^4.0.4"
- slash "^3.0.0"
- source-map "^0.6.1"
- write-file-atomic "^3.0.0"
-
-"@jest/types@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80"
- integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==
- dependencies:
- "@types/istanbul-lib-coverage" "^2.0.0"
- "@types/istanbul-reports" "^3.0.0"
- "@types/node" "*"
- "@types/yargs" "^16.0.0"
- chalk "^4.0.0"
-
-"@jest/types@^28.1.0":
- version "28.1.0"
- resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.0.tgz#508327a89976cbf9bd3e1cc74641a29fd7dfd519"
- integrity sha512-xmEggMPr317MIOjjDoZ4ejCSr9Lpbt/u34+dvc99t7DS8YirW5rwZEhzKPC2BMUFkUhI48qs6qLUSGw5FuL0GA==
- dependencies:
- "@jest/schemas" "^28.0.2"
- "@types/istanbul-lib-coverage" "^2.0.0"
- "@types/istanbul-reports" "^3.0.0"
- "@types/node" "*"
- "@types/yargs" "^17.0.8"
- chalk "^4.0.0"
-
-"@jridgewell/gen-mapping@^0.1.0":
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
- integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==
- dependencies:
- "@jridgewell/set-array" "^1.0.0"
- "@jridgewell/sourcemap-codec" "^1.4.10"
-
-"@jridgewell/gen-mapping@^0.3.0":
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9"
- integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==
- dependencies:
- "@jridgewell/set-array" "^1.0.0"
- "@jridgewell/sourcemap-codec" "^1.4.10"
- "@jridgewell/trace-mapping" "^0.3.9"
-
-"@jridgewell/resolve-uri@^3.0.3":
- version "3.0.7"
- resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe"
- integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==
-
-"@jridgewell/set-array@^1.0.0":
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea"
- integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==
-
-"@jridgewell/source-map@^0.3.2":
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
- integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
- dependencies:
- "@jridgewell/gen-mapping" "^0.3.0"
- "@jridgewell/trace-mapping" "^0.3.9"
-
-"@jridgewell/sourcemap-codec@^1.4.10":
- version "1.4.13"
- resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c"
- integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==
-
-"@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9":
- version "0.3.13"
- resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea"
- integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==
- dependencies:
- "@jridgewell/resolve-uri" "^3.0.3"
- "@jridgewell/sourcemap-codec" "^1.4.10"
-
-"@leichtgewicht/ip-codec@^2.0.1":
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b"
- integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==
-
-"@lezer/common@^0.15.0", "@lezer/common@^0.15.5":
- version "0.15.12"
- resolved "https://registry.yarnpkg.com/@lezer/common/-/common-0.15.12.tgz#2f21aec551dd5fd7d24eb069f90f54d5bc6ee5e9"
- integrity sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==
-
-"@lezer/cpp@^0.15.0":
- version "0.15.3"
- resolved "https://registry.yarnpkg.com/@lezer/cpp/-/cpp-0.15.3.tgz#51499ec09da0eef9f6d7fa3f6497c57c46162c3e"
- integrity sha512-QE5YxhnoQ4eJH9G2h5r+m4Zq7d/0NmA0eAnZmiOVggI7a3jpODIXZeJbkUPf4U2yzNCSWAGpZVk8XxkA+cTZvA==
- dependencies:
- "@lezer/lr" "^0.15.0"
-
-"@lezer/css@^0.15.2":
- version "0.15.2"
- resolved "https://registry.yarnpkg.com/@lezer/css/-/css-0.15.2.tgz#e96995da67df90bb4b191aaa8a486349cca5d8e7"
- integrity sha512-tnMOMZY0Zs6JQeVjqfmREYMV0GnmZR1NitndLWioZMD6mA7VQF/PPKPmJX1f+ZgVZQc5Am0df9mX3aiJnNJlKQ==
- dependencies:
- "@lezer/lr" "^0.15.0"
-
-"@lezer/html@^0.15.0":
- version "0.15.1"
- resolved "https://registry.yarnpkg.com/@lezer/html/-/html-0.15.1.tgz#973a5a179560d0789bf8737c06e6d143cc211406"
- integrity sha512-0ZYVhu+RwN6ZMM0gNnTxenRAdoycKc2wvpLfMjP0JkKR0vMxhtuLaIpsq9KW2Mv6l7ux5vdjq8CQ7fKDvia8KA==
- dependencies:
- "@lezer/lr" "^0.15.0"
-
-"@lezer/java@^0.15.0":
- version "0.15.0"
- resolved "https://registry.yarnpkg.com/@lezer/java/-/java-0.15.0.tgz#44da269cca36a9af1ad5c862552b2f2bf5847589"
- integrity sha512-Od2Ugo93XjLxCIEKlrwJfacmSMd7lEnkVQgBjMsZofjwEKZ2Y2ue6URntMFFiftTlNXbE29vYbweWYluEq+Cdw==
- dependencies:
- "@lezer/lr" "^0.15.0"
-
-"@lezer/javascript@^0.15.1":
- version "0.15.3"
- resolved "https://registry.yarnpkg.com/@lezer/javascript/-/javascript-0.15.3.tgz#833a4c5650bae07805b9af88de6706368844dc55"
- integrity sha512-8jA2NpOfpWwSPZxRhd9BxK2ZPvGd7nLE3LFTJ5AbMhXAzMHeMjneV6GEVd7dAIee85dtap0jdb6bgOSO0+lfwA==
- dependencies:
- "@lezer/lr" "^0.15.0"
-
-"@lezer/json@^0.15.0":
- version "0.15.0"
- resolved "https://registry.yarnpkg.com/@lezer/json/-/json-0.15.0.tgz#b96c1161eb8514e05f4eaaec95c68376e76e539f"
- integrity sha512-OsMjjBkTkeQ15iMCu5U1OiBubRC4V9Wm03zdIlUgNZ20aUPx5DWDRqUc5wG41JXVSj7Lxmo+idlFCfBBdxB8sw==
- dependencies:
- "@lezer/lr" "^0.15.0"
-
-"@lezer/lr@^0.15.0":
- version "0.15.8"
- resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-0.15.8.tgz#1564a911e62b0a0f75ca63794a6aa8c5dc63db21"
- integrity sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==
- dependencies:
- "@lezer/common" "^0.15.0"
-
-"@lezer/markdown@^0.15.0":
- version "0.15.6"
- resolved "https://registry.yarnpkg.com/@lezer/markdown/-/markdown-0.15.6.tgz#2a826a507399b32176efdc35554397f05227d2aa"
- integrity sha512-1XXLa4q0ZthryUEfO47ipvZHxNb+sCKoQIMM9dKs5vXZOBbgF2Vah/GL3g26BFIAEc2uCv4VQnI+lSrv58BT3g==
- dependencies:
- "@lezer/common" "^0.15.0"
-
-"@lezer/php@^0.15.0":
- version "0.15.0"
- resolved "https://registry.yarnpkg.com/@lezer/php/-/php-0.15.0.tgz#d09abd0ffaf256dcfac9b78cf4e6f2ee930b9efa"
- integrity sha512-kU3QSOko0jsv3RLhABPrRD4wEhaWYh2Uh0lTj9Q9BOsBJ5SoADfifO4gHkEDav7AgL/j+ulkKiHiilciTa/RaQ==
- dependencies:
- "@lezer/lr" "^0.15.0"
-
-"@lezer/python@^0.15.0":
- version "0.15.1"
- resolved "https://registry.yarnpkg.com/@lezer/python/-/python-0.15.1.tgz#dce18dade29fd93b01c1f7fc5dee4135e947f07b"
- integrity sha512-Xdb2nh+FoxR8ssEADGsroDtsnP+EDhiPpW9zhER3h+6cpGtZ2e9Oq/Rwn9nFQRiKCfMT+AQaqC3ZgAbhbnumyQ==
- dependencies:
- "@lezer/lr" "^0.15.0"
-
-"@lezer/rust@^0.15.0":
- version "0.15.1"
- resolved "https://registry.yarnpkg.com/@lezer/rust/-/rust-0.15.1.tgz#119965e4fb4743e4eb153aae4e95fb58e9853197"
- integrity sha512-9R7Mcfe/XWodpT7bYNKoOmEAN+AOHHfma9QUTdEhqduzd1G4qsdQkGSMPfsqt24sZCkQ1EREbE/lmEp4YxTlcA==
- dependencies:
- "@lezer/lr" "^0.15.0"
-
-"@lezer/xml@^0.15.0":
- version "0.15.1"
- resolved "https://registry.yarnpkg.com/@lezer/xml/-/xml-0.15.1.tgz#ad4bb442b18bf267fd370350543239f71d2075c2"
- integrity sha512-vVh01enxM9hSGOcFtztmX+Pa460HDq5jIeft9bDCe17PUOU0nAbfo883I3cW9lUOcmWNQ3btbkmXMGjRszJE6g==
- dependencies:
- "@lezer/lr" "^0.15.0"
-
-"@milkdown/core@6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/core/-/core-6.1.3.tgz#a90c2fe70fa1651a58d54736740fd857a8d7d1f1"
- integrity sha512-HYfDP0bjrFqcgvbevApB4zLmF/MJWBWK27g8duh9rTLKcCPjQcCfroBlTEzEhEtKRkSkSA69V28RgEM0TjmuZA==
- dependencies:
- "@milkdown/ctx" "6.1.3"
- "@milkdown/design-system" "6.1.3"
- "@milkdown/exception" "6.1.3"
- "@milkdown/transformer" "6.1.3"
- tslib "^2.3.1"
-
-"@milkdown/ctx@6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/ctx/-/ctx-6.1.3.tgz#21696d418d7e0f6980c567c43ceb511a4d7f33c4"
- integrity sha512-8DQHfGTgptZDpz61SeZ56/tyEQ/KwFGdHMxhMTowqk14qotYsGOnMmzD5/JfUZmWqb4oozAHFmK5wP1w/12j8Q==
- dependencies:
- "@milkdown/exception" "6.1.3"
- tslib "^2.3.1"
-
-"@milkdown/design-system@6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/design-system/-/design-system-6.1.3.tgz#ff9b10fc3998c4f5cdb5e0d6d648fe719057a162"
- integrity sha512-3B8XRaBa9PG+UGH3INc6wXXL9UW4GbOPjkSdz+SI2AH7OcjQi/TgwU6xhdv1XuFE1qGxRvsR7e0SwvwbAFmogA==
- dependencies:
- "@emotion/cache" "^11.7.1"
- "@emotion/css" "^11.1.3"
- "@milkdown/ctx" "6.1.3"
- tslib "^2.3.1"
-
-"@milkdown/exception@6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/exception/-/exception-6.1.3.tgz#5cc48db6cd412171a3e165ab36ffa0520a706172"
- integrity sha512-eYLsqQMkl6Y0JpjQZ0frkSXb+mtRFLAVmnUNFc9zoXMfEGgAwbfaYIHG12Um2tmHvDC39Tq6r38A0BzBge6Stg==
- dependencies:
- tslib "^2.3.1"
-
-"@milkdown/plugin-diagram@6.1.3":
- version "6.1.3"
- resolved "https://npm.shopee.io/@milkdown%2fplugin-diagram/-/plugin-diagram-6.1.3.tgz#36d32deee72dfe1fcbb8da98373cd263f91c1b8e"
- integrity sha512-go46hH5c7NJKekyXnMq6MU1EPYzPzE3EUi1JeL08FJSqIxEBYHDDHq87IpR+zSurLrB3PKBRwIbCPRwPL/6WGw==
- dependencies:
- "@milkdown/utils" "6.1.3"
- "@types/mermaid" "^8.2.8"
- mermaid "^9.0.0"
- nanoid "^3.1.25"
- tslib "^2.3.1"
- unist-util-visit "^4.0.0"
-
-"@milkdown/plugin-emoji@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/plugin-emoji/-/plugin-emoji-6.1.3.tgz#5d6450061d7a8224439cda98bc38cc9de254e20d"
- integrity sha512-JMO3P4WP+uXKRi1otNGhNaTlp9cSMMWP6tzW90c6CUCBhOwaVLHRI5tNsS5bzdzncPki0/R6VmRY5l+1M47nMQ==
- dependencies:
- "@milkdown/utils" "6.1.3"
- "@types/node-emoji" "^1.8.1"
- emoji-regex "^10.0.0"
- node-emoji "^1.10.0"
- remark-emoji "^3.0.1"
- tslib "^2.3.1"
- twemoji "^14.0.1"
- unist-util-visit "^4.0.0"
-
-"@milkdown/plugin-history@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/plugin-history/-/plugin-history-6.1.3.tgz#974ffba85e2a7a1205ff781252043f3bc95ced4a"
- integrity sha512-omImhhh4EaR4RMJSd5esFTUapJGwKMbhsDi1ADarZ8Vkyn2CVYG28XAw+tQ1Jpwv9ofs5V8spw6p0OEv+LY+xw==
- dependencies:
- "@milkdown/utils" "6.1.3"
- tslib "^2.3.1"
-
-"@milkdown/plugin-indent@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/plugin-indent/-/plugin-indent-6.1.3.tgz#a500273e021873fd2517fd7ff3f075a81d37bd5c"
- integrity sha512-hrPgbYtjqp4bgV9u0fSRHphMV/CNag5g6JlV2CeN2oD0CkPswNCCedOUNR3UjSlvyzxjUkVlFuxUWFWehuxejw==
- dependencies:
- "@milkdown/utils" "6.1.3"
- tslib "^2.3.1"
-
-"@milkdown/plugin-listener@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/plugin-listener/-/plugin-listener-6.1.3.tgz#30776b92792f919265ed42a415b18377f05ec5fb"
- integrity sha512-825KrjFk8uEU9rPx4zswdDkDlHj1pb9+fJSfo7JsyHj4FLmiuxFJIO0XSCZ8jnvZ7lRByChOpqPZ0Wo0gKkPbA==
- dependencies:
- "@milkdown/utils" "6.1.3"
- tslib "^2.3.1"
-
-"@milkdown/plugin-menu@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/plugin-menu/-/plugin-menu-6.1.3.tgz#20b16a9041d97ff6cc15ffbabc97981e8176e59e"
- integrity sha512-BC6FmbqCtuJeuwEnba8jh6BaVcbx6RPdgTl4uQIv1wiK+Zu5NLW9bZSUpl9txCqHiuS9pTmbLrhTpwL51BuXlQ==
- dependencies:
- "@milkdown/utils" "6.1.3"
- tslib "^2.3.1"
-
-"@milkdown/plugin-prism@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/plugin-prism/-/plugin-prism-6.1.3.tgz#7dd73176ce3d143bcc899dc2ef4335f24df0cd84"
- integrity sha512-J2EDOK0vAuLhL/w75uUqYlQuFFCpRK6E5WsjMbL96j98iN4B8qX+V3wWC+87K6Oi6dQWkmUXjQ37NLdY+Bd6Dg==
- dependencies:
- "@milkdown/utils" "6.1.3"
- "@types/refractor" "^3.0.0"
- refractor "^4.0.0"
- tslib "^2.3.1"
-
-"@milkdown/plugin-slash@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/plugin-slash/-/plugin-slash-6.1.3.tgz#d008d14e68fb047a76e18b876d9bfd44121935b6"
- integrity sha512-DTIDutqcSwPJhD6YLUECWhq7hVDMCkej/ZF3gttElD7FC+v+wNOzuRvpCAPrpgnR4O5xRVYC/qJU8Ez7KZuNFw==
- dependencies:
- "@milkdown/utils" "6.1.3"
- smooth-scroll-into-view-if-needed "^1.1.32"
- tslib "^2.3.1"
-
-"@milkdown/plugin-tooltip@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/plugin-tooltip/-/plugin-tooltip-6.1.3.tgz#6b1730c928fc4fbe2656bf0066675905f99494ad"
- integrity sha512-PBAIRqUrEPGjwZaTAXr12qKxO6EsvV+IZ0qqdZiTb9XLaFdx+wDMoh6LvUF5aJ5KV01xG3Jd0t55JgBhmQ7SUQ==
- dependencies:
- "@milkdown/utils" "6.1.3"
- tslib "^2.3.1"
-
-"@milkdown/plugin-upload@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/plugin-upload/-/plugin-upload-6.1.3.tgz#0f89e9cf6c1fc9b6aca00123a9784c5abb12d685"
- integrity sha512-dJn72bHEa60UoRunEEM/snLaurdE6RAa/0AzaCmhldBwZd8fQqYV4i+J/Gq6xDNZypGgCRfHwzXzSDz174RDdw==
- dependencies:
- "@milkdown/utils" "6.1.3"
- tslib "^2.3.1"
-
-"@milkdown/preset-commonmark@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/preset-commonmark/-/preset-commonmark-6.1.3.tgz#2f89a0ee5666e3035474a279f51beef3aef93375"
- integrity sha512-IQmBORFpTEN/3K0kSacWcRAF1E/ApHG5qA6KB4W6DmEVyGP6yfMoB22E5BKjiaWT9bpiEm6ReOZX8/AZ3Mmkmg==
- dependencies:
- "@milkdown/utils" "6.1.3"
- remark-inline-links "^6.0.0"
- tslib "^2.3.1"
-
-"@milkdown/preset-gfm@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/preset-gfm/-/preset-gfm-6.1.3.tgz#ee27687914307295681f112a527eb3b1fbd6bd92"
- integrity sha512-B0c5IZhjx50ZTFXuK16Qz7x7NHWOTgyJPjjSWJkpCOGWCdXbyhCfhRay+VxPRirgi7+xGyxtS3D2+mdkE6nsrA==
- dependencies:
- "@milkdown/utils" "6.1.3"
- remark-gfm "^3.0.0"
- tslib "^2.3.1"
-
-"@milkdown/prose@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/prose/-/prose-6.1.3.tgz#f23d6154e382de6ea37c032dc3ba29889e096202"
- integrity sha512-TNbBsoDbOMgRWfE8+PYil3TG0yvRx7NOHyZ6scIsaXULK+30YT/3E9FYO197hIz5G9MUzNYzRk7btdOLK1egGw==
- dependencies:
- "@milkdown/exception" "6.1.3"
- "@types/prosemirror-commands" "^1.0.4"
- "@types/prosemirror-dropcursor" "^1.0.2"
- "@types/prosemirror-gapcursor" "^1.0.4"
- "@types/prosemirror-history" "^1.0.2"
- "@types/prosemirror-inputrules" "^1.0.4"
- "@types/prosemirror-keymap" "^1.0.4"
- "@types/prosemirror-model" "^1.16.1"
- "@types/prosemirror-schema-list" "^1.0.3"
- "@types/prosemirror-state" "^1.2.6"
- "@types/prosemirror-transform" "^1.1.6"
- "@types/prosemirror-view" "^1.23.1"
- prosemirror-commands "^1.2.2"
- prosemirror-dropcursor "^1.3.5"
- prosemirror-gapcursor "^1.1.5"
- prosemirror-history "^1.1.3"
- prosemirror-inputrules "^1.1.3"
- prosemirror-keymap "^1.1.5"
- prosemirror-model "^1.16.1"
- prosemirror-schema-list "^1.1.6"
- prosemirror-state "^1.3.4"
- prosemirror-tables "^1.1.1"
- prosemirror-transform "^1.4.2"
- prosemirror-view "^1.23.12"
- tslib "^2.3.1"
-
-"@milkdown/react@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/react/-/react-6.1.3.tgz#0c73eeb433c95244a53d0b0171ccd2dae4208f67"
- integrity sha512-kyjHbZFnP1yl2TEgxLHjMTvZKWaUkE0N+YM86CrgRnmcei5c9VcuebC3/z6p4qNum0mmVB5m15rL3GfBUUAtVQ==
- dependencies:
- "@milkdown/utils" "6.1.3"
- nanoid "^3.1.25"
- tslib "^2.3.1"
-
-"@milkdown/theme-nord@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/theme-nord/-/theme-nord-6.1.3.tgz#23646dd99f9554f7b6f70ff36ab968c7d8ee4f50"
- integrity sha512-q6ySShN75+uZT+9t2cK7A/+wBp2pV9FIIJ5+50j0WPquR9ui++GRLDqTFvRh3nGCOQaXK+GHGNLP0ffH+O8SjQ==
- dependencies:
- "@milkdown/theme-pack-helper" "6.1.3"
-
-"@milkdown/theme-pack-helper@6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/theme-pack-helper/-/theme-pack-helper-6.1.3.tgz#1a6bcc33bec9f32a90d9e5dff3182223131fa915"
- integrity sha512-BKrpw1g45tLCe6MYLAZqq0l9XpjzJGYN3XaNPxDLXCnLV5h+4uKgANL75bHb/EXqV0N6mR/YKb1oX4u0l9JCnQ==
- dependencies:
- tslib "^2.3.1"
-
-"@milkdown/theme-tokyo@^6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/theme-tokyo/-/theme-tokyo-6.1.3.tgz#83aa49edc502622b427fe03d4a3b6dd9031e6095"
- integrity sha512-7L50ZDiCqvnW9GRGXiWDj+2xqk7s6yFVxhThr7XGbHWbC6Rt1lAQK+KhmczSxd9fszxKuO0SQ5Zx/9clDTDmdA==
- dependencies:
- "@milkdown/theme-pack-helper" "6.1.3"
-
-"@milkdown/transformer@6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/transformer/-/transformer-6.1.3.tgz#bc0018b2467b78aeb7d2b246a8bc1d1c0255d986"
- integrity sha512-52PFjksNcmPhxhl7oLdObOCqjPSUuw6d6++6Tg7wzF4S0yIvX71bR8hb7KmlCQWL87hyfISKb5n3gEXky4oEbQ==
- dependencies:
- "@milkdown/exception" "6.1.3"
- "@types/mdast" "^3.0.10"
- "@types/unist" "^2.0.6"
- remark "^14.0.1"
- tslib "^2.3.1"
- unified "^10.1.0"
-
-"@milkdown/utils@6.1.3":
- version "6.1.3"
- resolved "https://registry.yarnpkg.com/@milkdown/utils/-/utils-6.1.3.tgz#9dc644612322bcb9dfe2298ff6f2825dead0242e"
- integrity sha512-UEuYUz6aWijtC9I3KBYoe1vZ9t0PlNS+FbAvCJ6B3VU53gKsRIENNspgUn3BRosFkt0te+36OLQkikSQpQSmxw==
- dependencies:
- "@milkdown/exception" "6.1.3"
- tslib "^2.3.1"
-
-"@nodelib/fs.scandir@2.1.5":
- version "2.1.5"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
- integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
- dependencies:
- "@nodelib/fs.stat" "2.0.5"
- run-parallel "^1.1.9"
-
-"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
- integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
-
-"@nodelib/fs.walk@^1.2.3":
- version "1.2.8"
- resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
- integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
- dependencies:
- "@nodelib/fs.scandir" "2.1.5"
- fastq "^1.6.0"
-
-"@pmmmwh/react-refresh-webpack-plugin@^0.5.3":
- version "0.5.7"
- resolved "https://registry.yarnpkg.com/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.7.tgz#58f8217ba70069cc6a73f5d7e05e85b458c150e2"
- integrity sha512-bcKCAzF0DV2IIROp9ZHkRJa6O4jy7NlnHdWL3GmcUxYWNjLXkK5kfELELwEfSP5hXPfVL/qOGMAROuMQb9GG8Q==
- dependencies:
- ansi-html-community "^0.0.8"
- common-path-prefix "^3.0.0"
- core-js-pure "^3.8.1"
- error-stack-parser "^2.0.6"
- find-up "^5.0.0"
- html-entities "^2.1.0"
- loader-utils "^2.0.0"
- schema-utils "^3.0.0"
- source-map "^0.7.3"
-
-"@reduxjs/toolkit@^1.7.1":
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.8.2.tgz#352fd17bc858af51d21ce8d28183a930cab9e638"
- integrity sha512-CtPw5TkN1pHRigMFCOS/0qg3b/yfPV5qGCsltVnIz7bx4PKTJlGHYfIxm97qskLknMzuGfjExaYdXJ77QTL0vg==
- dependencies:
- immer "^9.0.7"
- redux "^4.1.2"
- redux-thunk "^2.4.1"
- reselect "^4.1.5"
-
-"@rollup/plugin-babel@^5.2.0":
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz#04bc0608f4aa4b2e4b1aebf284344d0f68fda283"
- integrity sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==
- dependencies:
- "@babel/helper-module-imports" "^7.10.4"
- "@rollup/pluginutils" "^3.1.0"
-
-"@rollup/plugin-node-resolve@^11.2.1":
- version "11.2.1"
- resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz#82aa59397a29cd4e13248b106e6a4a1880362a60"
- integrity sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==
- dependencies:
- "@rollup/pluginutils" "^3.1.0"
- "@types/resolve" "1.17.1"
- builtin-modules "^3.1.0"
- deepmerge "^4.2.2"
- is-module "^1.0.0"
- resolve "^1.19.0"
-
-"@rollup/plugin-replace@^2.4.1":
- version "2.4.2"
- resolved "https://registry.yarnpkg.com/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz#a2d539314fbc77c244858faa523012825068510a"
- integrity sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==
- dependencies:
- "@rollup/pluginutils" "^3.1.0"
- magic-string "^0.25.7"
-
-"@rollup/pluginutils@^3.1.0":
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b"
- integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==
- dependencies:
- "@types/estree" "0.0.39"
- estree-walker "^1.0.1"
- picomatch "^2.2.2"
-
-"@rushstack/eslint-patch@^1.1.0":
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.3.tgz#6801033be7ff87a6b7cadaf5b337c9f366a3c4b0"
- integrity sha512-WiBSI6JBIhC6LRIsB2Kwh8DsGTlbBU+mLRxJmAe3LjHTdkDpwIbEOZgoXBbZilk/vlfjK8i6nKRAvIRn1XaIMw==
-
-"@sinclair/typebox@^0.23.3":
- version "0.23.5"
- resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.5.tgz#93f7b9f4e3285a7a9ade7557d9a8d36809cbc47d"
- integrity sha512-AFBVi/iT4g20DHoujvMH1aEDn8fGJh4xsRGCP6d8RpLPMqsNPvW01Jcn0QysXTsg++/xj25NmJsGyH9xug/wKg==
-
-"@sinonjs/commons@^1.7.0":
- version "1.8.3"
- resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
- integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==
- dependencies:
- type-detect "4.0.8"
-
-"@sinonjs/fake-timers@^8.0.1":
- version "8.1.0"
- resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz#3fdc2b6cb58935b21bfb8d1625eb1300484316e7"
- integrity sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==
- dependencies:
- "@sinonjs/commons" "^1.7.0"
-
-"@surma/rollup-plugin-off-main-thread@^2.2.3":
- version "2.2.3"
- resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053"
- integrity sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==
- dependencies:
- ejs "^3.1.6"
- json5 "^2.2.0"
- magic-string "^0.25.0"
- string.prototype.matchall "^4.0.6"
-
-"@svgr/babel-plugin-add-jsx-attribute@^5.4.0":
- version "5.4.0"
- resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906"
- integrity sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg==
-
-"@svgr/babel-plugin-remove-jsx-attribute@^5.4.0":
- version "5.4.0"
- resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz#6b2c770c95c874654fd5e1d5ef475b78a0a962ef"
- integrity sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg==
-
-"@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1":
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz#25621a8915ed7ad70da6cea3d0a6dbc2ea933efd"
- integrity sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA==
-
-"@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1":
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz#0b221fc57f9fcd10e91fe219e2cd0dd03145a897"
- integrity sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ==
-
-"@svgr/babel-plugin-svg-dynamic-title@^5.4.0":
- version "5.4.0"
- resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz#139b546dd0c3186b6e5db4fefc26cb0baea729d7"
- integrity sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg==
-
-"@svgr/babel-plugin-svg-em-dimensions@^5.4.0":
- version "5.4.0"
- resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz#6543f69526632a133ce5cabab965deeaea2234a0"
- integrity sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw==
-
-"@svgr/babel-plugin-transform-react-native-svg@^5.4.0":
- version "5.4.0"
- resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz#00bf9a7a73f1cad3948cdab1f8dfb774750f8c80"
- integrity sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q==
-
-"@svgr/babel-plugin-transform-svg-component@^5.5.0":
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz#583a5e2a193e214da2f3afeb0b9e8d3250126b4a"
- integrity sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ==
-
-"@svgr/babel-preset@^5.5.0":
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/@svgr/babel-preset/-/babel-preset-5.5.0.tgz#8af54f3e0a8add7b1e2b0fcd5a882c55393df327"
- integrity sha512-4FiXBjvQ+z2j7yASeGPEi8VD/5rrGQk4Xrq3EdJmoZgz/tpqChpo5hgXDvmEauwtvOc52q8ghhZK4Oy7qph4ig==
- dependencies:
- "@svgr/babel-plugin-add-jsx-attribute" "^5.4.0"
- "@svgr/babel-plugin-remove-jsx-attribute" "^5.4.0"
- "@svgr/babel-plugin-remove-jsx-empty-expression" "^5.0.1"
- "@svgr/babel-plugin-replace-jsx-attribute-value" "^5.0.1"
- "@svgr/babel-plugin-svg-dynamic-title" "^5.4.0"
- "@svgr/babel-plugin-svg-em-dimensions" "^5.4.0"
- "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0"
- "@svgr/babel-plugin-transform-svg-component" "^5.5.0"
-
-"@svgr/core@^5.5.0":
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/@svgr/core/-/core-5.5.0.tgz#82e826b8715d71083120fe8f2492ec7d7874a579"
- integrity sha512-q52VOcsJPvV3jO1wkPtzTuKlvX7Y3xIcWRpCMtBF3MrteZJtBfQw/+u0B1BHy5ColpQc1/YVTrPEtSYIMNZlrQ==
- dependencies:
- "@svgr/plugin-jsx" "^5.5.0"
- camelcase "^6.2.0"
- cosmiconfig "^7.0.0"
-
-"@svgr/hast-util-to-babel-ast@^5.5.0":
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz#5ee52a9c2533f73e63f8f22b779f93cd432a5461"
- integrity sha512-cAaR/CAiZRB8GP32N+1jocovUtvlj0+e65TB50/6Lcime+EA49m/8l+P2ko+XPJ4dw3xaPS3jOL4F2X4KWxoeQ==
- dependencies:
- "@babel/types" "^7.12.6"
-
-"@svgr/plugin-jsx@^5.5.0":
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz#1aa8cd798a1db7173ac043466d7b52236b369000"
- integrity sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA==
- dependencies:
- "@babel/core" "^7.12.3"
- "@svgr/babel-preset" "^5.5.0"
- "@svgr/hast-util-to-babel-ast" "^5.5.0"
- svg-parser "^2.0.2"
-
-"@svgr/plugin-svgo@^5.5.0":
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz#02da55d85320549324e201c7b2e53bf431fcc246"
- integrity sha512-r5swKk46GuQl4RrVejVwpeeJaydoxkdwkM1mBKOgJLBUJPGaLci6ylg/IjhrRsREKDkr4kbMWdgOtbXEh0fyLQ==
- dependencies:
- cosmiconfig "^7.0.0"
- deepmerge "^4.2.2"
- svgo "^1.2.2"
-
-"@svgr/webpack@^5.5.0":
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-5.5.0.tgz#aae858ee579f5fa8ce6c3166ef56c6a1b381b640"
- integrity sha512-DOBOK255wfQxguUta2INKkzPj6AIS6iafZYiYmHn6W3pHlycSRRlvWKCfLDG10fXfLWqE3DJHgRUOyJYmARa7g==
- dependencies:
- "@babel/core" "^7.12.3"
- "@babel/plugin-transform-react-constant-elements" "^7.12.1"
- "@babel/preset-env" "^7.12.1"
- "@babel/preset-react" "^7.12.5"
- "@svgr/core" "^5.5.0"
- "@svgr/plugin-jsx" "^5.5.0"
- "@svgr/plugin-svgo" "^5.5.0"
- loader-utils "^2.0.0"
-
-"@tootallnate/once@1":
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
- integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
-
-"@trysound/sax@0.2.0":
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
- integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
-
-"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14":
- version "7.1.19"
- resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460"
- integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==
- dependencies:
- "@babel/parser" "^7.1.0"
- "@babel/types" "^7.0.0"
- "@types/babel__generator" "*"
- "@types/babel__template" "*"
- "@types/babel__traverse" "*"
-
-"@types/babel__generator@*":
- version "7.6.4"
- resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7"
- integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==
- dependencies:
- "@babel/types" "^7.0.0"
-
-"@types/babel__template@*":
- version "7.4.1"
- resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969"
- integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==
- dependencies:
- "@babel/parser" "^7.1.0"
- "@babel/types" "^7.0.0"
-
-"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6":
- version "7.17.1"
- resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.17.1.tgz#1a0e73e8c28c7e832656db372b779bfd2ef37314"
- integrity sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==
- dependencies:
- "@babel/types" "^7.3.0"
-
-"@types/body-parser@*":
- version "1.19.2"
- resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
- integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==
- dependencies:
- "@types/connect" "*"
- "@types/node" "*"
-
-"@types/bonjour@^3.5.9":
- version "3.5.10"
- resolved "https://registry.yarnpkg.com/@types/bonjour/-/bonjour-3.5.10.tgz#0f6aadfe00ea414edc86f5d106357cda9701e275"
- integrity sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==
- dependencies:
- "@types/node" "*"
-
-"@types/connect-history-api-fallback@^1.3.5":
- version "1.3.5"
- resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae"
- integrity sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==
- dependencies:
- "@types/express-serve-static-core" "*"
- "@types/node" "*"
-
-"@types/connect@*":
- version "3.4.35"
- resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
- integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
- dependencies:
- "@types/node" "*"
-
-"@types/debug@^4.0.0":
- version "4.1.7"
- resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82"
- integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==
- dependencies:
- "@types/ms" "*"
-
-"@types/eslint-scope@^3.7.3":
- version "3.7.3"
- resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224"
- integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==
- dependencies:
- "@types/eslint" "*"
- "@types/estree" "*"
-
-"@types/eslint@*":
- version "8.4.2"
- resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.2.tgz#48f2ac58ab9c631cb68845c3d956b28f79fad575"
- integrity sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==
- dependencies:
- "@types/estree" "*"
- "@types/json-schema" "*"
-
-"@types/eslint@^7.28.2":
- version "7.29.0"
- resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.29.0.tgz#e56ddc8e542815272720bb0b4ccc2aff9c3e1c78"
- integrity sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==
- dependencies:
- "@types/estree" "*"
- "@types/json-schema" "*"
-
-"@types/estree@*", "@types/estree@^0.0.51":
- version "0.0.51"
- resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
- integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
-
-"@types/estree@0.0.39":
- version "0.0.39"
- resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
- integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
-
-"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18":
- version "4.17.28"
- resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8"
- integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==
- dependencies:
- "@types/node" "*"
- "@types/qs" "*"
- "@types/range-parser" "*"
-
-"@types/express@*", "@types/express@^4.17.13":
- version "4.17.13"
- resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
- integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
- dependencies:
- "@types/body-parser" "*"
- "@types/express-serve-static-core" "^4.17.18"
- "@types/qs" "*"
- "@types/serve-static" "*"
-
-"@types/graceful-fs@^4.1.2":
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15"
- integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==
- dependencies:
- "@types/node" "*"
-
-"@types/hast@^2.0.0":
- version "2.3.4"
- resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc"
- integrity sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g==
- dependencies:
- "@types/unist" "*"
-
-"@types/history@^4.7.11":
- version "4.7.11"
- resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.11.tgz#56588b17ae8f50c53983a524fc3cc47437969d64"
- integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
-
-"@types/hoist-non-react-statics@^3.3.0":
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
- integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
- dependencies:
- "@types/react" "*"
- hoist-non-react-statics "^3.3.0"
-
-"@types/html-minifier-terser@^6.0.0":
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
- integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==
-
-"@types/http-proxy@^1.17.8":
- version "1.17.9"
- resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.9.tgz#7f0e7931343761efde1e2bf48c40f02f3f75705a"
- integrity sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==
- dependencies:
- "@types/node" "*"
-
-"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44"
- integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==
-
-"@types/istanbul-lib-report@*":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686"
- integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==
- dependencies:
- "@types/istanbul-lib-coverage" "*"
-
-"@types/istanbul-reports@^3.0.0":
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff"
- integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==
- dependencies:
- "@types/istanbul-lib-report" "*"
-
-"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
- version "7.0.11"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
- integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
-
-"@types/json5@^0.0.29":
- version "0.0.29"
- resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
- integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
-
-"@types/mdast@^3.0.0", "@types/mdast@^3.0.10":
- version "3.0.10"
- resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af"
- integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==
- dependencies:
- "@types/unist" "*"
-
-"@types/mermaid@^8.2.8":
- version "8.2.9"
- resolved "https://npm.shopee.io/@types%2fmermaid/-/mermaid-8.2.9.tgz#1844505dcffcd47703e94628a6200583d35c2c76"
- integrity sha512-f1i8fNoVFVJXedk+R7GcEk4KoOWzWAU3CzFqlVw1qWKktfsataBERezCz1pOdKy8Ec02ZdPQXGM7NU2lPHABYQ==
-
-"@types/mime@^1":
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
- integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
-
-"@types/ms@*":
- version "0.7.31"
- resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197"
- integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
-
-"@types/node-emoji@^1.8.1":
- version "1.8.1"
- resolved "https://registry.yarnpkg.com/@types/node-emoji/-/node-emoji-1.8.1.tgz#689cb74fdf6e84309bcafce93a135dfecd01de3f"
- integrity sha512-0fRfA90FWm6KJfw6P9QGyo0HDTCmthZ7cWaBQndITlaWLTZ6njRyKwrwpzpg+n6kBXBIGKeUHEQuBx7bphGJkA==
-
-"@types/node@*":
- version "17.0.38"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.38.tgz#f8bb07c371ccb1903f3752872c89f44006132947"
- integrity sha512-5jY9RhV7c0Z4Jy09G+NIDTsCZ5G0L5n+Z+p+Y7t5VJHM30bgwzSjVtlcBxqAj+6L/swIlvtOSzr8rBk/aNyV2g==
-
-"@types/orderedmap@*":
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/@types/orderedmap/-/orderedmap-1.0.0.tgz#807455a192bba52cbbb4517044bc82bdbfa8c596"
- integrity sha512-dxKo80TqYx3YtBipHwA/SdFmMMyLCnP+5mkEqN0eMjcTBzHkiiX0ES118DsjDBjvD+zeSsSU9jULTZ+frog+Gw==
-
-"@types/parse-json@^4.0.0":
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
- integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
-
-"@types/prettier@^2.1.5":
- version "2.6.3"
- resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a"
- integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg==
-
-"@types/prismjs@*", "@types/prismjs@^1.0.0":
- version "1.26.0"
- resolved "https://registry.yarnpkg.com/@types/prismjs/-/prismjs-1.26.0.tgz#a1c3809b0ad61c62cac6d4e0c56d610c910b7654"
- integrity sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==
-
-"@types/prop-types@*":
- version "15.7.5"
- resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
- integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
-
-"@types/prosemirror-commands@*", "@types/prosemirror-commands@^1.0.4":
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-commands/-/prosemirror-commands-1.0.4.tgz#d08551415127d93ae62e7239d30db0b5e7208e22"
- integrity sha512-utDNYB3EXLjAfYIcRWJe6pn3kcQ5kG4RijbT/0Y/TFOm6yhvYS/D9eJVnijdg9LDjykapcezchxGRqFD5LcyaQ==
- dependencies:
- "@types/prosemirror-model" "*"
- "@types/prosemirror-state" "*"
- "@types/prosemirror-view" "*"
-
-"@types/prosemirror-dropcursor@^1.0.2":
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-dropcursor/-/prosemirror-dropcursor-1.0.3.tgz#49250849b8a0b86e8c29eb1ba70a463e53e46947"
- integrity sha512-b0/8njnJ4lwyHKcGuCMf3x7r1KjxyugB1R/c2iMCjplsJHSC7UY9+OysqgJR5uUXRekUSGniiLgBtac/lvH6wg==
- dependencies:
- "@types/prosemirror-state" "*"
-
-"@types/prosemirror-gapcursor@^1.0.4":
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-gapcursor/-/prosemirror-gapcursor-1.0.4.tgz#7df7d373edb33ea8da12084bfd462cf84cd69761"
- integrity sha512-9xKjFIG5947dzerFvkLWp6F53JwrUYoYwh3SgcTFEp8SbSfNNrez/PFYVZKPnoqPoaK5WtTdQTaMwpCV9rXQIg==
- dependencies:
- "@types/prosemirror-model" "*"
- "@types/prosemirror-state" "*"
-
-"@types/prosemirror-history@^1.0.2":
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-history/-/prosemirror-history-1.0.3.tgz#f1110efbe758129b5475e466ff077f0a8d9b964f"
- integrity sha512-5TloMDRavgLjOAKXp1Li8u0xcsspzbT1Cm9F2pwHOkgvQOz1jWQb2VIXO7RVNsFjLBZdIXlyfSLivro3DuMWXg==
- dependencies:
- "@types/prosemirror-model" "*"
- "@types/prosemirror-state" "*"
-
-"@types/prosemirror-inputrules@^1.0.4":
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-inputrules/-/prosemirror-inputrules-1.0.4.tgz#4cb75054d954aa0f6f42099be05eb6c0e6958bae"
- integrity sha512-lJIMpOjO47SYozQybUkpV6QmfuQt7GZKHtVrvS+mR5UekA8NMC5HRIVMyaIauJLWhKU6oaNjpVaXdw41kh165g==
- dependencies:
- "@types/prosemirror-model" "*"
- "@types/prosemirror-state" "*"
-
-"@types/prosemirror-keymap@^1.0.4":
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.4.tgz#f73c79810e8d0e0a20d153d84f998f02e5afbc0c"
- integrity sha512-ycevwkqUh+jEQtPwqO7sWGcm+Sybmhu8MpBsM8DlO3+YTKnXbKA6SDz/+q14q1wK3UA8lHJyfR+v+GPxfUSemg==
- dependencies:
- "@types/prosemirror-commands" "*"
- "@types/prosemirror-model" "*"
- "@types/prosemirror-state" "*"
- "@types/prosemirror-view" "*"
-
-"@types/prosemirror-model@*", "@types/prosemirror-model@^1.16.1":
- version "1.16.2"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-model/-/prosemirror-model-1.16.2.tgz#8896adac3a5d5d66f06491bb13940aa734a7b6e8"
- integrity sha512-1XPJopkKP3oHSBP61uuSuW13DIDZPWvAzP6Pv2/6mixk8EBPUeRGIW548DjJTicMo23gEg1zvCZy9asblQdWag==
- dependencies:
- "@types/orderedmap" "*"
-
-"@types/prosemirror-schema-list@^1.0.3":
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-schema-list/-/prosemirror-schema-list-1.0.3.tgz#bdf1893a7915fbdc5c49b3cac9368e96213d70de"
- integrity sha512-uWybOf+M2Ea7rlbs0yLsS4YJYNGXYtn4N+w8HCw3Vvfl6wBAROzlMt0gV/D/VW/7J/LlAjwMezuGe8xi24HzXA==
- dependencies:
- "@types/orderedmap" "*"
- "@types/prosemirror-model" "*"
- "@types/prosemirror-state" "*"
-
-"@types/prosemirror-state@*", "@types/prosemirror-state@^1.2.6":
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-state/-/prosemirror-state-1.3.0.tgz#7fd25db7244c027eef0849d79b112a8a0dfbb483"
- integrity sha512-nMdUF6w8B++NH4V54X+4GvDty7M02UfuHQW0s1AS25Z4ZrOW4RSY2+s57doXBbeMSjzYV/QoMxCY2sT3KQ2VdQ==
- dependencies:
- "@types/prosemirror-model" "*"
- "@types/prosemirror-transform" "*"
- "@types/prosemirror-view" "*"
-
-"@types/prosemirror-transform@*", "@types/prosemirror-transform@^1.1.6":
- version "1.4.2"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-transform/-/prosemirror-transform-1.4.2.tgz#0be91da7ee962c40c19ae1db1e4bf5b6e7ee6914"
- integrity sha512-FZNzjYm6YUkb1XXOrw2193TiFzwM92ui1nycNaRSd5JDbugf9yBLkXm4Rq3HGJJxBBkRcUE8niqUW5aWlXQQiQ==
- dependencies:
- "@types/prosemirror-model" "*"
-
-"@types/prosemirror-view@*", "@types/prosemirror-view@^1.23.1":
- version "1.23.3"
- resolved "https://registry.yarnpkg.com/@types/prosemirror-view/-/prosemirror-view-1.23.3.tgz#8ad847fc45b9c7eba8362fccdafc7144c491ea35"
- integrity sha512-T5dPDmZiXAazJVSvnx55D6h4mcpiH2q2wTyO9zIeOdox5zx964+zcDl9dFNaXG3qCGlERwMPckhBZL1HCxyygw==
- dependencies:
- "@types/prosemirror-model" "*"
- "@types/prosemirror-state" "*"
- "@types/prosemirror-transform" "*"
-
-"@types/q@^1.5.1":
- version "1.5.5"
- resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df"
- integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==
-
-"@types/qs@*":
- version "6.9.7"
- resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
- integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
-
-"@types/range-parser@*":
- version "1.2.4"
- resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
- integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
-
-"@types/react-dom@^17.0.9":
- version "17.0.17"
- resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.17.tgz#2e3743277a793a96a99f1bf87614598289da68a1"
- integrity sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg==
- dependencies:
- "@types/react" "^17"
-
-"@types/react-redux@^7.1.20":
- version "7.1.24"
- resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.24.tgz#6caaff1603aba17b27d20f8ad073e4c077e975c0"
- integrity sha512-7FkurKcS1k0FHZEtdbbgN8Oc6b+stGSfZYjQGicofJ0j4U0qIn/jaSvnP2pLwZKiai3/17xqqxkkrxTgN8UNbQ==
- dependencies:
- "@types/hoist-non-react-statics" "^3.3.0"
- "@types/react" "*"
- hoist-non-react-statics "^3.3.0"
- redux "^4.0.0"
-
-"@types/react-router-dom@^5.3.1":
- version "5.3.3"
- resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.3.tgz#e9d6b4a66fcdbd651a5f106c2656a30088cc1e83"
- integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
- dependencies:
- "@types/history" "^4.7.11"
- "@types/react" "*"
- "@types/react-router" "*"
-
-"@types/react-router@*":
- version "5.1.18"
- resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.18.tgz#c8851884b60bc23733500d86c1266e1cfbbd9ef3"
- integrity sha512-YYknwy0D0iOwKQgz9v8nOzt2J6l4gouBmDnWqUUznltOTaon+r8US8ky8HvN0tXvc38U9m6z/t2RsVsnd1zM0g==
- dependencies:
- "@types/history" "^4.7.11"
- "@types/react" "*"
-
-"@types/react@*":
- version "18.0.10"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.10.tgz#5692944d4a45e204fb7a981eb1388afe919cf4d0"
- integrity sha512-dIugadZuIPrRzvIEevIu7A1smqOAjkSMv8qOfwPt9Ve6i6JT/FQcCHyk2qIAxwsQNKZt5/oGR0T4z9h2dXRAkg==
- dependencies:
- "@types/prop-types" "*"
- "@types/scheduler" "*"
- csstype "^3.0.2"
-
-"@types/react@^17", "@types/react@^17.0.30":
- version "17.0.45"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.45.tgz#9b3d5b661fd26365fefef0e766a1c6c30ccf7b3f"
- integrity sha512-YfhQ22Lah2e3CHPsb93tRwIGNiSwkuz1/blk4e6QrWS0jQzCSNbGLtOEYhPg02W0yGTTmpajp7dCTbBAMN3qsg==
- dependencies:
- "@types/prop-types" "*"
- "@types/scheduler" "*"
- csstype "^3.0.2"
-
-"@types/refractor@^3.0.0":
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/@types/refractor/-/refractor-3.0.2.tgz#2d42128d59f78f84d2c799ffc5ab5cadbcba2d82"
- integrity sha512-2HMXuwGuOqzUG+KUTm9GDJCHl0LCBKsB5cg28ujEmVi/0qgTb6jOmkVSO5K48qXksyl2Fr3C0Q2VrgD4zbwyXg==
- dependencies:
- "@types/prismjs" "*"
-
-"@types/resolve@1.17.1":
- version "1.17.1"
- resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
- integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==
- dependencies:
- "@types/node" "*"
-
-"@types/retry@0.12.0":
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d"
- integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==
-
-"@types/scheduler@*":
- version "0.16.2"
- resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
- integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
-
-"@types/serve-index@^1.9.1":
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/@types/serve-index/-/serve-index-1.9.1.tgz#1b5e85370a192c01ec6cec4735cf2917337a6278"
- integrity sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==
- dependencies:
- "@types/express" "*"
-
-"@types/serve-static@*":
- version "1.13.10"
- resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9"
- integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==
- dependencies:
- "@types/mime" "^1"
- "@types/node" "*"
-
-"@types/sockjs@^0.3.33":
- version "0.3.33"
- resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f"
- integrity sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==
- dependencies:
- "@types/node" "*"
-
-"@types/stack-utils@^2.0.0":
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
- integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
-
-"@types/trusted-types@^2.0.2":
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
- integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
-
-"@types/unist@*", "@types/unist@^2.0.0", "@types/unist@^2.0.6":
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d"
- integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==
-
-"@types/ws@^8.5.1":
- version "8.5.3"
- resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
- integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==
- dependencies:
- "@types/node" "*"
-
-"@types/yargs-parser@*":
- version "21.0.0"
- resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
- integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==
-
-"@types/yargs@^16.0.0":
- version "16.0.4"
- resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.4.tgz#26aad98dd2c2a38e421086ea9ad42b9e51642977"
- integrity sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==
- dependencies:
- "@types/yargs-parser" "*"
-
-"@types/yargs@^17.0.8":
- version "17.0.10"
- resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a"
- integrity sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==
- dependencies:
- "@types/yargs-parser" "*"
-
-"@typescript-eslint/eslint-plugin@^5.5.0":
- version "5.27.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.27.0.tgz#23d82a4f21aaafd8f69dbab7e716323bb6695cc8"
- integrity sha512-DDrIA7GXtmHXr1VCcx9HivA39eprYBIFxbQEHI6NyraRDxCGpxAFiYQAT/1Y0vh1C+o2vfBiy4IuPoXxtTZCAQ==
- dependencies:
- "@typescript-eslint/scope-manager" "5.27.0"
- "@typescript-eslint/type-utils" "5.27.0"
- "@typescript-eslint/utils" "5.27.0"
- debug "^4.3.4"
- functional-red-black-tree "^1.0.1"
- ignore "^5.2.0"
- regexpp "^3.2.0"
- semver "^7.3.7"
- tsutils "^3.21.0"
-
-"@typescript-eslint/experimental-utils@^5.0.0":
- version "5.27.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.27.0.tgz#dfe4c6087f60be8950e32fa83f4a8f2fccd86e47"
- integrity sha512-ZOn342bYh19IYvkiorrqnzNoRAr91h3GiFSSfa4tlHV+R9GgR8SxCwAi8PKMyT8+pfwMxfQdNbwKsMurbF9hzg==
- dependencies:
- "@typescript-eslint/utils" "5.27.0"
-
-"@typescript-eslint/parser@^5.5.0":
- version "5.27.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.27.0.tgz#62bb091ed5cf9c7e126e80021bb563dcf36b6b12"
- integrity sha512-8oGjQF46c52l7fMiPPvX4It3u3V3JipssqDfHQ2hcR0AeR8Zge+OYyKUCm5b70X72N1qXt0qgHenwN6Gc2SXZA==
- dependencies:
- "@typescript-eslint/scope-manager" "5.27.0"
- "@typescript-eslint/types" "5.27.0"
- "@typescript-eslint/typescript-estree" "5.27.0"
- debug "^4.3.4"
-
-"@typescript-eslint/scope-manager@5.27.0":
- version "5.27.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.27.0.tgz#a272178f613050ed62f51f69aae1e19e870a8bbb"
- integrity sha512-VnykheBQ/sHd1Vt0LJ1JLrMH1GzHO+SzX6VTXuStISIsvRiurue/eRkTqSrG0CexHQgKG8shyJfR4o5VYioB9g==
- dependencies:
- "@typescript-eslint/types" "5.27.0"
- "@typescript-eslint/visitor-keys" "5.27.0"
-
-"@typescript-eslint/type-utils@5.27.0":
- version "5.27.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.27.0.tgz#36fd95f6747412251d79c795b586ba766cf0974b"
- integrity sha512-vpTvRRchaf628Hb/Xzfek+85o//zEUotr1SmexKvTfs7czXfYjXVT/a5yDbpzLBX1rhbqxjDdr1Gyo0x1Fc64g==
- dependencies:
- "@typescript-eslint/utils" "5.27.0"
- debug "^4.3.4"
- tsutils "^3.21.0"
-
-"@typescript-eslint/types@5.27.0":
- version "5.27.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.27.0.tgz#c3f44b9dda6177a9554f94a74745ca495ba9c001"
- integrity sha512-lY6C7oGm9a/GWhmUDOs3xAVRz4ty/XKlQ2fOLr8GAIryGn0+UBOoJDWyHer3UgrHkenorwvBnphhP+zPmzmw0A==
-
-"@typescript-eslint/typescript-estree@5.27.0":
- version "5.27.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.27.0.tgz#7965f5b553c634c5354a47dcce0b40b94611e995"
- integrity sha512-QywPMFvgZ+MHSLRofLI7BDL+UczFFHyj0vF5ibeChDAJgdTV8k4xgEwF0geFhVlPc1p8r70eYewzpo6ps+9LJQ==
- dependencies:
- "@typescript-eslint/types" "5.27.0"
- "@typescript-eslint/visitor-keys" "5.27.0"
- debug "^4.3.4"
- globby "^11.1.0"
- is-glob "^4.0.3"
- semver "^7.3.7"
- tsutils "^3.21.0"
-
-"@typescript-eslint/utils@5.27.0", "@typescript-eslint/utils@^5.13.0":
- version "5.27.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.27.0.tgz#d0021cbf686467a6a9499bd0589e19665f9f7e71"
- integrity sha512-nZvCrkIJppym7cIbP3pOwIkAefXOmfGPnCM0LQfzNaKxJHI6VjI8NC662uoiPlaf5f6ymkTy9C3NQXev2mdXmA==
- dependencies:
- "@types/json-schema" "^7.0.9"
- "@typescript-eslint/scope-manager" "5.27.0"
- "@typescript-eslint/types" "5.27.0"
- "@typescript-eslint/typescript-estree" "5.27.0"
- eslint-scope "^5.1.1"
- eslint-utils "^3.0.0"
-
-"@typescript-eslint/visitor-keys@5.27.0":
- version "5.27.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.27.0.tgz#97aa9a5d2f3df8215e6d3b77f9d214a24db269bd"
- integrity sha512-46cYrteA2MrIAjv9ai44OQDUoCZyHeGIc4lsjCUX2WT6r4C+kidz1bNiR4017wHOPUythYeH+Sc7/cFP97KEAA==
- dependencies:
- "@typescript-eslint/types" "5.27.0"
- eslint-visitor-keys "^3.3.0"
-
-"@uiw/react-codemirror@4.5.3":
- version "4.5.3"
- resolved "https://registry.yarnpkg.com/@uiw/react-codemirror/-/react-codemirror-4.5.3.tgz#bc5f9dc4327c9ac1dbf94dd0f9e11590dda0629c"
- integrity sha512-GmeYFINI8ZQ2bhSwUXt8nioVianSTI1BoPvelbIzIiZAf7UN63egrHqyEZIqCMZdi+q62CpDPgwTS1LYSsHlfw==
- dependencies:
- "@babel/runtime" "^7.17.2"
- "@codemirror/basic-setup" "^0.19.1"
- "@codemirror/state" "^0.19.9"
- "@codemirror/theme-one-dark" "^0.19.1"
- "@codemirror/view" "^0.19.45"
-
-"@webassemblyjs/ast@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
- integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==
- dependencies:
- "@webassemblyjs/helper-numbers" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
-
-"@webassemblyjs/floating-point-hex-parser@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f"
- integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==
-
-"@webassemblyjs/helper-api-error@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16"
- integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==
-
-"@webassemblyjs/helper-buffer@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5"
- integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==
-
-"@webassemblyjs/helper-numbers@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae"
- integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==
- dependencies:
- "@webassemblyjs/floating-point-hex-parser" "1.11.1"
- "@webassemblyjs/helper-api-error" "1.11.1"
- "@xtuc/long" "4.2.2"
-
-"@webassemblyjs/helper-wasm-bytecode@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1"
- integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==
-
-"@webassemblyjs/helper-wasm-section@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a"
- integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-buffer" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/wasm-gen" "1.11.1"
-
-"@webassemblyjs/ieee754@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614"
- integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==
- dependencies:
- "@xtuc/ieee754" "^1.2.0"
-
-"@webassemblyjs/leb128@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5"
- integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==
- dependencies:
- "@xtuc/long" "4.2.2"
-
-"@webassemblyjs/utf8@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff"
- integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==
-
-"@webassemblyjs/wasm-edit@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6"
- integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-buffer" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/helper-wasm-section" "1.11.1"
- "@webassemblyjs/wasm-gen" "1.11.1"
- "@webassemblyjs/wasm-opt" "1.11.1"
- "@webassemblyjs/wasm-parser" "1.11.1"
- "@webassemblyjs/wast-printer" "1.11.1"
-
-"@webassemblyjs/wasm-gen@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76"
- integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/ieee754" "1.11.1"
- "@webassemblyjs/leb128" "1.11.1"
- "@webassemblyjs/utf8" "1.11.1"
-
-"@webassemblyjs/wasm-opt@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2"
- integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-buffer" "1.11.1"
- "@webassemblyjs/wasm-gen" "1.11.1"
- "@webassemblyjs/wasm-parser" "1.11.1"
-
-"@webassemblyjs/wasm-parser@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199"
- integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/helper-api-error" "1.11.1"
- "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
- "@webassemblyjs/ieee754" "1.11.1"
- "@webassemblyjs/leb128" "1.11.1"
- "@webassemblyjs/utf8" "1.11.1"
-
-"@webassemblyjs/wast-printer@1.11.1":
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0"
- integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==
- dependencies:
- "@webassemblyjs/ast" "1.11.1"
- "@xtuc/long" "4.2.2"
-
-"@xtuc/ieee754@^1.2.0":
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
- integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
-
-"@xtuc/long@4.2.2":
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
- integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
-
-abab@^2.0.3, abab@^2.0.5:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
- integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
-
-accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
- version "1.3.8"
- resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
- integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
- dependencies:
- mime-types "~2.1.34"
- negotiator "0.6.3"
-
-acorn-globals@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
- integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==
- dependencies:
- acorn "^7.1.1"
- acorn-walk "^7.1.1"
-
-acorn-import-assertions@^1.7.6:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9"
- integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==
-
-acorn-jsx@^5.3.2:
- version "5.3.2"
- resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
- integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
-
-acorn-node@^1.8.2:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
- integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==
- dependencies:
- acorn "^7.0.0"
- acorn-walk "^7.0.0"
- xtend "^4.0.2"
-
-acorn-walk@^7.0.0, acorn-walk@^7.1.1:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
- integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
-
-acorn@^7.0.0, acorn@^7.1.1:
- version "7.4.1"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
- integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-
-acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1:
- version "8.7.1"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
- integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
-
-address@^1.0.1, address@^1.1.2:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/address/-/address-1.2.0.tgz#d352a62c92fee90f89a693eccd2a8b2139ab02d9"
- integrity sha512-tNEZYz5G/zYunxFm7sfhAxkXEuLj3K6BKwv6ZURlsF6yiUQ65z0Q2wZW9L5cPUl9ocofGvXOdFYbFHp0+6MOig==
-
-adjust-sourcemap-loader@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz#fc4a0fd080f7d10471f30a7320f25560ade28c99"
- integrity sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==
- dependencies:
- loader-utils "^2.0.0"
- regex-parser "^2.2.11"
-
-agent-base@6:
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
- integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
- dependencies:
- debug "4"
-
-ajv-formats@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
- integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
- dependencies:
- ajv "^8.0.0"
-
-ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
- version "3.5.2"
- resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
- integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
-
-ajv-keywords@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16"
- integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==
- dependencies:
- fast-deep-equal "^3.1.3"
-
-ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5:
- version "6.12.6"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
- integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
- dependencies:
- fast-deep-equal "^3.1.1"
- fast-json-stable-stringify "^2.0.0"
- json-schema-traverse "^0.4.1"
- uri-js "^4.2.2"
-
-ajv@^8.0.0, ajv@^8.6.0, ajv@^8.8.0:
- version "8.11.0"
- resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f"
- integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
- dependencies:
- fast-deep-equal "^3.1.1"
- json-schema-traverse "^1.0.0"
- require-from-string "^2.0.2"
- uri-js "^4.2.2"
-
-ansi-escapes@^4.2.1, ansi-escapes@^4.3.1:
- version "4.3.2"
- resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
- integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
- dependencies:
- type-fest "^0.21.3"
-
-ansi-html-community@^0.0.8:
- version "0.0.8"
- resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41"
- integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==
-
-ansi-regex@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
- integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
-
-ansi-regex@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a"
- integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==
-
-ansi-styles@^3.2.1:
- version "3.2.1"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
- integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
- dependencies:
- color-convert "^1.9.0"
-
-ansi-styles@^4.0.0, ansi-styles@^4.1.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
- integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
- dependencies:
- color-convert "^2.0.1"
-
-ansi-styles@^5.0.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
- integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
-
-anymatch@^3.0.3, anymatch@~3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
- integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
- dependencies:
- normalize-path "^3.0.0"
- picomatch "^2.0.4"
-
-arg@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.1.tgz#eb0c9a8f77786cad2af8ff2b862899842d7b6adb"
- integrity sha512-e0hDa9H2Z9AwFkk2qDlwhoMYE4eToKarchkQHovNdLTCYMHZHeRjI71crOh+dio4K6u1IcwubQqo79Ga4CyAQA==
-
-argparse@^1.0.7:
- version "1.0.10"
- resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
- integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
- dependencies:
- sprintf-js "~1.0.2"
-
-argparse@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
- integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
-
-aria-query@^4.2.2:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b"
- integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==
- dependencies:
- "@babel/runtime" "^7.10.2"
- "@babel/runtime-corejs3" "^7.10.2"
-
-array-flatten@1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
- integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
-
-array-flatten@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099"
- integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==
-
-array-includes@^3.1.4, array-includes@^3.1.5:
- version "3.1.5"
- resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb"
- integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.4"
- es-abstract "^1.19.5"
- get-intrinsic "^1.1.1"
- is-string "^1.0.7"
-
-array-union@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
- integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
-
-array.prototype.flat@^1.2.5:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b"
- integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
- es-abstract "^1.19.2"
- es-shim-unscopables "^1.0.0"
-
-array.prototype.flatmap@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f"
- integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
- es-abstract "^1.19.2"
- es-shim-unscopables "^1.0.0"
-
-array.prototype.reduce@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f"
- integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
- es-abstract "^1.19.2"
- es-array-method-boxes-properly "^1.0.0"
- is-string "^1.0.7"
-
-asap@~2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
- integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
-
-ast-types-flow@^0.0.7:
- version "0.0.7"
- resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad"
- integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==
-
-async@^3.2.3:
- version "3.2.3"
- resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
- integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
-
-asynckit@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
- integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
-
-at-least-node@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
- integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
-
-autoprefixer@^10.4.7:
- version "10.4.7"
- resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.7.tgz#1db8d195f41a52ca5069b7593be167618edbbedf"
- integrity sha512-ypHju4Y2Oav95SipEcCcI5J7CGPuvz8oat7sUtYj3ClK44bldfvtvcxK6IEK++7rqB7YchDGzweZIBG+SD0ZAA==
- dependencies:
- browserslist "^4.20.3"
- caniuse-lite "^1.0.30001335"
- fraction.js "^4.2.0"
- normalize-range "^0.1.2"
- picocolors "^1.0.0"
- postcss-value-parser "^4.2.0"
-
-axe-core@^4.3.5:
- version "4.4.2"
- resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.2.tgz#dcf7fb6dea866166c3eab33d68208afe4d5f670c"
- integrity sha512-LVAaGp/wkkgYJcjmHsoKx4juT1aQvJyPcW09MLCjVTh3V2cc6PnyempiLMNH5iMdfIX/zdbjUx2KDjMLCTdPeA==
-
-axobject-query@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
- integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
-
-babel-jest@^27.4.2, babel-jest@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444"
- integrity sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==
- dependencies:
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/babel__core" "^7.1.14"
- babel-plugin-istanbul "^6.1.1"
- babel-preset-jest "^27.5.1"
- chalk "^4.0.0"
- graceful-fs "^4.2.9"
- slash "^3.0.0"
-
-babel-loader@^8.2.3:
- version "8.2.5"
- resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e"
- integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==
- dependencies:
- find-cache-dir "^3.3.1"
- loader-utils "^2.0.0"
- make-dir "^3.1.0"
- schema-utils "^2.6.5"
-
-babel-plugin-dynamic-import-node@^2.3.3:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3"
- integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==
- dependencies:
- object.assign "^4.1.0"
-
-babel-plugin-istanbul@^6.1.1:
- version "6.1.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73"
- integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.0.0"
- "@istanbuljs/load-nyc-config" "^1.0.0"
- "@istanbuljs/schema" "^0.1.2"
- istanbul-lib-instrument "^5.0.4"
- test-exclude "^6.0.0"
-
-babel-plugin-jest-hoist@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz#9be98ecf28c331eb9f5df9c72d6f89deb8181c2e"
- integrity sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==
- dependencies:
- "@babel/template" "^7.3.3"
- "@babel/types" "^7.3.3"
- "@types/babel__core" "^7.0.0"
- "@types/babel__traverse" "^7.0.6"
-
-babel-plugin-macros@^2.6.1:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138"
- integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg==
- dependencies:
- "@babel/runtime" "^7.7.2"
- cosmiconfig "^6.0.0"
- resolve "^1.12.0"
-
-babel-plugin-macros@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz#9ef6dc74deb934b4db344dc973ee851d148c50c1"
- integrity sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==
- dependencies:
- "@babel/runtime" "^7.12.5"
- cosmiconfig "^7.0.0"
- resolve "^1.19.0"
-
-babel-plugin-named-asset-import@^0.3.8:
- version "0.3.8"
- resolved "https://registry.yarnpkg.com/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz#6b7fa43c59229685368683c28bc9734f24524cc2"
- integrity sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==
-
-babel-plugin-polyfill-corejs2@^0.3.0:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5"
- integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==
- dependencies:
- "@babel/compat-data" "^7.13.11"
- "@babel/helper-define-polyfill-provider" "^0.3.1"
- semver "^6.1.1"
-
-babel-plugin-polyfill-corejs3@^0.5.0:
- version "0.5.2"
- resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72"
- integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==
- dependencies:
- "@babel/helper-define-polyfill-provider" "^0.3.1"
- core-js-compat "^3.21.0"
-
-babel-plugin-polyfill-regenerator@^0.3.0:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990"
- integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==
- dependencies:
- "@babel/helper-define-polyfill-provider" "^0.3.1"
-
-babel-plugin-transform-react-remove-prop-types@^0.4.24:
- version "0.4.24"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a"
- integrity sha512-eqj0hVcJUR57/Ug2zE1Yswsw4LhuqqHhD+8v120T1cl3kjg76QwtyBrdIk4WVwK+lAhBJVYCd/v+4nc4y+8JsA==
-
-babel-preset-current-node-syntax@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b"
- integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==
- dependencies:
- "@babel/plugin-syntax-async-generators" "^7.8.4"
- "@babel/plugin-syntax-bigint" "^7.8.3"
- "@babel/plugin-syntax-class-properties" "^7.8.3"
- "@babel/plugin-syntax-import-meta" "^7.8.3"
- "@babel/plugin-syntax-json-strings" "^7.8.3"
- "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3"
- "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
- "@babel/plugin-syntax-numeric-separator" "^7.8.3"
- "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
- "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
- "@babel/plugin-syntax-optional-chaining" "^7.8.3"
- "@babel/plugin-syntax-top-level-await" "^7.8.3"
-
-babel-preset-jest@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81"
- integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==
- dependencies:
- babel-plugin-jest-hoist "^27.5.1"
- babel-preset-current-node-syntax "^1.0.0"
-
-babel-preset-react-app@^10.0.1:
- version "10.0.1"
- resolved "https://registry.yarnpkg.com/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz#ed6005a20a24f2c88521809fa9aea99903751584"
- integrity sha512-b0D9IZ1WhhCWkrTXyFuIIgqGzSkRIH5D5AmB0bXbzYAB1OBAwHcUeyWW2LorutLWF5btNo/N7r/cIdmvvKJlYg==
- dependencies:
- "@babel/core" "^7.16.0"
- "@babel/plugin-proposal-class-properties" "^7.16.0"
- "@babel/plugin-proposal-decorators" "^7.16.4"
- "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.0"
- "@babel/plugin-proposal-numeric-separator" "^7.16.0"
- "@babel/plugin-proposal-optional-chaining" "^7.16.0"
- "@babel/plugin-proposal-private-methods" "^7.16.0"
- "@babel/plugin-transform-flow-strip-types" "^7.16.0"
- "@babel/plugin-transform-react-display-name" "^7.16.0"
- "@babel/plugin-transform-runtime" "^7.16.4"
- "@babel/preset-env" "^7.16.4"
- "@babel/preset-react" "^7.16.0"
- "@babel/preset-typescript" "^7.16.0"
- "@babel/runtime" "^7.16.3"
- babel-plugin-macros "^3.1.0"
- babel-plugin-transform-react-remove-prop-types "^0.4.24"
-
-bail@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d"
- integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==
-
-balanced-match@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
- integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
-
-batch@0.6.1:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
- integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==
-
-bfj@^7.0.2:
- version "7.0.2"
- resolved "https://registry.yarnpkg.com/bfj/-/bfj-7.0.2.tgz#1988ce76f3add9ac2913fd8ba47aad9e651bfbb2"
- integrity sha512-+e/UqUzwmzJamNF50tBV6tZPTORow7gQ96iFow+8b562OdMpEK0BcJEq2OSPEDmAbSMBQ7PKZ87ubFkgxpYWgw==
- dependencies:
- bluebird "^3.5.5"
- check-types "^11.1.1"
- hoopy "^0.1.4"
- tryer "^1.0.1"
-
-big.js@^5.2.2:
- version "5.2.2"
- resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
- integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==
-
-binary-extensions@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
- integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
-
-bluebird@^3.5.5:
- version "3.7.2"
- resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
- integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
-
-body-parser@1.20.0:
- version "1.20.0"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5"
- integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==
- dependencies:
- bytes "3.1.2"
- content-type "~1.0.4"
- debug "2.6.9"
- depd "2.0.0"
- destroy "1.2.0"
- http-errors "2.0.0"
- iconv-lite "0.4.24"
- on-finished "2.4.1"
- qs "6.10.3"
- raw-body "2.5.1"
- type-is "~1.6.18"
- unpipe "1.0.0"
-
-bonjour-service@^1.0.11:
- version "1.0.12"
- resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.0.12.tgz#28fbd4683f5f2e36feedb833e24ba661cac960c3"
- integrity sha512-pMmguXYCu63Ug37DluMKEHdxc+aaIf/ay4YbF8Gxtba+9d3u+rmEWy61VK3Z3hp8Rskok3BunHYnG0dUHAsblw==
- dependencies:
- array-flatten "^2.1.2"
- dns-equal "^1.0.0"
- fast-deep-equal "^3.1.3"
- multicast-dns "^7.2.4"
-
-boolbase@^1.0.0, boolbase@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
- integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
-
-brace-expansion@^1.1.7:
- version "1.1.11"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
- integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
- dependencies:
- balanced-match "^1.0.0"
- concat-map "0.0.1"
-
-brace-expansion@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
- integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
- dependencies:
- balanced-match "^1.0.0"
-
-braces@^3.0.2, braces@~3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
- integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
- dependencies:
- fill-range "^7.0.1"
-
-browser-process-hrtime@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
- integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
-
-browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.6, browserslist@^4.18.1, browserslist@^4.20.2, browserslist@^4.20.3:
- version "4.20.3"
- resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.3.tgz#eb7572f49ec430e054f56d52ff0ebe9be915f8bf"
- integrity sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==
- dependencies:
- caniuse-lite "^1.0.30001332"
- electron-to-chromium "^1.4.118"
- escalade "^3.1.1"
- node-releases "^2.0.3"
- picocolors "^1.0.0"
-
-bser@2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
- integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==
- dependencies:
- node-int64 "^0.4.0"
-
-buffer-from@^1.0.0:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
- integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
-
-builtin-modules@^3.1.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6"
- integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==
-
-bytes@3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
- integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==
-
-bytes@3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
- integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
-
-call-bind@^1.0.0, call-bind@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
- integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
- dependencies:
- function-bind "^1.1.1"
- get-intrinsic "^1.0.2"
-
-callsites@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
- integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
-
-camel-case@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
- integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==
- dependencies:
- pascal-case "^3.1.2"
- tslib "^2.0.3"
-
-camelcase-css@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
- integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
-
-camelcase@^5.3.1:
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
- integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
-
-camelcase@^6.2.0, camelcase@^6.2.1:
- version "6.3.0"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
- integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
-
-caniuse-api@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0"
- integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==
- dependencies:
- browserslist "^4.0.0"
- caniuse-lite "^1.0.0"
- lodash.memoize "^4.1.2"
- lodash.uniq "^4.5.0"
-
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001332, caniuse-lite@^1.0.30001335:
- version "1.0.30001346"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001346.tgz#e895551b46b9cc9cc9de852facd42f04839a8fbe"
- integrity sha512-q6ibZUO2t88QCIPayP/euuDREq+aMAxFE5S70PkrLh0iTDj/zEhgvJRKC2+CvXY6EWc6oQwUR48lL5vCW6jiXQ==
-
-case-sensitive-paths-webpack-plugin@^2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4"
- integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==
-
-ccount@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5"
- integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==
-
-chalk@^2.0.0, chalk@^2.4.1:
- version "2.4.2"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
- integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
- dependencies:
- ansi-styles "^3.2.1"
- escape-string-regexp "^1.0.5"
- supports-color "^5.3.0"
-
-chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
- integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
- dependencies:
- ansi-styles "^4.1.0"
- supports-color "^7.1.0"
-
-char-regex@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
- integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
-
-char-regex@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-2.0.1.tgz#6dafdb25f9d3349914079f010ba8d0e6ff9cd01e"
- integrity sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==
-
-character-entities-html4@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b"
- integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==
-
-character-entities-legacy@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b"
- integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==
-
-character-entities@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.1.tgz#98724833e1e27990dee0bd0f2b8a859c3476aac7"
- integrity sha512-OzmutCf2Kmc+6DrFrrPS8/tDh2+DpnrfzdICHWhcVC9eOd0N1PXmQEE1a8iM4IziIAG+8tmTq3K+oo0ubH6RRQ==
-
-character-reference-invalid@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9"
- integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==
-
-charcodes@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/charcodes/-/charcodes-0.2.0.tgz#5208d327e6cc05f99eb80ffc814707572d1f14e4"
- integrity sha512-Y4kiDb+AM4Ecy58YkuZrrSRJBDQdQ2L+NyS1vHHFtNtUjgutcZfx3yp1dAONI/oPaPmyGfCLx5CxL+zauIMyKQ==
-
-check-types@^11.1.1:
- version "11.1.2"
- resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.1.2.tgz#86a7c12bf5539f6324eb0e70ca8896c0e38f3e2f"
- integrity sha512-tzWzvgePgLORb9/3a0YenggReLKAIb2owL03H2Xdoe5pKcUyWRSEQ8xfCar8t2SIAuEDwtmx2da1YB52YuHQMQ==
-
-chokidar@^3.4.2, chokidar@^3.5.3:
- version "3.5.3"
- resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
- integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
- dependencies:
- anymatch "~3.1.2"
- braces "~3.0.2"
- glob-parent "~5.1.2"
- is-binary-path "~2.1.0"
- is-glob "~4.0.1"
- normalize-path "~3.0.0"
- readdirp "~3.6.0"
- optionalDependencies:
- fsevents "~2.3.2"
-
-chrome-trace-event@^1.0.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
- integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
-
-ci-info@^3.2.0:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.1.tgz#58331f6f472a25fe3a50a351ae3052936c2c7f32"
- integrity sha512-SXgeMX9VwDe7iFFaEWkA5AstuER9YKqy4EhHqr4DVqkwmD9rpVimkMKWHdjn30Ja45txyjhSn63lVX69eVCckg==
-
-cjs-module-lexer@^1.0.0:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40"
- integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==
-
-clean-css@^5.2.2:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.0.tgz#ad3d8238d5f3549e83d5f87205189494bc7cbb59"
- integrity sha512-YYuuxv4H/iNb1Z/5IbMRoxgrzjWGhOEFfd+groZ5dMCVkpENiMZmwspdrzBo9286JjM1gZJPAyL7ZIdzuvu2AQ==
- dependencies:
- source-map "~0.6.0"
-
-clipboard@^2.0.11:
- version "2.0.11"
- resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.11.tgz#62180360b97dd668b6b3a84ec226975762a70be5"
- integrity sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==
- dependencies:
- good-listener "^1.2.2"
- select "^1.1.2"
- tiny-emitter "^2.0.0"
-
-cliui@^7.0.2:
- version "7.0.4"
- resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
- integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
- dependencies:
- string-width "^4.2.0"
- strip-ansi "^6.0.0"
- wrap-ansi "^7.0.0"
-
-co@^4.6.0:
- version "4.6.0"
- resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
- integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==
-
-coa@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3"
- integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==
- dependencies:
- "@types/q" "^1.5.1"
- chalk "^2.4.1"
- q "^1.1.2"
-
-collect-v8-coverage@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
- integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==
-
-color-convert@^1.9.0:
- version "1.9.3"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
- integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
- dependencies:
- color-name "1.1.3"
-
-color-convert@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
- integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
- dependencies:
- color-name "~1.1.4"
-
-color-name@1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
- integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
-
-color-name@^1.1.4, color-name@~1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
- integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
-
-colord@^2.9.1:
- version "2.9.2"
- resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.2.tgz#25e2bacbbaa65991422c07ea209e2089428effb1"
- integrity sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==
-
-colorette@^2.0.10:
- version "2.0.16"
- resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da"
- integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==
-
-combined-stream@^1.0.8:
- version "1.0.8"
- resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
- integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
- dependencies:
- delayed-stream "~1.0.0"
-
-comma-separated-tokens@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.2.tgz#d4c25abb679b7751c880be623c1179780fe1dd98"
- integrity sha512-G5yTt3KQN4Yn7Yk4ed73hlZ1evrFKXeUW3086p3PRFNp7m2vIjI6Pg+Kgb+oyzhd9F2qdcoj67+y3SdxL5XWsg==
-
-commander@7, commander@^7.2.0:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
- integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
-
-commander@^2.20.0:
- version "2.20.3"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
- integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
-
-commander@^8.3.0:
- version "8.3.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
- integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
-
-common-path-prefix@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0"
- integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==
-
-common-tags@^1.8.0:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6"
- integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==
-
-commondir@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
- integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
-
-compressible@~2.0.16:
- version "2.0.18"
- resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
- integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
- dependencies:
- mime-db ">= 1.43.0 < 2"
-
-compression@^1.7.4:
- version "1.7.4"
- resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
- integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
- dependencies:
- accepts "~1.3.5"
- bytes "3.0.0"
- compressible "~2.0.16"
- debug "2.6.9"
- on-headers "~1.0.2"
- safe-buffer "5.1.2"
- vary "~1.1.2"
-
-compute-scroll-into-view@^1.0.17:
- version "1.0.17"
- resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab"
- integrity sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==
-
-concat-map@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
- integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
-
-confusing-browser-globals@^1.0.11:
- version "1.0.11"
- resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81"
- integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==
-
-connect-history-api-fallback@^1.6.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
- integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==
-
-content-disposition@0.5.4:
- version "0.5.4"
- resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
- integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
- dependencies:
- safe-buffer "5.2.1"
-
-content-type@~1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
- integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
-
-convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
- integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
- dependencies:
- safe-buffer "~5.1.1"
-
-cookie-signature@1.0.6:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
- integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==
-
-cookie@0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
- integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
-
-copy-anything@^2.0.1:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480"
- integrity sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==
- dependencies:
- is-what "^3.14.1"
-
-core-js-compat@^3.21.0, core-js-compat@^3.22.1:
- version "3.22.8"
- resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.22.8.tgz#46fa34ce1ddf742acd7f95f575f66bbb21e05d62"
- integrity sha512-pQnwg4xtuvc2Bs/5zYQPaEYYSuTxsF7LBWF0SvnVhthZo/Qe+rJpcEekrdNK5DWwDJ0gv0oI9NNX5Mppdy0ctg==
- dependencies:
- browserslist "^4.20.3"
- semver "7.0.0"
-
-core-js-pure@^3.20.2, core-js-pure@^3.8.1:
- version "3.22.8"
- resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.22.8.tgz#f2157793b58719196ccf9673cc14f3683adc0957"
- integrity sha512-bOxbZIy9S5n4OVH63XaLVXZ49QKicjowDx/UELyJ68vxfCRpYsbyh/WNZNfEfAk+ekA8vSjt+gCDpvh672bc3w==
-
-core-js@^3.19.2:
- version "3.22.8"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.22.8.tgz#23f860b1fe60797cc4f704d76c93fea8a2f60631"
- integrity sha512-UoGQ/cfzGYIuiq6Z7vWL1HfkE9U9IZ4Ub+0XSiJTCzvbZzgPA69oDF2f+lgJ6dFFLEdjW5O6svvoKzXX23xFkA==
-
-core-util-is@~1.0.0:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
- integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
-
-cose-base@^1.0.0:
- version "1.0.3"
- resolved "https://npm.shopee.io/cose-base/-/cose-base-1.0.3.tgz#650334b41b869578a543358b80cda7e0abe0a60a"
- integrity sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==
- dependencies:
- layout-base "^1.0.0"
-
-cose-base@^2.2.0:
- version "2.2.0"
- resolved "https://npm.shopee.io/cose-base/-/cose-base-2.2.0.tgz#1c395c35b6e10bb83f9769ca8b817d614add5c01"
- integrity sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==
- dependencies:
- layout-base "^2.0.0"
-
-cosmiconfig@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982"
- integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==
- dependencies:
- "@types/parse-json" "^4.0.0"
- import-fresh "^3.1.0"
- parse-json "^5.0.0"
- path-type "^4.0.0"
- yaml "^1.7.2"
-
-cosmiconfig@^7.0.0:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d"
- integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==
- dependencies:
- "@types/parse-json" "^4.0.0"
- import-fresh "^3.2.1"
- parse-json "^5.0.0"
- path-type "^4.0.0"
- yaml "^1.10.0"
-
-crelt@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94"
- integrity sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==
-
-cross-env@^7.0.3:
- version "7.0.3"
- resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
- integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
- dependencies:
- cross-spawn "^7.0.1"
-
-cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
- version "7.0.3"
- resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
- integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
- dependencies:
- path-key "^3.1.0"
- shebang-command "^2.0.0"
- which "^2.0.1"
-
-crypto-random-string@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
- integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
-
-css-blank-pseudo@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz#36523b01c12a25d812df343a32c322d2a2324561"
- integrity sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==
- dependencies:
- postcss-selector-parser "^6.0.9"
-
-css-declaration-sorter@^6.2.2:
- version "6.2.2"
- resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.2.2.tgz#bfd2f6f50002d6a3ae779a87d3a0c5d5b10e0f02"
- integrity sha512-Ufadglr88ZLsrvS11gjeu/40Lw74D9Am/Jpr3LlYm5Q4ZP5KdlUhG+6u2EjyXeZcxmZ2h1ebCKngDjolpeLHpg==
-
-css-has-pseudo@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz#57f6be91ca242d5c9020ee3e51bbb5b89fc7af73"
- integrity sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==
- dependencies:
- postcss-selector-parser "^6.0.9"
-
-css-loader@^6.5.1:
- version "6.7.1"
- resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.7.1.tgz#e98106f154f6e1baf3fc3bc455cb9981c1d5fd2e"
- integrity sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==
- dependencies:
- icss-utils "^5.1.0"
- postcss "^8.4.7"
- postcss-modules-extract-imports "^3.0.0"
- postcss-modules-local-by-default "^4.0.0"
- postcss-modules-scope "^3.0.0"
- postcss-modules-values "^4.0.0"
- postcss-value-parser "^4.2.0"
- semver "^7.3.5"
-
-css-minimizer-webpack-plugin@^3.2.0:
- version "3.4.1"
- resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz#ab78f781ced9181992fe7b6e4f3422e76429878f"
- integrity sha512-1u6D71zeIfgngN2XNRJefc/hY7Ybsxd74Jm4qngIXyUEk7fss3VUzuHxLAq/R8NAba4QU9OUSaMZlbpRc7bM4Q==
- dependencies:
- cssnano "^5.0.6"
- jest-worker "^27.0.2"
- postcss "^8.3.5"
- schema-utils "^4.0.0"
- serialize-javascript "^6.0.0"
- source-map "^0.6.1"
-
-css-prefers-color-scheme@^6.0.3:
- version "6.0.3"
- resolved "https://registry.yarnpkg.com/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz#ca8a22e5992c10a5b9d315155e7caee625903349"
- integrity sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==
-
-css-select-base-adapter@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7"
- integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==
-
-css-select@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef"
- integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==
- dependencies:
- boolbase "^1.0.0"
- css-what "^3.2.1"
- domutils "^1.7.0"
- nth-check "^1.0.2"
-
-css-select@^4.1.3:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b"
- integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==
- dependencies:
- boolbase "^1.0.0"
- css-what "^6.0.1"
- domhandler "^4.3.1"
- domutils "^2.8.0"
- nth-check "^2.0.1"
-
-css-tree@1.0.0-alpha.37:
- version "1.0.0-alpha.37"
- resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22"
- integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==
- dependencies:
- mdn-data "2.0.4"
- source-map "^0.6.1"
-
-css-tree@^1.1.2, css-tree@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d"
- integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==
- dependencies:
- mdn-data "2.0.14"
- source-map "^0.6.1"
-
-css-what@^3.2.1:
- version "3.4.2"
- resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
- integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
-
-css-what@^6.0.1:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
- integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
-
-cssdb@^6.6.2:
- version "6.6.2"
- resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-6.6.2.tgz#6c1c1777483c909a8fc64f296a51546136b35f45"
- integrity sha512-w08LaP+DRoPlw4g4LSUp+EWRrWTPlrzWREcU7/6IeMfL7tPR2P9oeQ1G+pxyfMmLWBNDwqHWa6kxiuGMLb71EA==
-
-cssesc@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
- integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
-
-cssnano-preset-default@^5.2.10:
- version "5.2.10"
- resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.10.tgz#6dfffe6cc3b13f3bb356a42c49a334a98700ef45"
- integrity sha512-H8TJRhTjBKVOPltp9vr9El9I+IfYsOMhmXdK0LwdvwJcxYX9oWkY7ctacWusgPWAgQq1vt/WO8v+uqpfLnM7QA==
- dependencies:
- css-declaration-sorter "^6.2.2"
- cssnano-utils "^3.1.0"
- postcss-calc "^8.2.3"
- postcss-colormin "^5.3.0"
- postcss-convert-values "^5.1.2"
- postcss-discard-comments "^5.1.2"
- postcss-discard-duplicates "^5.1.0"
- postcss-discard-empty "^5.1.1"
- postcss-discard-overridden "^5.1.0"
- postcss-merge-longhand "^5.1.5"
- postcss-merge-rules "^5.1.2"
- postcss-minify-font-values "^5.1.0"
- postcss-minify-gradients "^5.1.1"
- postcss-minify-params "^5.1.3"
- postcss-minify-selectors "^5.2.1"
- postcss-normalize-charset "^5.1.0"
- postcss-normalize-display-values "^5.1.0"
- postcss-normalize-positions "^5.1.0"
- postcss-normalize-repeat-style "^5.1.0"
- postcss-normalize-string "^5.1.0"
- postcss-normalize-timing-functions "^5.1.0"
- postcss-normalize-unicode "^5.1.0"
- postcss-normalize-url "^5.1.0"
- postcss-normalize-whitespace "^5.1.1"
- postcss-ordered-values "^5.1.1"
- postcss-reduce-initial "^5.1.0"
- postcss-reduce-transforms "^5.1.0"
- postcss-svgo "^5.1.0"
- postcss-unique-selectors "^5.1.1"
-
-cssnano-utils@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-3.1.0.tgz#95684d08c91511edfc70d2636338ca37ef3a6861"
- integrity sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==
-
-cssnano@^5.0.6:
- version "5.1.10"
- resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.10.tgz#fc6ddd9a4d7d238f320634326ed814cf0abf6e1c"
- integrity sha512-ACpnRgDg4m6CZD/+8SgnLcGCgy6DDGdkMbOawwdvVxNietTNLe/MtWcenp6qT0PRt5wzhGl6/cjMWCdhKXC9QA==
- dependencies:
- cssnano-preset-default "^5.2.10"
- lilconfig "^2.0.3"
- yaml "^1.10.2"
-
-csso@^4.0.2, csso@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529"
- integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==
- dependencies:
- css-tree "^1.1.2"
-
-cssom@^0.4.4:
- version "0.4.4"
- resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
- integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==
-
-cssom@~0.3.6:
- version "0.3.8"
- resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
- integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
-
-cssstyle@^2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852"
- integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==
- dependencies:
- cssom "~0.3.6"
-
-csstype@^3.0.2:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
- integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
-
-customize-cra-less-loader@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/customize-cra-less-loader/-/customize-cra-less-loader-2.0.0.tgz#633b47e5eee300d6b4fe2988bc97d66646434e04"
- integrity sha512-b+ofZYNNyZgav09t+HJA4Fm7IKhmF6F/QOCrLpZvo0cod5uT+53EuelvYJV9t12vGgd/xJuvfw+AbaQhJR2Tgg==
-
-customize-cra@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/customize-cra/-/customize-cra-1.0.0.tgz#73286563631aa08127ad4d30a2e3c89cf4e93c8d"
- integrity sha512-DbtaLuy59224U+xCiukkxSq8clq++MOtJ1Et7LED1fLszWe88EoblEYFBJ895sB1mC6B4uu3xPT/IjClELhMbA==
- dependencies:
- lodash.flow "^3.5.0"
-
-cytoscape-cose-bilkent@^4.1.0:
- version "4.1.0"
- resolved "https://npm.shopee.io/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz#762fa121df9930ffeb51a495d87917c570ac209b"
- integrity sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==
- dependencies:
- cose-base "^1.0.0"
-
-cytoscape-fcose@^2.1.0:
- version "2.2.0"
- resolved "https://npm.shopee.io/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz#e4d6f6490df4fab58ae9cea9e5c3ab8d7472f471"
- integrity sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==
- dependencies:
- cose-base "^2.2.0"
-
-cytoscape@^3.23.0:
- version "3.31.0"
- resolved "https://npm.shopee.io/cytoscape/-/cytoscape-3.31.0.tgz#cffbbb8ca51db01cbf360e0cf59088db6d429837"
- integrity sha512-zDGn1K/tfZwEnoGOcHc0H4XazqAAXAuDpcYw9mUnUjATjqljyCNGJv8uEvbvxGaGHaVshxMecyl6oc6uKzRfbw==
-
-"d3-array@2 - 3", "d3-array@2.10.0 - 3", "d3-array@2.5.0 - 3", d3-array@3, d3-array@^3.2.0:
- version "3.2.4"
- resolved "https://npm.shopee.io/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5"
- integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==
- dependencies:
- internmap "1 - 2"
-
-d3-axis@3:
- version "3.0.0"
- resolved "https://npm.shopee.io/d3-axis/-/d3-axis-3.0.0.tgz#c42a4a13e8131d637b745fc2973824cfeaf93322"
- integrity sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==
-
-d3-brush@3:
- version "3.0.0"
- resolved "https://npm.shopee.io/d3-brush/-/d3-brush-3.0.0.tgz#6f767c4ed8dcb79de7ede3e1c0f89e63ef64d31c"
- integrity sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==
- dependencies:
- d3-dispatch "1 - 3"
- d3-drag "2 - 3"
- d3-interpolate "1 - 3"
- d3-selection "3"
- d3-transition "3"
-
-d3-chord@3:
- version "3.0.1"
- resolved "https://npm.shopee.io/d3-chord/-/d3-chord-3.0.1.tgz#d156d61f485fce8327e6abf339cb41d8cbba6966"
- integrity sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==
- dependencies:
- d3-path "1 - 3"
-
-"d3-color@1 - 3", d3-color@3:
- version "3.1.0"
- resolved "https://npm.shopee.io/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
- integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
-
-d3-contour@4:
- version "4.0.2"
- resolved "https://npm.shopee.io/d3-contour/-/d3-contour-4.0.2.tgz#bb92063bc8c5663acb2422f99c73cbb6c6ae3bcc"
- integrity sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==
- dependencies:
- d3-array "^3.2.0"
-
-d3-delaunay@6:
- version "6.0.4"
- resolved "https://npm.shopee.io/d3-delaunay/-/d3-delaunay-6.0.4.tgz#98169038733a0a5babbeda55054f795bb9e4a58b"
- integrity sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==
- dependencies:
- delaunator "5"
-
-"d3-dispatch@1 - 3", d3-dispatch@3:
- version "3.0.1"
- resolved "https://npm.shopee.io/d3-dispatch/-/d3-dispatch-3.0.1.tgz#5fc75284e9c2375c36c839411a0cf550cbfc4d5e"
- integrity sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==
-
-"d3-drag@2 - 3", d3-drag@3:
- version "3.0.0"
- resolved "https://npm.shopee.io/d3-drag/-/d3-drag-3.0.0.tgz#994aae9cd23c719f53b5e10e3a0a6108c69607ba"
- integrity sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==
- dependencies:
- d3-dispatch "1 - 3"
- d3-selection "3"
-
-"d3-dsv@1 - 3", d3-dsv@3:
- version "3.0.1"
- resolved "https://npm.shopee.io/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73"
- integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==
- dependencies:
- commander "7"
- iconv-lite "0.6"
- rw "1"
-
-"d3-ease@1 - 3", d3-ease@3:
- version "3.0.1"
- resolved "https://npm.shopee.io/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
- integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
-
-d3-fetch@3:
- version "3.0.1"
- resolved "https://npm.shopee.io/d3-fetch/-/d3-fetch-3.0.1.tgz#83141bff9856a0edb5e38de89cdcfe63d0a60a22"
- integrity sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==
- dependencies:
- d3-dsv "1 - 3"
-
-d3-force@3:
- version "3.0.0"
- resolved "https://npm.shopee.io/d3-force/-/d3-force-3.0.0.tgz#3e2ba1a61e70888fe3d9194e30d6d14eece155c4"
- integrity sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==
- dependencies:
- d3-dispatch "1 - 3"
- d3-quadtree "1 - 3"
- d3-timer "1 - 3"
-
-"d3-format@1 - 3", d3-format@3:
- version "3.1.0"
- resolved "https://npm.shopee.io/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641"
- integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==
-
-d3-geo@3:
- version "3.1.1"
- resolved "https://npm.shopee.io/d3-geo/-/d3-geo-3.1.1.tgz#6027cf51246f9b2ebd64f99e01dc7c3364033a4d"
- integrity sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==
- dependencies:
- d3-array "2.5.0 - 3"
-
-d3-hierarchy@3:
- version "3.1.2"
- resolved "https://npm.shopee.io/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz#b01cd42c1eed3d46db77a5966cf726f8c09160c6"
- integrity sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==
-
-"d3-interpolate@1 - 3", "d3-interpolate@1.2.0 - 3", d3-interpolate@3:
- version "3.0.1"
- resolved "https://npm.shopee.io/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
- integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
- dependencies:
- d3-color "1 - 3"
-
-"d3-path@1 - 3", d3-path@3, d3-path@^3.1.0:
- version "3.1.0"
- resolved "https://npm.shopee.io/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526"
- integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==
-
-d3-polygon@3:
- version "3.0.1"
- resolved "https://npm.shopee.io/d3-polygon/-/d3-polygon-3.0.1.tgz#0b45d3dd1c48a29c8e057e6135693ec80bf16398"
- integrity sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==
-
-"d3-quadtree@1 - 3", d3-quadtree@3:
- version "3.0.1"
- resolved "https://npm.shopee.io/d3-quadtree/-/d3-quadtree-3.0.1.tgz#6dca3e8be2b393c9a9d514dabbd80a92deef1a4f"
- integrity sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==
-
-d3-random@3:
- version "3.0.1"
- resolved "https://npm.shopee.io/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4"
- integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==
-
-d3-scale-chromatic@3:
- version "3.1.0"
- resolved "https://npm.shopee.io/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz#34c39da298b23c20e02f1a4b239bd0f22e7f1314"
- integrity sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==
- dependencies:
- d3-color "1 - 3"
- d3-interpolate "1 - 3"
-
-d3-scale@4:
- version "4.0.2"
- resolved "https://npm.shopee.io/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396"
- integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==
- dependencies:
- d3-array "2.10.0 - 3"
- d3-format "1 - 3"
- d3-interpolate "1.2.0 - 3"
- d3-time "2.1.1 - 3"
- d3-time-format "2 - 4"
-
-"d3-selection@2 - 3", d3-selection@3:
- version "3.0.0"
- resolved "https://npm.shopee.io/d3-selection/-/d3-selection-3.0.0.tgz#c25338207efa72cc5b9bd1458a1a41901f1e1b31"
- integrity sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==
-
-d3-shape@3:
- version "3.2.0"
- resolved "https://npm.shopee.io/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5"
- integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==
- dependencies:
- d3-path "^3.1.0"
-
-"d3-time-format@2 - 4", d3-time-format@4:
- version "4.1.0"
- resolved "https://npm.shopee.io/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a"
- integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==
- dependencies:
- d3-time "1 - 3"
-
-"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@3:
- version "3.1.0"
- resolved "https://npm.shopee.io/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7"
- integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==
- dependencies:
- d3-array "2 - 3"
-
-"d3-timer@1 - 3", d3-timer@3:
- version "3.0.1"
- resolved "https://npm.shopee.io/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
- integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
-
-"d3-transition@2 - 3", d3-transition@3:
- version "3.0.1"
- resolved "https://npm.shopee.io/d3-transition/-/d3-transition-3.0.1.tgz#6869fdde1448868077fdd5989200cb61b2a1645f"
- integrity sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==
- dependencies:
- d3-color "1 - 3"
- d3-dispatch "1 - 3"
- d3-ease "1 - 3"
- d3-interpolate "1 - 3"
- d3-timer "1 - 3"
-
-d3-zoom@3:
- version "3.0.0"
- resolved "https://npm.shopee.io/d3-zoom/-/d3-zoom-3.0.0.tgz#d13f4165c73217ffeaa54295cd6969b3e7aee8f3"
- integrity sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==
- dependencies:
- d3-dispatch "1 - 3"
- d3-drag "2 - 3"
- d3-interpolate "1 - 3"
- d3-selection "2 - 3"
- d3-transition "2 - 3"
-
-d3@^7.4.0, d3@^7.8.2:
- version "7.9.0"
- resolved "https://npm.shopee.io/d3/-/d3-7.9.0.tgz#579e7acb3d749caf8860bd1741ae8d371070cd5d"
- integrity sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==
- dependencies:
- d3-array "3"
- d3-axis "3"
- d3-brush "3"
- d3-chord "3"
- d3-color "3"
- d3-contour "4"
- d3-delaunay "6"
- d3-dispatch "3"
- d3-drag "3"
- d3-dsv "3"
- d3-ease "3"
- d3-fetch "3"
- d3-force "3"
- d3-format "3"
- d3-geo "3"
- d3-hierarchy "3"
- d3-interpolate "3"
- d3-path "3"
- d3-polygon "3"
- d3-quadtree "3"
- d3-random "3"
- d3-scale "4"
- d3-scale-chromatic "3"
- d3-selection "3"
- d3-shape "3"
- d3-time "3"
- d3-time-format "4"
- d3-timer "3"
- d3-transition "3"
- d3-zoom "3"
-
-dagre-d3-es@7.0.9:
- version "7.0.9"
- resolved "https://npm.shopee.io/dagre-d3-es/-/dagre-d3-es-7.0.9.tgz#aca12fccd9d09955a4430029ba72ee6934542a8d"
- integrity sha512-rYR4QfVmy+sR44IBDvVtcAmOReGBvRCWDpO2QjYwqgh9yijw6eSHBqaPG/LIOEy7aBsniLvtMW6pg19qJhq60w==
- dependencies:
- d3 "^7.8.2"
- lodash-es "^4.17.21"
-
-damerau-levenshtein@^1.0.7:
- version "1.0.8"
- resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
- integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
-
-data-urls@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b"
- integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==
- dependencies:
- abab "^2.0.3"
- whatwg-mimetype "^2.3.0"
- whatwg-url "^8.0.0"
-
-dayjs@^1.11.7:
- version "1.11.13"
- resolved "https://npm.shopee.io/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
- integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
-
-debug@2.6.9, debug@^2.6.0, debug@^2.6.9:
- version "2.6.9"
- resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
- integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
- dependencies:
- ms "2.0.0"
-
-debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
- version "4.3.4"
- resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
- integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
- dependencies:
- ms "2.1.2"
-
-debug@^3.2.6, debug@^3.2.7:
- version "3.2.7"
- resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
- integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
- dependencies:
- ms "^2.1.1"
-
-decimal.js@^10.2.1:
- version "10.3.1"
- resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
- integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
-
-decode-named-character-reference@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e"
- integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==
- dependencies:
- character-entities "^2.0.0"
-
-dedent@^0.7.0:
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
- integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==
-
-deep-is@^0.1.3, deep-is@~0.1.3:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
- integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
-
-deepmerge@^4.2.2:
- version "4.2.2"
- resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
- integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==
-
-default-gateway@^6.0.3:
- version "6.0.3"
- resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71"
- integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==
- dependencies:
- execa "^5.0.0"
-
-define-lazy-prop@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
- integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==
-
-define-properties@^1.1.3, define-properties@^1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1"
- integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==
- dependencies:
- has-property-descriptors "^1.0.0"
- object-keys "^1.1.1"
-
-defined@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
- integrity sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==
-
-delaunator@5:
- version "5.0.1"
- resolved "https://npm.shopee.io/delaunator/-/delaunator-5.0.1.tgz#39032b08053923e924d6094fe2cde1a99cc51278"
- integrity sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==
- dependencies:
- robust-predicates "^3.0.2"
-
-delayed-stream@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
- integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
-
-delegate@^3.1.2:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
- integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
-
-depd@2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
- integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
-
-depd@~1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
- integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==
-
-dequal@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d"
- integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug==
-
-destroy@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
- integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
-
-detect-newline@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
- integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
-
-detect-node@^2.0.4:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
- integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
-
-detect-port-alt@^1.1.6:
- version "1.1.6"
- resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275"
- integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q==
- dependencies:
- address "^1.0.1"
- debug "^2.6.0"
-
-detective@^5.2.0:
- version "5.2.1"
- resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034"
- integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==
- dependencies:
- acorn-node "^1.8.2"
- defined "^1.0.0"
- minimist "^1.2.6"
-
-didyoumean@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
- integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==
-
-diff-sequences@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327"
- integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==
-
-diff@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40"
- integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==
-
-dir-glob@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
- integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
- dependencies:
- path-type "^4.0.0"
-
-dlv@^1.1.3:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79"
- integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==
-
-dns-equal@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
- integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==
-
-dns-packet@^5.2.2:
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.3.1.tgz#eb94413789daec0f0ebe2fcc230bdc9d7c91b43d"
- integrity sha512-spBwIj0TK0Ey3666GwIdWVfUpLyubpU53BTCu8iPn4r4oXd9O14Hjg3EHw3ts2oed77/SeckunUYCyRlSngqHw==
- dependencies:
- "@leichtgewicht/ip-codec" "^2.0.1"
-
-doctrine@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
- integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
- dependencies:
- esutils "^2.0.2"
-
-doctrine@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
- integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
- dependencies:
- esutils "^2.0.2"
-
-dom-converter@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
- integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==
- dependencies:
- utila "~0.4"
-
-dom-serializer@0:
- version "0.2.2"
- resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
- integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
- dependencies:
- domelementtype "^2.0.1"
- entities "^2.0.0"
-
-dom-serializer@^1.0.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
- integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
- dependencies:
- domelementtype "^2.0.1"
- domhandler "^4.2.0"
- entities "^2.0.0"
-
-domelementtype@1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
- integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
-
-domelementtype@^2.0.1, domelementtype@^2.2.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
- integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
-
-domexception@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304"
- integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==
- dependencies:
- webidl-conversions "^5.0.0"
-
-domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1:
- version "4.3.1"
- resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
- integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
- dependencies:
- domelementtype "^2.2.0"
-
-dompurify@2.4.3:
- version "2.4.3"
- resolved "https://npm.shopee.io/dompurify/-/dompurify-2.4.3.tgz#f4133af0e6a50297fc8874e2eaedc13a3c308c03"
- integrity sha512-q6QaLcakcRjebxjg8/+NP+h0rPfatOgOzc46Fst9VAA3jF2ApfKBNKMzdP4DYTqtUMXSCd5pRS/8Po/OmoCHZQ==
-
-domutils@^1.7.0:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
- integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==
- dependencies:
- dom-serializer "0"
- domelementtype "1"
-
-domutils@^2.5.2, domutils@^2.8.0:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
- integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
- dependencies:
- dom-serializer "^1.0.1"
- domelementtype "^2.2.0"
- domhandler "^4.2.0"
-
-dot-case@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
- integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
- dependencies:
- no-case "^3.0.4"
- tslib "^2.0.3"
-
-dotenv-expand@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
- integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==
-
-dotenv@^10.0.0:
- version "10.0.0"
- resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
- integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
-
-duplexer@^0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6"
- integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==
-
-ee-first@1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
- integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
-
-ejs@^3.1.6:
- version "3.1.8"
- resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.8.tgz#758d32910c78047585c7ef1f92f9ee041c1c190b"
- integrity sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==
- dependencies:
- jake "^10.8.5"
-
-electron-to-chromium@^1.4.118:
- version "1.4.144"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.144.tgz#9a5d1f41452ecc65b686d529ae919248da44f406"
- integrity sha512-R3RV3rU1xWwFJlSClVWDvARaOk6VUO/FubHLodIASDB3Mc2dzuWvNdfOgH9bwHUTqT79u92qw60NWfwUdzAqdg==
-
-elkjs@^0.8.2:
- version "0.8.2"
- resolved "https://npm.shopee.io/elkjs/-/elkjs-0.8.2.tgz#c37763c5a3e24e042e318455e0147c912a7c248e"
- integrity sha512-L6uRgvZTH+4OF5NE/MBbzQx/WYpru1xCBE9respNj6qznEewGUIfhzmm7horWWxbNO2M0WckQypGctR8lH79xQ==
-
-emittery@^0.10.2:
- version "0.10.2"
- resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.10.2.tgz#902eec8aedb8c41938c46e9385e9db7e03182933"
- integrity sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==
-
-emittery@^0.8.1:
- version "0.8.1"
- resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860"
- integrity sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==
-
-emoji-regex@^10.0.0:
- version "10.1.0"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.1.0.tgz#d50e383743c0f7a5945c47087295afc112e3cf66"
- integrity sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg==
-
-emoji-regex@^8.0.0:
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
- integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
-
-emoji-regex@^9.2.2:
- version "9.2.2"
- resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72"
- integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
-
-emojis-list@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
- integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
-
-emoticon@^4.0.0:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/emoticon/-/emoticon-4.0.1.tgz#2d2bbbf231ce3a5909e185bbb64a9da703a1e749"
- integrity sha512-dqx7eA9YaqyvYtUhJwT4rC1HIp82j5ybS1/vQ42ur+jBe17dJMwZE4+gvL1XadSFfxaPFFGt3Xsw+Y8akThDlw==
-
-encodeurl@~1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
- integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
-
-enhanced-resolve@^5.9.3:
- version "5.9.3"
- resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88"
- integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==
- dependencies:
- graceful-fs "^4.2.4"
- tapable "^2.2.0"
-
-entities@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
- integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
-
-errno@^0.1.1:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
- integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
- dependencies:
- prr "~1.0.1"
-
-error-ex@^1.3.1:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
- integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
- dependencies:
- is-arrayish "^0.2.1"
-
-error-stack-parser@^2.0.6:
- version "2.0.7"
- resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.7.tgz#b0c6e2ce27d0495cf78ad98715e0cad1219abb57"
- integrity sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA==
- dependencies:
- stackframe "^1.1.1"
-
-es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.1:
- version "1.20.1"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814"
- integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==
- dependencies:
- call-bind "^1.0.2"
- es-to-primitive "^1.2.1"
- function-bind "^1.1.1"
- function.prototype.name "^1.1.5"
- get-intrinsic "^1.1.1"
- get-symbol-description "^1.0.0"
- has "^1.0.3"
- has-property-descriptors "^1.0.0"
- has-symbols "^1.0.3"
- internal-slot "^1.0.3"
- is-callable "^1.2.4"
- is-negative-zero "^2.0.2"
- is-regex "^1.1.4"
- is-shared-array-buffer "^1.0.2"
- is-string "^1.0.7"
- is-weakref "^1.0.2"
- object-inspect "^1.12.0"
- object-keys "^1.1.1"
- object.assign "^4.1.2"
- regexp.prototype.flags "^1.4.3"
- string.prototype.trimend "^1.0.5"
- string.prototype.trimstart "^1.0.5"
- unbox-primitive "^1.0.2"
-
-es-array-method-boxes-properly@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e"
- integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==
-
-es-module-lexer@^0.9.0:
- version "0.9.3"
- resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
- integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
-
-es-shim-unscopables@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241"
- integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==
- dependencies:
- has "^1.0.3"
-
-es-to-primitive@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
- integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
- dependencies:
- is-callable "^1.1.4"
- is-date-object "^1.0.1"
- is-symbol "^1.0.2"
-
-escalade@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
- integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
-
-escape-html@~1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
- integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
-
-escape-string-regexp@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
- integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
-
-escape-string-regexp@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
- integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
-
-escape-string-regexp@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
- integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
-
-escape-string-regexp@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
- integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
-
-escodegen@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd"
- integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==
- dependencies:
- esprima "^4.0.1"
- estraverse "^5.2.0"
- esutils "^2.0.2"
- optionator "^0.8.1"
- optionalDependencies:
- source-map "~0.6.1"
-
-eslint-config-react-app@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz#73ba3929978001c5c86274c017ea57eb5fa644b4"
- integrity sha512-K6rNzvkIeHaTd8m/QEh1Zko0KI7BACWkkneSs6s9cKZC/J27X3eZR6Upt1jkmZ/4FK+XUOPPxMEN7+lbUXfSlA==
- dependencies:
- "@babel/core" "^7.16.0"
- "@babel/eslint-parser" "^7.16.3"
- "@rushstack/eslint-patch" "^1.1.0"
- "@typescript-eslint/eslint-plugin" "^5.5.0"
- "@typescript-eslint/parser" "^5.5.0"
- babel-preset-react-app "^10.0.1"
- confusing-browser-globals "^1.0.11"
- eslint-plugin-flowtype "^8.0.3"
- eslint-plugin-import "^2.25.3"
- eslint-plugin-jest "^25.3.0"
- eslint-plugin-jsx-a11y "^6.5.1"
- eslint-plugin-react "^7.27.1"
- eslint-plugin-react-hooks "^4.3.0"
- eslint-plugin-testing-library "^5.0.1"
-
-eslint-import-resolver-node@^0.3.6:
- version "0.3.6"
- resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd"
- integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==
- dependencies:
- debug "^3.2.7"
- resolve "^1.20.0"
-
-eslint-module-utils@^2.7.3:
- version "2.7.3"
- resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee"
- integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==
- dependencies:
- debug "^3.2.7"
- find-up "^2.1.0"
-
-eslint-plugin-flowtype@^8.0.3:
- version "8.0.3"
- resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz#e1557e37118f24734aa3122e7536a038d34a4912"
- integrity sha512-dX8l6qUL6O+fYPtpNRideCFSpmWOUVx5QcaGLVqe/vlDiBSe4vYljDWDETwnyFzpl7By/WVIu6rcrniCgH9BqQ==
- dependencies:
- lodash "^4.17.21"
- string-natural-compare "^3.0.1"
-
-eslint-plugin-import@^2.25.3:
- version "2.26.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b"
- integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==
- dependencies:
- array-includes "^3.1.4"
- array.prototype.flat "^1.2.5"
- debug "^2.6.9"
- doctrine "^2.1.0"
- eslint-import-resolver-node "^0.3.6"
- eslint-module-utils "^2.7.3"
- has "^1.0.3"
- is-core-module "^2.8.1"
- is-glob "^4.0.3"
- minimatch "^3.1.2"
- object.values "^1.1.5"
- resolve "^1.22.0"
- tsconfig-paths "^3.14.1"
-
-eslint-plugin-jest@^25.3.0:
- version "25.7.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz#ff4ac97520b53a96187bad9c9814e7d00de09a6a"
- integrity sha512-PWLUEXeeF7C9QGKqvdSbzLOiLTx+bno7/HC9eefePfEb257QFHg7ye3dh80AZVkaa/RQsBB1Q/ORQvg2X7F0NQ==
- dependencies:
- "@typescript-eslint/experimental-utils" "^5.0.0"
-
-eslint-plugin-jsx-a11y@^6.5.1:
- version "6.5.1"
- resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz#cdbf2df901040ca140b6ec14715c988889c2a6d8"
- integrity sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g==
- dependencies:
- "@babel/runtime" "^7.16.3"
- aria-query "^4.2.2"
- array-includes "^3.1.4"
- ast-types-flow "^0.0.7"
- axe-core "^4.3.5"
- axobject-query "^2.2.0"
- damerau-levenshtein "^1.0.7"
- emoji-regex "^9.2.2"
- has "^1.0.3"
- jsx-ast-utils "^3.2.1"
- language-tags "^1.0.5"
- minimatch "^3.0.4"
-
-eslint-plugin-react-hooks@^4.3.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.5.0.tgz#5f762dfedf8b2cf431c689f533c9d3fa5dcf25ad"
- integrity sha512-8k1gRt7D7h03kd+SAAlzXkQwWK22BnK6GKZG+FJA6BAGy22CFvl8kCIXKpVux0cCxMWDQUPqSok0LKaZ0aOcCw==
-
-eslint-plugin-react@^7.27.1:
- version "7.30.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.30.0.tgz#8e7b1b2934b8426ac067a0febade1b13bd7064e3"
- integrity sha512-RgwH7hjW48BleKsYyHK5vUAvxtE9SMPDKmcPRQgtRCYaZA0XQPt5FSkrU3nhz5ifzMZcA8opwmRJ2cmOO8tr5A==
- dependencies:
- array-includes "^3.1.5"
- array.prototype.flatmap "^1.3.0"
- doctrine "^2.1.0"
- estraverse "^5.3.0"
- jsx-ast-utils "^2.4.1 || ^3.0.0"
- minimatch "^3.1.2"
- object.entries "^1.1.5"
- object.fromentries "^2.0.5"
- object.hasown "^1.1.1"
- object.values "^1.1.5"
- prop-types "^15.8.1"
- resolve "^2.0.0-next.3"
- semver "^6.3.0"
- string.prototype.matchall "^4.0.7"
-
-eslint-plugin-testing-library@^5.0.1:
- version "5.5.1"
- resolved "https://registry.yarnpkg.com/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.5.1.tgz#6fe602f9082a421b471bbae8aed692e26fe981b3"
- integrity sha512-plLEkkbAKBjPxsLj7x4jNapcHAg2ernkQlKKrN2I8NrQwPISZHyCUNvg5Hv3EDqOQReToQb5bnqXYbkijJPE/g==
- dependencies:
- "@typescript-eslint/utils" "^5.13.0"
-
-eslint-scope@5.1.1, eslint-scope@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
- integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
- dependencies:
- esrecurse "^4.3.0"
- estraverse "^4.1.1"
-
-eslint-scope@^7.1.1:
- version "7.1.1"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
- integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
- dependencies:
- esrecurse "^4.3.0"
- estraverse "^5.2.0"
-
-eslint-utils@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
- integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
- dependencies:
- eslint-visitor-keys "^2.0.0"
-
-eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
- integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
-
-eslint-visitor-keys@^3.3.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
- integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
-
-eslint-webpack-plugin@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/eslint-webpack-plugin/-/eslint-webpack-plugin-3.1.1.tgz#83dad2395e5f572d6f4d919eedaa9cf902890fcb"
- integrity sha512-xSucskTN9tOkfW7so4EaiFIkulWLXwCB/15H917lR6pTv0Zot6/fetFucmENRb7J5whVSFKIvwnrnsa78SG2yg==
- dependencies:
- "@types/eslint" "^7.28.2"
- jest-worker "^27.3.1"
- micromatch "^4.0.4"
- normalize-path "^3.0.0"
- schema-utils "^3.1.1"
-
-eslint@^8.3.0:
- version "8.16.0"
- resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.16.0.tgz#6d936e2d524599f2a86c708483b4c372c5d3bbae"
- integrity sha512-MBndsoXY/PeVTDJeWsYj7kLZ5hQpJOfMYLsF6LicLHQWbRDG19lK5jOix4DPl8yY4SUFcE3txy86OzFLWT+yoA==
- dependencies:
- "@eslint/eslintrc" "^1.3.0"
- "@humanwhocodes/config-array" "^0.9.2"
- ajv "^6.10.0"
- chalk "^4.0.0"
- cross-spawn "^7.0.2"
- debug "^4.3.2"
- doctrine "^3.0.0"
- escape-string-regexp "^4.0.0"
- eslint-scope "^7.1.1"
- eslint-utils "^3.0.0"
- eslint-visitor-keys "^3.3.0"
- espree "^9.3.2"
- esquery "^1.4.0"
- esutils "^2.0.2"
- fast-deep-equal "^3.1.3"
- file-entry-cache "^6.0.1"
- functional-red-black-tree "^1.0.1"
- glob-parent "^6.0.1"
- globals "^13.15.0"
- ignore "^5.2.0"
- import-fresh "^3.0.0"
- imurmurhash "^0.1.4"
- is-glob "^4.0.0"
- js-yaml "^4.1.0"
- json-stable-stringify-without-jsonify "^1.0.1"
- levn "^0.4.1"
- lodash.merge "^4.6.2"
- minimatch "^3.1.2"
- natural-compare "^1.4.0"
- optionator "^0.9.1"
- regexpp "^3.2.0"
- strip-ansi "^6.0.1"
- strip-json-comments "^3.1.0"
- text-table "^0.2.0"
- v8-compile-cache "^2.0.3"
-
-espree@^9.3.2:
- version "9.3.2"
- resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596"
- integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==
- dependencies:
- acorn "^8.7.1"
- acorn-jsx "^5.3.2"
- eslint-visitor-keys "^3.3.0"
-
-esprima@^4.0.0, esprima@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
- integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
-
-esquery@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
- integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
- dependencies:
- estraverse "^5.1.0"
-
-esrecurse@^4.3.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
- integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
- dependencies:
- estraverse "^5.2.0"
-
-estraverse@^4.1.1:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
- integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
-
-estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
- integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
-
-estree-walker@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700"
- integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==
-
-esutils@^2.0.2:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
- integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
-
-etag@~1.8.1:
- version "1.8.1"
- resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
- integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
-
-eventemitter3@^4.0.0:
- version "4.0.7"
- resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
- integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
-
-events@^3.2.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
- integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
-
-execa@^5.0.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
- integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
- dependencies:
- cross-spawn "^7.0.3"
- get-stream "^6.0.0"
- human-signals "^2.1.0"
- is-stream "^2.0.0"
- merge-stream "^2.0.0"
- npm-run-path "^4.0.1"
- onetime "^5.1.2"
- signal-exit "^3.0.3"
- strip-final-newline "^2.0.0"
-
-exit@^0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
- integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==
-
-expect@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/expect/-/expect-27.5.1.tgz#83ce59f1e5bdf5f9d2b94b61d2050db48f3fef74"
- integrity sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==
- dependencies:
- "@jest/types" "^27.5.1"
- jest-get-type "^27.5.1"
- jest-matcher-utils "^27.5.1"
- jest-message-util "^27.5.1"
-
-express@^4.17.3:
- version "4.18.1"
- resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf"
- integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==
- dependencies:
- accepts "~1.3.8"
- array-flatten "1.1.1"
- body-parser "1.20.0"
- content-disposition "0.5.4"
- content-type "~1.0.4"
- cookie "0.5.0"
- cookie-signature "1.0.6"
- debug "2.6.9"
- depd "2.0.0"
- encodeurl "~1.0.2"
- escape-html "~1.0.3"
- etag "~1.8.1"
- finalhandler "1.2.0"
- fresh "0.5.2"
- http-errors "2.0.0"
- merge-descriptors "1.0.1"
- methods "~1.1.2"
- on-finished "2.4.1"
- parseurl "~1.3.3"
- path-to-regexp "0.1.7"
- proxy-addr "~2.0.7"
- qs "6.10.3"
- range-parser "~1.2.1"
- safe-buffer "5.2.1"
- send "0.18.0"
- serve-static "1.15.0"
- setprototypeof "1.2.0"
- statuses "2.0.1"
- type-is "~1.6.18"
- utils-merge "1.0.1"
- vary "~1.1.2"
-
-extend@^3.0.0:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
- integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
-
-fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
- integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-
-fast-glob@^3.2.11, fast-glob@^3.2.9:
- version "3.2.11"
- resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
- integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
- dependencies:
- "@nodelib/fs.stat" "^2.0.2"
- "@nodelib/fs.walk" "^1.2.3"
- glob-parent "^5.1.2"
- merge2 "^1.3.0"
- micromatch "^4.0.4"
-
-fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
- integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
-
-fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
- integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
-
-fastq@^1.6.0:
- version "1.13.0"
- resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
- integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
- dependencies:
- reusify "^1.0.4"
-
-faye-websocket@^0.11.3:
- version "0.11.4"
- resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da"
- integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==
- dependencies:
- websocket-driver ">=0.5.1"
-
-fb-watchman@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85"
- integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==
- dependencies:
- bser "2.1.1"
-
-file-entry-cache@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
- integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
- dependencies:
- flat-cache "^3.0.4"
-
-file-loader@^6.2.0:
- version "6.2.0"
- resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d"
- integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==
- dependencies:
- loader-utils "^2.0.0"
- schema-utils "^3.0.0"
-
-filelist@^1.0.1:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
- integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==
- dependencies:
- minimatch "^5.0.1"
-
-filesize@^8.0.6:
- version "8.0.7"
- resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8"
- integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==
-
-fill-range@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
- integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
- dependencies:
- to-regex-range "^5.0.1"
-
-finalhandler@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32"
- integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==
- dependencies:
- debug "2.6.9"
- encodeurl "~1.0.2"
- escape-html "~1.0.3"
- on-finished "2.4.1"
- parseurl "~1.3.3"
- statuses "2.0.1"
- unpipe "~1.0.0"
-
-find-cache-dir@^3.3.1:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b"
- integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==
- dependencies:
- commondir "^1.0.1"
- make-dir "^3.0.2"
- pkg-dir "^4.1.0"
-
-find-root@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
- integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
-
-find-up@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
- integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==
- dependencies:
- locate-path "^2.0.0"
-
-find-up@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73"
- integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==
- dependencies:
- locate-path "^3.0.0"
-
-find-up@^4.0.0, find-up@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
- integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
- dependencies:
- locate-path "^5.0.0"
- path-exists "^4.0.0"
-
-find-up@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
- integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
- dependencies:
- locate-path "^6.0.0"
- path-exists "^4.0.0"
-
-flat-cache@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
- integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
- dependencies:
- flatted "^3.1.0"
- rimraf "^3.0.2"
-
-flatted@^3.1.0:
- version "3.2.5"
- resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
- integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
-
-follow-redirects@^1.0.0:
- version "1.15.1"
- resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
- integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
-
-fork-ts-checker-webpack-plugin@^6.5.0:
- version "6.5.2"
- resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz#4f67183f2f9eb8ba7df7177ce3cf3e75cdafb340"
- integrity sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==
- dependencies:
- "@babel/code-frame" "^7.8.3"
- "@types/json-schema" "^7.0.5"
- chalk "^4.1.0"
- chokidar "^3.4.2"
- cosmiconfig "^6.0.0"
- deepmerge "^4.2.2"
- fs-extra "^9.0.0"
- glob "^7.1.6"
- memfs "^3.1.2"
- minimatch "^3.0.4"
- schema-utils "2.7.0"
- semver "^7.3.2"
- tapable "^1.0.0"
-
-form-data@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
- integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
- dependencies:
- asynckit "^0.4.0"
- combined-stream "^1.0.8"
- mime-types "^2.1.12"
-
-forwarded@0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
- integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
-
-fraction.js@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
- integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==
-
-fresh@0.5.2:
- version "0.5.2"
- resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
- integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
-
-fs-extra@^10.0.0:
- version "10.1.0"
- resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
- integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
- dependencies:
- graceful-fs "^4.2.0"
- jsonfile "^6.0.1"
- universalify "^2.0.0"
-
-fs-extra@^8.0.1:
- version "8.1.0"
- resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
- integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
- dependencies:
- graceful-fs "^4.2.0"
- jsonfile "^4.0.0"
- universalify "^0.1.0"
-
-fs-extra@^9.0.0, fs-extra@^9.0.1:
- version "9.1.0"
- resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
- integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
- dependencies:
- at-least-node "^1.0.0"
- graceful-fs "^4.2.0"
- jsonfile "^6.0.1"
- universalify "^2.0.0"
-
-fs-monkey@1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.3.tgz#ae3ac92d53bb328efe0e9a1d9541f6ad8d48e2d3"
- integrity sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==
-
-fs.realpath@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
- integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
-
-fsevents@^2.3.2, fsevents@~2.3.2:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
- integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
-
-function-bind@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
- integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
-
-function.prototype.name@^1.1.5:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
- integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
- es-abstract "^1.19.0"
- functions-have-names "^1.2.2"
-
-functional-red-black-tree@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
- integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==
-
-functions-have-names@^1.2.2:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
- integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
-
-gensync@^1.0.0-beta.2:
- version "1.0.0-beta.2"
- resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
- integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
-
-get-caller-file@^2.0.5:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
- integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
-
-get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
- integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
- dependencies:
- function-bind "^1.1.1"
- has "^1.0.3"
- has-symbols "^1.0.1"
-
-get-own-enumerable-property-symbols@^3.0.0:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
- integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==
-
-get-package-type@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
- integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
-
-get-stream@^6.0.0:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
- integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
-
-get-symbol-description@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6"
- integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==
- dependencies:
- call-bind "^1.0.2"
- get-intrinsic "^1.1.1"
-
-glob-parent@^5.1.2, glob-parent@~5.1.2:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
- integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
- dependencies:
- is-glob "^4.0.1"
-
-glob-parent@^6.0.1, glob-parent@^6.0.2:
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
- integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
- dependencies:
- is-glob "^4.0.3"
-
-glob-to-regexp@^0.4.1:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
- integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
-
-glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
- version "7.2.3"
- resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
- integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.1.1"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-global-modules@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
- integrity sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==
- dependencies:
- global-prefix "^3.0.0"
-
-global-prefix@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97"
- integrity sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
- dependencies:
- ini "^1.3.5"
- kind-of "^6.0.2"
- which "^1.3.1"
-
-globals@^11.1.0:
- version "11.12.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
- integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
-
-globals@^13.15.0:
- version "13.15.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac"
- integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==
- dependencies:
- type-fest "^0.20.2"
-
-globby@^11.0.4, globby@^11.1.0:
- version "11.1.0"
- resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
- integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
- dependencies:
- array-union "^2.1.0"
- dir-glob "^3.0.1"
- fast-glob "^3.2.9"
- ignore "^5.2.0"
- merge2 "^1.4.1"
- slash "^3.0.0"
-
-good-listener@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
- integrity sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==
- dependencies:
- delegate "^3.1.2"
-
-graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9:
- version "4.2.10"
- resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
- integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
-
-gzip-size@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462"
- integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==
- dependencies:
- duplexer "^0.1.2"
-
-handle-thing@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e"
- integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==
-
-harmony-reflect@^1.4.6:
- version "1.6.2"
- resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.2.tgz#31ecbd32e648a34d030d86adb67d4d47547fe710"
- integrity sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==
-
-has-bigints@^1.0.1, has-bigints@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
- integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
-
-has-flag@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
- integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
-
-has-flag@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
- integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
-
-has-property-descriptors@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
- integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
- dependencies:
- get-intrinsic "^1.1.1"
-
-has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
- integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
-
-has-tostringtag@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
- integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==
- dependencies:
- has-symbols "^1.0.2"
-
-has@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
- integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
- dependencies:
- function-bind "^1.1.1"
-
-hast-util-parse-selector@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-3.1.0.tgz#a519e27e8b61bd5a98fad494ed06131ce68d9c3f"
- integrity sha512-AyjlI2pTAZEOeu7GeBPZhROx0RHBnydkQIXlhnFzDi0qfXTmGUWoCYZtomHbrdrheV4VFUlPcfJ6LMF5T6sQzg==
- dependencies:
- "@types/hast" "^2.0.0"
-
-hastscript@^7.0.0:
- version "7.0.2"
- resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-7.0.2.tgz#d811fc040817d91923448a28156463b2e40d590a"
- integrity sha512-uA8ooUY4ipaBvKcMuPehTAB/YfFLSSzCwFSwT6ltJbocFUKH/GDHLN+tflq7lSRf9H86uOuxOFkh1KgIy3Gg2g==
- dependencies:
- "@types/hast" "^2.0.0"
- comma-separated-tokens "^2.0.0"
- hast-util-parse-selector "^3.0.0"
- property-information "^6.0.0"
- space-separated-tokens "^2.0.0"
-
-he@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
- integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
-
-history@^4.9.0:
- version "4.10.1"
- resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3"
- integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==
- dependencies:
- "@babel/runtime" "^7.1.2"
- loose-envify "^1.2.0"
- resolve-pathname "^3.0.0"
- tiny-invariant "^1.0.2"
- tiny-warning "^1.0.0"
- value-equal "^1.0.1"
-
-hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
- integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
- dependencies:
- react-is "^16.7.0"
-
-hoopy@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d"
- integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==
-
-hpack.js@^2.1.6:
- version "2.1.6"
- resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
- integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==
- dependencies:
- inherits "^2.0.1"
- obuf "^1.0.0"
- readable-stream "^2.0.1"
- wbuf "^1.1.0"
-
-html-encoding-sniffer@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3"
- integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==
- dependencies:
- whatwg-encoding "^1.0.5"
-
-html-entities@^2.1.0, html-entities@^2.3.2:
- version "2.3.3"
- resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46"
- integrity sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==
-
-html-escaper@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
- integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
-
-html-minifier-terser@^6.0.2:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab"
- integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==
- dependencies:
- camel-case "^4.1.2"
- clean-css "^5.2.2"
- commander "^8.3.0"
- he "^1.2.0"
- param-case "^3.0.4"
- relateurl "^0.2.7"
- terser "^5.10.0"
-
-html-webpack-plugin@^5.5.0:
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.0.tgz#c3911936f57681c1f9f4d8b68c158cd9dfe52f50"
- integrity sha512-sy88PC2cRTVxvETRgUHFrL4No3UxvcH8G1NepGhqaTT+GXN2kTamqasot0inS5hXeg1cMbFDt27zzo9p35lZVw==
- dependencies:
- "@types/html-minifier-terser" "^6.0.0"
- html-minifier-terser "^6.0.2"
- lodash "^4.17.21"
- pretty-error "^4.0.0"
- tapable "^2.0.0"
-
-htmlparser2@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
- integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
- dependencies:
- domelementtype "^2.0.1"
- domhandler "^4.0.0"
- domutils "^2.5.2"
- entities "^2.0.0"
-
-http-deceiver@^1.2.7:
- version "1.2.7"
- resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
- integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==
-
-http-errors@2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
- integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==
- dependencies:
- depd "2.0.0"
- inherits "2.0.4"
- setprototypeof "1.2.0"
- statuses "2.0.1"
- toidentifier "1.0.1"
-
-http-errors@~1.6.2:
- version "1.6.3"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d"
- integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==
- dependencies:
- depd "~1.1.2"
- inherits "2.0.3"
- setprototypeof "1.1.0"
- statuses ">= 1.4.0 < 2"
-
-http-parser-js@>=0.5.1:
- version "0.5.6"
- resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.6.tgz#2e02406ab2df8af8a7abfba62e0da01c62b95afd"
- integrity sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==
-
-http-proxy-agent@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a"
- integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==
- dependencies:
- "@tootallnate/once" "1"
- agent-base "6"
- debug "4"
-
-http-proxy-middleware@^2.0.3:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f"
- integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==
- dependencies:
- "@types/http-proxy" "^1.17.8"
- http-proxy "^1.18.1"
- is-glob "^4.0.1"
- is-plain-obj "^3.0.0"
- micromatch "^4.0.2"
-
-http-proxy@^1.18.1:
- version "1.18.1"
- resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
- integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
- dependencies:
- eventemitter3 "^4.0.0"
- follow-redirects "^1.0.0"
- requires-port "^1.0.0"
-
-https-proxy-agent@^5.0.0:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
- integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
- dependencies:
- agent-base "6"
- debug "4"
-
-human-signals@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
- integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
-
-iconv-lite@0.4.24, iconv-lite@^0.4.4:
- version "0.4.24"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
- integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
- dependencies:
- safer-buffer ">= 2.1.2 < 3"
-
-iconv-lite@0.6, iconv-lite@^0.6.3:
- version "0.6.3"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
- integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
- dependencies:
- safer-buffer ">= 2.1.2 < 3.0.0"
-
-icss-utils@^5.0.0, icss-utils@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
- integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
-
-idb@^6.1.4:
- version "6.1.5"
- resolved "https://registry.yarnpkg.com/idb/-/idb-6.1.5.tgz#dbc53e7adf1ac7c59f9b2bf56e00b4ea4fce8c7b"
- integrity sha512-IJtugpKkiVXQn5Y+LteyBCNk1N8xpGV3wWZk9EVtZWH8DYkjBn0bX1XnGP9RkyZF0sAcywa6unHqSWKe7q4LGw==
-
-identity-obj-proxy@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14"
- integrity sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==
- dependencies:
- harmony-reflect "^1.4.6"
-
-ignore@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
- integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
-
-image-size@~0.5.0:
- version "0.5.5"
- resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
- integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==
-
-immer@^9.0.7:
- version "9.0.14"
- resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.14.tgz#e05b83b63999d26382bb71676c9d827831248a48"
- integrity sha512-ubBeqQutOSLIFCUBN03jGeOS6a3DoYlSYwYJTa+gSKEZKU5redJIqkIdZ3JVv/4RZpfcXdAWH5zCNLWPRv2WDw==
-
-import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
- integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
- dependencies:
- parent-module "^1.0.0"
- resolve-from "^4.0.0"
-
-import-local@^3.0.2:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
- integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==
- dependencies:
- pkg-dir "^4.2.0"
- resolve-cwd "^3.0.0"
-
-imurmurhash@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
- integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
-
-inflight@^1.0.4:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
- integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
- dependencies:
- once "^1.3.0"
- wrappy "1"
-
-inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
- integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
-
-inherits@2.0.3:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
- integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==
-
-ini@^1.3.5:
- version "1.3.8"
- resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
- integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
-
-internal-slot@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
- integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==
- dependencies:
- get-intrinsic "^1.1.0"
- has "^1.0.3"
- side-channel "^1.0.4"
-
-"internmap@1 - 2":
- version "2.0.3"
- resolved "https://npm.shopee.io/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009"
- integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==
-
-ipaddr.js@1.9.1:
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
- integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
-
-ipaddr.js@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0"
- integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==
-
-is-alphabetical@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b"
- integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==
-
-is-alphanumerical@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875"
- integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==
- dependencies:
- is-alphabetical "^2.0.0"
- is-decimal "^2.0.0"
-
-is-arrayish@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
- integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
-
-is-bigint@^1.0.1:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
- integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==
- dependencies:
- has-bigints "^1.0.1"
-
-is-binary-path@~2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
- integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
- dependencies:
- binary-extensions "^2.0.0"
-
-is-boolean-object@^1.1.0:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
- integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==
- dependencies:
- call-bind "^1.0.2"
- has-tostringtag "^1.0.0"
-
-is-buffer@^2.0.0:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
- integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
-
-is-callable@^1.1.4, is-callable@^1.2.4:
- version "1.2.4"
- resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
- integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==
-
-is-core-module@^2.2.0, is-core-module@^2.8.1:
- version "2.9.0"
- resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
- integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
- dependencies:
- has "^1.0.3"
-
-is-date-object@^1.0.1:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
- integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
- dependencies:
- has-tostringtag "^1.0.0"
-
-is-decimal@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7"
- integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==
-
-is-docker@^2.0.0, is-docker@^2.1.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
- integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
-
-is-extglob@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
- integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
-
-is-fullwidth-code-point@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
- integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
-
-is-generator-fn@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
- integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
-
-is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
- integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
- dependencies:
- is-extglob "^2.1.1"
-
-is-hexadecimal@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027"
- integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==
-
-is-module@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
- integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==
-
-is-negative-zero@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
- integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
-
-is-number-object@^1.0.4:
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc"
- integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==
- dependencies:
- has-tostringtag "^1.0.0"
-
-is-number@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
- integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
-
-is-obj@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
- integrity sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==
-
-is-plain-obj@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7"
- integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==
-
-is-plain-obj@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.0.0.tgz#06c0999fd7574edf5a906ba5644ad0feb3a84d22"
- integrity sha512-NXRbBtUdBioI73y/HmOhogw/U5msYPC9DAtGkJXeFcFWSFZw0mCUsPxk/snTuJHzNKA8kLBK4rH97RMB1BfCXw==
-
-is-potential-custom-element-name@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
- integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
-
-is-regex@^1.1.4:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
- integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
- dependencies:
- call-bind "^1.0.2"
- has-tostringtag "^1.0.0"
-
-is-regexp@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
- integrity sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==
-
-is-root@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c"
- integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==
-
-is-shared-array-buffer@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
- integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==
- dependencies:
- call-bind "^1.0.2"
-
-is-stream@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
- integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
-
-is-string@^1.0.5, is-string@^1.0.7:
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
- integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
- dependencies:
- has-tostringtag "^1.0.0"
-
-is-symbol@^1.0.2, is-symbol@^1.0.3:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
- integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
- dependencies:
- has-symbols "^1.0.2"
-
-is-typedarray@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
- integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
-
-is-weakref@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
- integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
- dependencies:
- call-bind "^1.0.2"
-
-is-what@^3.14.1:
- version "3.14.1"
- resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1"
- integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==
-
-is-wsl@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
- integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
- dependencies:
- is-docker "^2.0.0"
-
-isarray@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
- integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==
-
-isarray@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
- integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
-
-isexe@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
- integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
-
-istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3"
- integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==
-
-istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f"
- integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==
- dependencies:
- "@babel/core" "^7.12.3"
- "@babel/parser" "^7.14.7"
- "@istanbuljs/schema" "^0.1.2"
- istanbul-lib-coverage "^3.2.0"
- semver "^6.3.0"
-
-istanbul-lib-report@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6"
- integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==
- dependencies:
- istanbul-lib-coverage "^3.0.0"
- make-dir "^3.0.0"
- supports-color "^7.1.0"
-
-istanbul-lib-source-maps@^4.0.0:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551"
- integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==
- dependencies:
- debug "^4.1.1"
- istanbul-lib-coverage "^3.0.0"
- source-map "^0.6.1"
-
-istanbul-reports@^3.1.3:
- version "3.1.4"
- resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.4.tgz#1b6f068ecbc6c331040aab5741991273e609e40c"
- integrity sha512-r1/DshN4KSE7xWEknZLLLLDn5CJybV3nw01VTkp6D5jzLuELlcbudfj/eSQFvrKsJuTVCGnePO7ho82Nw9zzfw==
- dependencies:
- html-escaper "^2.0.0"
- istanbul-lib-report "^3.0.0"
-
-jake@^10.8.5:
- version "10.8.5"
- resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46"
- integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==
- dependencies:
- async "^3.2.3"
- chalk "^4.0.2"
- filelist "^1.0.1"
- minimatch "^3.0.4"
-
-jest-changed-files@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-27.5.1.tgz#a348aed00ec9bf671cc58a66fcbe7c3dfd6a68f5"
- integrity sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==
- dependencies:
- "@jest/types" "^27.5.1"
- execa "^5.0.0"
- throat "^6.0.1"
-
-jest-circus@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-27.5.1.tgz#37a5a4459b7bf4406e53d637b49d22c65d125ecc"
- integrity sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==
- dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- chalk "^4.0.0"
- co "^4.6.0"
- dedent "^0.7.0"
- expect "^27.5.1"
- is-generator-fn "^2.0.0"
- jest-each "^27.5.1"
- jest-matcher-utils "^27.5.1"
- jest-message-util "^27.5.1"
- jest-runtime "^27.5.1"
- jest-snapshot "^27.5.1"
- jest-util "^27.5.1"
- pretty-format "^27.5.1"
- slash "^3.0.0"
- stack-utils "^2.0.3"
- throat "^6.0.1"
-
-jest-cli@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.5.1.tgz#278794a6e6458ea8029547e6c6cbf673bd30b145"
- integrity sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==
- dependencies:
- "@jest/core" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/types" "^27.5.1"
- chalk "^4.0.0"
- exit "^0.1.2"
- graceful-fs "^4.2.9"
- import-local "^3.0.2"
- jest-config "^27.5.1"
- jest-util "^27.5.1"
- jest-validate "^27.5.1"
- prompts "^2.0.1"
- yargs "^16.2.0"
-
-jest-config@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.5.1.tgz#5c387de33dca3f99ad6357ddeccd91bf3a0e4a41"
- integrity sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==
- dependencies:
- "@babel/core" "^7.8.0"
- "@jest/test-sequencer" "^27.5.1"
- "@jest/types" "^27.5.1"
- babel-jest "^27.5.1"
- chalk "^4.0.0"
- ci-info "^3.2.0"
- deepmerge "^4.2.2"
- glob "^7.1.1"
- graceful-fs "^4.2.9"
- jest-circus "^27.5.1"
- jest-environment-jsdom "^27.5.1"
- jest-environment-node "^27.5.1"
- jest-get-type "^27.5.1"
- jest-jasmine2 "^27.5.1"
- jest-regex-util "^27.5.1"
- jest-resolve "^27.5.1"
- jest-runner "^27.5.1"
- jest-util "^27.5.1"
- jest-validate "^27.5.1"
- micromatch "^4.0.4"
- parse-json "^5.2.0"
- pretty-format "^27.5.1"
- slash "^3.0.0"
- strip-json-comments "^3.1.1"
-
-jest-diff@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def"
- integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==
- dependencies:
- chalk "^4.0.0"
- diff-sequences "^27.5.1"
- jest-get-type "^27.5.1"
- pretty-format "^27.5.1"
-
-jest-docblock@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.5.1.tgz#14092f364a42c6108d42c33c8cf30e058e25f6c0"
- integrity sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==
- dependencies:
- detect-newline "^3.0.0"
-
-jest-each@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-27.5.1.tgz#5bc87016f45ed9507fed6e4702a5b468a5b2c44e"
- integrity sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==
- dependencies:
- "@jest/types" "^27.5.1"
- chalk "^4.0.0"
- jest-get-type "^27.5.1"
- jest-util "^27.5.1"
- pretty-format "^27.5.1"
-
-jest-environment-jsdom@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz#ea9ccd1fc610209655a77898f86b2b559516a546"
- integrity sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==
- dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/fake-timers" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- jest-mock "^27.5.1"
- jest-util "^27.5.1"
- jsdom "^16.6.0"
-
-jest-environment-node@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-27.5.1.tgz#dedc2cfe52fab6b8f5714b4808aefa85357a365e"
- integrity sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==
- dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/fake-timers" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- jest-mock "^27.5.1"
- jest-util "^27.5.1"
-
-jest-get-type@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1"
- integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==
-
-jest-haste-map@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.5.1.tgz#9fd8bd7e7b4fa502d9c6164c5640512b4e811e7f"
- integrity sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==
- dependencies:
- "@jest/types" "^27.5.1"
- "@types/graceful-fs" "^4.1.2"
- "@types/node" "*"
- anymatch "^3.0.3"
- fb-watchman "^2.0.0"
- graceful-fs "^4.2.9"
- jest-regex-util "^27.5.1"
- jest-serializer "^27.5.1"
- jest-util "^27.5.1"
- jest-worker "^27.5.1"
- micromatch "^4.0.4"
- walker "^1.0.7"
- optionalDependencies:
- fsevents "^2.3.2"
-
-jest-jasmine2@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz#a037b0034ef49a9f3d71c4375a796f3b230d1ac4"
- integrity sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==
- dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/source-map" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- chalk "^4.0.0"
- co "^4.6.0"
- expect "^27.5.1"
- is-generator-fn "^2.0.0"
- jest-each "^27.5.1"
- jest-matcher-utils "^27.5.1"
- jest-message-util "^27.5.1"
- jest-runtime "^27.5.1"
- jest-snapshot "^27.5.1"
- jest-util "^27.5.1"
- pretty-format "^27.5.1"
- throat "^6.0.1"
-
-jest-leak-detector@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz#6ec9d54c3579dd6e3e66d70e3498adf80fde3fb8"
- integrity sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==
- dependencies:
- jest-get-type "^27.5.1"
- pretty-format "^27.5.1"
-
-jest-matcher-utils@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab"
- integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==
- dependencies:
- chalk "^4.0.0"
- jest-diff "^27.5.1"
- jest-get-type "^27.5.1"
- pretty-format "^27.5.1"
-
-jest-message-util@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-27.5.1.tgz#bdda72806da10d9ed6425e12afff38cd1458b6cf"
- integrity sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==
- dependencies:
- "@babel/code-frame" "^7.12.13"
- "@jest/types" "^27.5.1"
- "@types/stack-utils" "^2.0.0"
- chalk "^4.0.0"
- graceful-fs "^4.2.9"
- micromatch "^4.0.4"
- pretty-format "^27.5.1"
- slash "^3.0.0"
- stack-utils "^2.0.3"
-
-jest-message-util@^28.1.0:
- version "28.1.0"
- resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.0.tgz#7e8f0b9049e948e7b94c2a52731166774ba7d0af"
- integrity sha512-RpA8mpaJ/B2HphDMiDlrAZdDytkmwFqgjDZovM21F35lHGeUeCvYmm6W+sbQ0ydaLpg5bFAUuWG1cjqOl8vqrw==
- dependencies:
- "@babel/code-frame" "^7.12.13"
- "@jest/types" "^28.1.0"
- "@types/stack-utils" "^2.0.0"
- chalk "^4.0.0"
- graceful-fs "^4.2.9"
- micromatch "^4.0.4"
- pretty-format "^28.1.0"
- slash "^3.0.0"
- stack-utils "^2.0.3"
-
-jest-mock@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-27.5.1.tgz#19948336d49ef4d9c52021d34ac7b5f36ff967d6"
- integrity sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==
- dependencies:
- "@jest/types" "^27.5.1"
- "@types/node" "*"
-
-jest-pnp-resolver@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c"
- integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==
-
-jest-regex-util@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95"
- integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==
-
-jest-regex-util@^28.0.0:
- version "28.0.2"
- resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead"
- integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==
-
-jest-resolve-dependencies@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz#d811ecc8305e731cc86dd79741ee98fed06f1da8"
- integrity sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==
- dependencies:
- "@jest/types" "^27.5.1"
- jest-regex-util "^27.5.1"
- jest-snapshot "^27.5.1"
-
-jest-resolve@^27.4.2, jest-resolve@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-27.5.1.tgz#a2f1c5a0796ec18fe9eb1536ac3814c23617b384"
- integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==
- dependencies:
- "@jest/types" "^27.5.1"
- chalk "^4.0.0"
- graceful-fs "^4.2.9"
- jest-haste-map "^27.5.1"
- jest-pnp-resolver "^1.2.2"
- jest-util "^27.5.1"
- jest-validate "^27.5.1"
- resolve "^1.20.0"
- resolve.exports "^1.1.0"
- slash "^3.0.0"
-
-jest-runner@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-27.5.1.tgz#071b27c1fa30d90540805c5645a0ec167c7b62e5"
- integrity sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==
- dependencies:
- "@jest/console" "^27.5.1"
- "@jest/environment" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- chalk "^4.0.0"
- emittery "^0.8.1"
- graceful-fs "^4.2.9"
- jest-docblock "^27.5.1"
- jest-environment-jsdom "^27.5.1"
- jest-environment-node "^27.5.1"
- jest-haste-map "^27.5.1"
- jest-leak-detector "^27.5.1"
- jest-message-util "^27.5.1"
- jest-resolve "^27.5.1"
- jest-runtime "^27.5.1"
- jest-util "^27.5.1"
- jest-worker "^27.5.1"
- source-map-support "^0.5.6"
- throat "^6.0.1"
-
-jest-runtime@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-27.5.1.tgz#4896003d7a334f7e8e4a53ba93fb9bcd3db0a1af"
- integrity sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==
- dependencies:
- "@jest/environment" "^27.5.1"
- "@jest/fake-timers" "^27.5.1"
- "@jest/globals" "^27.5.1"
- "@jest/source-map" "^27.5.1"
- "@jest/test-result" "^27.5.1"
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
- chalk "^4.0.0"
- cjs-module-lexer "^1.0.0"
- collect-v8-coverage "^1.0.0"
- execa "^5.0.0"
- glob "^7.1.3"
- graceful-fs "^4.2.9"
- jest-haste-map "^27.5.1"
- jest-message-util "^27.5.1"
- jest-mock "^27.5.1"
- jest-regex-util "^27.5.1"
- jest-resolve "^27.5.1"
- jest-snapshot "^27.5.1"
- jest-util "^27.5.1"
- slash "^3.0.0"
- strip-bom "^4.0.0"
-
-jest-serializer@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-27.5.1.tgz#81438410a30ea66fd57ff730835123dea1fb1f64"
- integrity sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==
- dependencies:
- "@types/node" "*"
- graceful-fs "^4.2.9"
-
-jest-snapshot@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-27.5.1.tgz#b668d50d23d38054a51b42c4039cab59ae6eb6a1"
- integrity sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==
- dependencies:
- "@babel/core" "^7.7.2"
- "@babel/generator" "^7.7.2"
- "@babel/plugin-syntax-typescript" "^7.7.2"
- "@babel/traverse" "^7.7.2"
- "@babel/types" "^7.0.0"
- "@jest/transform" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/babel__traverse" "^7.0.4"
- "@types/prettier" "^2.1.5"
- babel-preset-current-node-syntax "^1.0.0"
- chalk "^4.0.0"
- expect "^27.5.1"
- graceful-fs "^4.2.9"
- jest-diff "^27.5.1"
- jest-get-type "^27.5.1"
- jest-haste-map "^27.5.1"
- jest-matcher-utils "^27.5.1"
- jest-message-util "^27.5.1"
- jest-util "^27.5.1"
- natural-compare "^1.4.0"
- pretty-format "^27.5.1"
- semver "^7.3.2"
-
-jest-util@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9"
- integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==
- dependencies:
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- chalk "^4.0.0"
- ci-info "^3.2.0"
- graceful-fs "^4.2.9"
- picomatch "^2.2.3"
-
-jest-util@^28.1.0:
- version "28.1.0"
- resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.0.tgz#d54eb83ad77e1dd441408738c5a5043642823be5"
- integrity sha512-qYdCKD77k4Hwkose2YBEqQk7PzUf/NSE+rutzceduFveQREeH6b+89Dc9+wjX9dAwHcgdx4yedGA3FQlU/qCTA==
- dependencies:
- "@jest/types" "^28.1.0"
- "@types/node" "*"
- chalk "^4.0.0"
- ci-info "^3.2.0"
- graceful-fs "^4.2.9"
- picomatch "^2.2.3"
-
-jest-validate@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-27.5.1.tgz#9197d54dc0bdb52260b8db40b46ae668e04df067"
- integrity sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==
- dependencies:
- "@jest/types" "^27.5.1"
- camelcase "^6.2.0"
- chalk "^4.0.0"
- jest-get-type "^27.5.1"
- leven "^3.1.0"
- pretty-format "^27.5.1"
-
-jest-watch-typeahead@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz#b4a6826dfb9c9420da2f7bc900de59dad11266a9"
- integrity sha512-Va5nLSJTN7YFtC2jd+7wsoe1pNe5K4ShLux/E5iHEwlB9AxaxmggY7to9KUqKojhaJw3aXqt5WAb4jGPOolpEw==
- dependencies:
- ansi-escapes "^4.3.1"
- chalk "^4.0.0"
- jest-regex-util "^28.0.0"
- jest-watcher "^28.0.0"
- slash "^4.0.0"
- string-length "^5.0.1"
- strip-ansi "^7.0.1"
-
-jest-watcher@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-27.5.1.tgz#71bd85fb9bde3a2c2ec4dc353437971c43c642a2"
- integrity sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==
- dependencies:
- "@jest/test-result" "^27.5.1"
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- ansi-escapes "^4.2.1"
- chalk "^4.0.0"
- jest-util "^27.5.1"
- string-length "^4.0.1"
-
-jest-watcher@^28.0.0:
- version "28.1.0"
- resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-28.1.0.tgz#aaa7b4164a4e77eeb5f7d7b25ede5e7b4e9c9aaf"
- integrity sha512-tNHMtfLE8Njcr2IRS+5rXYA4BhU90gAOwI9frTGOqd+jX0P/Au/JfRSNqsf5nUTcWdbVYuLxS1KjnzILSoR5hA==
- dependencies:
- "@jest/test-result" "^28.1.0"
- "@jest/types" "^28.1.0"
- "@types/node" "*"
- ansi-escapes "^4.2.1"
- chalk "^4.0.0"
- emittery "^0.10.2"
- jest-util "^28.1.0"
- string-length "^4.0.1"
-
-jest-worker@^26.2.1:
- version "26.6.2"
- resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
- integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
- dependencies:
- "@types/node" "*"
- merge-stream "^2.0.0"
- supports-color "^7.0.0"
-
-jest-worker@^27.0.2, jest-worker@^27.3.1, jest-worker@^27.4.5, jest-worker@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0"
- integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==
- dependencies:
- "@types/node" "*"
- merge-stream "^2.0.0"
- supports-color "^8.0.0"
-
-jest@^27.4.3:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest/-/jest-27.5.1.tgz#dadf33ba70a779be7a6fc33015843b51494f63fc"
- integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==
- dependencies:
- "@jest/core" "^27.5.1"
- import-local "^3.0.2"
- jest-cli "^27.5.1"
-
-"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
- integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
-
-js-yaml@^3.13.1:
- version "3.14.1"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
- integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
- dependencies:
- argparse "^1.0.7"
- esprima "^4.0.0"
-
-js-yaml@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
- integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
- dependencies:
- argparse "^2.0.1"
-
-jsdom@^16.6.0:
- version "16.7.0"
- resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710"
- integrity sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==
- dependencies:
- abab "^2.0.5"
- acorn "^8.2.4"
- acorn-globals "^6.0.0"
- cssom "^0.4.4"
- cssstyle "^2.3.0"
- data-urls "^2.0.0"
- decimal.js "^10.2.1"
- domexception "^2.0.1"
- escodegen "^2.0.0"
- form-data "^3.0.0"
- html-encoding-sniffer "^2.0.1"
- http-proxy-agent "^4.0.1"
- https-proxy-agent "^5.0.0"
- is-potential-custom-element-name "^1.0.1"
- nwsapi "^2.2.0"
- parse5 "6.0.1"
- saxes "^5.0.1"
- symbol-tree "^3.2.4"
- tough-cookie "^4.0.0"
- w3c-hr-time "^1.0.2"
- w3c-xmlserializer "^2.0.0"
- webidl-conversions "^6.1.0"
- whatwg-encoding "^1.0.5"
- whatwg-mimetype "^2.3.0"
- whatwg-url "^8.5.0"
- ws "^7.4.6"
- xml-name-validator "^3.0.0"
-
-jsesc@^2.5.1:
- version "2.5.2"
- resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
- integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
-
-jsesc@~0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
- integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==
-
-json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
- integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
-
-json-schema-traverse@^0.4.1:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
- integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
-
-json-schema-traverse@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
- integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
-
-json-schema@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5"
- integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
-
-json-stable-stringify-without-jsonify@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
- integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
-
-json5@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
- integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
- dependencies:
- minimist "^1.2.0"
-
-json5@^2.1.2, json5@^2.2.0, json5@^2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
- integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
-
-jsonfile@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
- integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
- optionalDependencies:
- graceful-fs "^4.1.6"
-
-jsonfile@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-5.0.0.tgz#e6b718f73da420d612823996fdf14a03f6ff6922"
- integrity sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==
- dependencies:
- universalify "^0.1.2"
- optionalDependencies:
- graceful-fs "^4.1.6"
-
-jsonfile@^6.0.1:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
- integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
- dependencies:
- universalify "^2.0.0"
- optionalDependencies:
- graceful-fs "^4.1.6"
-
-jsonpointer@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.0.tgz#f802669a524ec4805fa7389eadbc9921d5dc8072"
- integrity sha512-PNYZIdMjVIvVgDSYKTT63Y+KZ6IZvGRNNWcxwD+GNnUz1MKPfv30J8ueCjdwcN0nDx2SlshgyB7Oy0epAzVRRg==
-
-"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.2.1:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.0.tgz#e624f259143b9062c92b6413ff92a164c80d3ccb"
- integrity sha512-XzO9luP6L0xkxwhIJMTJQpZo/eeN60K08jHdexfD569AGxeNug6UketeHXEhROoM8aR7EcUoOQmIhcJQjcuq8Q==
- dependencies:
- array-includes "^3.1.4"
- object.assign "^4.1.2"
-
-khroma@^2.0.0:
- version "2.1.0"
- resolved "https://npm.shopee.io/khroma/-/khroma-2.1.0.tgz#45f2ce94ce231a437cf5b63c2e886e6eb42bbbb1"
- integrity sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==
-
-kind-of@^6.0.2:
- version "6.0.3"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
- integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
-
-kleur@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
- integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
-
-kleur@^4.0.3:
- version "4.1.4"
- resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d"
- integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA==
-
-klona@^2.0.4, klona@^2.0.5:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc"
- integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==
-
-language-subtag-registry@~0.3.2:
- version "0.3.21"
- resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a"
- integrity sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg==
-
-language-tags@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a"
- integrity sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ==
- dependencies:
- language-subtag-registry "~0.3.2"
-
-layout-base@^1.0.0:
- version "1.0.2"
- resolved "https://npm.shopee.io/layout-base/-/layout-base-1.0.2.tgz#1291e296883c322a9dd4c5dd82063721b53e26e2"
- integrity sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==
-
-layout-base@^2.0.0:
- version "2.0.1"
- resolved "https://npm.shopee.io/layout-base/-/layout-base-2.0.1.tgz#d0337913586c90f9c2c075292069f5c2da5dd285"
- integrity sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==
-
-less-loader@^11.0.0:
- version "11.0.0"
- resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-11.0.0.tgz#a31b2bc5cdfb62f1c7de9b2d01cd944c22b1a024"
- integrity sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==
- dependencies:
- klona "^2.0.4"
-
-less@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/less/-/less-4.1.2.tgz#6099ee584999750c2624b65f80145f8674e4b4b0"
- integrity sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA==
- dependencies:
- copy-anything "^2.0.1"
- parse-node-version "^1.0.1"
- tslib "^2.3.0"
- optionalDependencies:
- errno "^0.1.1"
- graceful-fs "^4.1.2"
- image-size "~0.5.0"
- make-dir "^2.1.0"
- mime "^1.4.1"
- needle "^2.5.2"
- source-map "~0.6.0"
-
-leven@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
- integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
-
-levn@^0.4.1:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
- integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
- dependencies:
- prelude-ls "^1.2.1"
- type-check "~0.4.0"
-
-levn@~0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
- integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==
- dependencies:
- prelude-ls "~1.1.2"
- type-check "~0.3.2"
-
-lilconfig@^2.0.3, lilconfig@^2.0.5:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25"
- integrity sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==
-
-lines-and-columns@^1.1.6:
- version "1.2.4"
- resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
- integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
-
-loader-runner@^4.2.0:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
- integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
-
-loader-utils@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
- integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
- dependencies:
- big.js "^5.2.2"
- emojis-list "^3.0.0"
- json5 "^2.1.2"
-
-loader-utils@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.0.tgz#bcecc51a7898bee7473d4bc6b845b23af8304d4f"
- integrity sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==
-
-locate-path@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
- integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==
- dependencies:
- p-locate "^2.0.0"
- path-exists "^3.0.0"
-
-locate-path@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
- integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==
- dependencies:
- p-locate "^3.0.0"
- path-exists "^3.0.0"
-
-locate-path@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
- integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
- dependencies:
- p-locate "^4.1.0"
-
-locate-path@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
- integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
- dependencies:
- p-locate "^5.0.0"
-
-lodash-es@^4.17.21:
- version "4.17.21"
- resolved "https://npm.shopee.io/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
- integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
-
-lodash.debounce@^4.0.8:
- version "4.0.8"
- resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
- integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
-
-lodash.flow@^3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a"
- integrity sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==
-
-lodash.memoize@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
- integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
-
-lodash.merge@^4.6.2:
- version "4.6.2"
- resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
- integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
-
-lodash.sortby@^4.7.0:
- version "4.7.0"
- resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
- integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==
-
-lodash.uniq@^4.5.0:
- version "4.5.0"
- resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
- integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
-
-lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
- version "4.17.21"
- resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
- integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
-
-longest-streak@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.0.1.tgz#c97315b7afa0e7d9525db9a5a2953651432bdc5d"
- integrity sha512-cHlYSUpL2s7Fb3394mYxwTYj8niTaNHUCLr0qdiCXQfSjfuA7CKofpX2uSwEfFDQ0EB7JcnMnm+GjbqqoinYYg==
-
-loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
- integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
- dependencies:
- js-tokens "^3.0.0 || ^4.0.0"
-
-lower-case@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
- integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
- dependencies:
- tslib "^2.0.3"
-
-lru-cache@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
- integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
- dependencies:
- yallist "^4.0.0"
-
-magic-string@^0.25.0, magic-string@^0.25.7:
- version "0.25.9"
- resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
- integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
- dependencies:
- sourcemap-codec "^1.4.8"
-
-make-dir@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
- integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
- dependencies:
- pify "^4.0.1"
- semver "^5.6.0"
-
-make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
- integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
- dependencies:
- semver "^6.0.0"
-
-makeerror@1.0.12:
- version "1.0.12"
- resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
- integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==
- dependencies:
- tmpl "1.0.5"
-
-markdown-table@^3.0.0:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.2.tgz#9b59eb2c1b22fe71954a65ff512887065a7bb57c"
- integrity sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==
-
-mdast-util-definitions@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.0.tgz#b6d10ef00a3c4cf191e8d9a5fa58d7f4a366f817"
- integrity sha512-5hcR7FL2EuZ4q6lLMUK5w4lHT2H3vqL9quPvYZ/Ku5iifrirfMHiGdhxdXMUbUkDmz5I+TYMd7nbaxUhbQkfpQ==
- dependencies:
- "@types/mdast" "^3.0.0"
- "@types/unist" "^2.0.0"
- unist-util-visit "^3.0.0"
-
-mdast-util-directive@^2.0.0:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/mdast-util-directive/-/mdast-util-directive-2.2.1.tgz#823d8e67e2aad04166e31c0a43931d3462be77fe"
- integrity sha512-yZRRuaulzc6bM4IOyZfkOrVs+9Sf1BC+rldRXJyl/Ej6S/6ewQQ9jt75HvEoqZZ4m9ealVTHiS4MP2GRUE7INA==
- dependencies:
- "@types/mdast" "^3.0.0"
- "@types/unist" "^2.0.0"
- mdast-util-to-markdown "^1.3.0"
- parse-entities "^4.0.0"
- stringify-entities "^4.0.0"
- unist-util-visit-parents "^5.0.0"
-
-mdast-util-find-and-replace@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-2.2.0.tgz#6167edf16c2fd79e7213024544575f304151953f"
- integrity sha512-bz8hUWkMX7UcasORORcyBEsTKJ+dBiFwRPrm43hHC9NMRylIMLbfO5rwfeCN+UtY4AAi7s8WqXftb9eX6ZsqCg==
- dependencies:
- escape-string-regexp "^5.0.0"
- unist-util-is "^5.0.0"
- unist-util-visit-parents "^5.0.0"
-
-mdast-util-from-markdown@^1.0.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-1.2.0.tgz#84df2924ccc6c995dec1e2368b2b208ad0a76268"
- integrity sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q==
- dependencies:
- "@types/mdast" "^3.0.0"
- "@types/unist" "^2.0.0"
- decode-named-character-reference "^1.0.0"
- mdast-util-to-string "^3.1.0"
- micromark "^3.0.0"
- micromark-util-decode-numeric-character-reference "^1.0.0"
- micromark-util-decode-string "^1.0.0"
- micromark-util-normalize-identifier "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
- unist-util-stringify-position "^3.0.0"
- uvu "^0.5.0"
-
-mdast-util-gfm-autolink-literal@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-1.0.2.tgz#4032dcbaddaef7d4f2f3768ed830475bb22d3970"
- integrity sha512-FzopkOd4xTTBeGXhXSBU0OCDDh5lUj2rd+HQqG92Ld+jL4lpUfgX2AT2OHAVP9aEeDKp7G92fuooSZcYJA3cRg==
- dependencies:
- "@types/mdast" "^3.0.0"
- ccount "^2.0.0"
- mdast-util-find-and-replace "^2.0.0"
- micromark-util-character "^1.0.0"
-
-mdast-util-gfm-footnote@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.1.tgz#11d2d40a1a673a399c459e467fa85e00223191fe"
- integrity sha512-p+PrYlkw9DeCRkTVw1duWqPRHX6Ywh2BNKJQcZbCwAuP/59B0Lk9kakuAd7KbQprVO4GzdW8eS5++A9PUSqIyw==
- dependencies:
- "@types/mdast" "^3.0.0"
- mdast-util-to-markdown "^1.3.0"
- micromark-util-normalize-identifier "^1.0.0"
-
-mdast-util-gfm-strikethrough@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.1.tgz#a4a74c36864ec6a6e3bbd31e1977f29beb475789"
- integrity sha512-zKJbEPe+JP6EUv0mZ0tQUyLQOC+FADt0bARldONot/nefuISkaZFlmVK4tU6JgfyZGrky02m/I6PmehgAgZgqg==
- dependencies:
- "@types/mdast" "^3.0.0"
- mdast-util-to-markdown "^1.3.0"
-
-mdast-util-gfm-table@^1.0.0:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.4.tgz#0dbb25f04fd9c0877dc63b76203ecbdf5d945755"
- integrity sha512-aEuoPwZyP4iIMkf2cLWXxx3EQ6Bmh2yKy9MVCg4i6Sd3cX80dcLEfXO/V4ul3pGH9czBK4kp+FAl+ZHmSUt9/w==
- dependencies:
- markdown-table "^3.0.0"
- mdast-util-from-markdown "^1.0.0"
- mdast-util-to-markdown "^1.3.0"
-
-mdast-util-gfm-task-list-item@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.1.tgz#6f35f09c6e2bcbe88af62fdea02ac199cc802c5c"
- integrity sha512-KZ4KLmPdABXOsfnM6JHUIjxEvcx2ulk656Z/4Balw071/5qgnhz+H1uGtf2zIGnrnvDC8xR4Fj9uKbjAFGNIeA==
- dependencies:
- "@types/mdast" "^3.0.0"
- mdast-util-to-markdown "^1.3.0"
-
-mdast-util-gfm@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/mdast-util-gfm/-/mdast-util-gfm-2.0.1.tgz#16fcf70110ae689a06d77e8f4e346223b64a0ea6"
- integrity sha512-42yHBbfWIFisaAfV1eixlabbsa6q7vHeSPY+cg+BBjX51M8xhgMacqH9g6TftB/9+YkcI0ooV4ncfrJslzm/RQ==
- dependencies:
- mdast-util-from-markdown "^1.0.0"
- mdast-util-gfm-autolink-literal "^1.0.0"
- mdast-util-gfm-footnote "^1.0.0"
- mdast-util-gfm-strikethrough "^1.0.0"
- mdast-util-gfm-table "^1.0.0"
- mdast-util-gfm-task-list-item "^1.0.0"
- mdast-util-to-markdown "^1.0.0"
-
-mdast-util-to-markdown@^1.0.0, mdast-util-to-markdown@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-1.3.0.tgz#38b6cdc8dc417de642a469c4fc2abdf8c931bd1e"
- integrity sha512-6tUSs4r+KK4JGTTiQ7FfHmVOaDrLQJPmpjD6wPMlHGUVXoG9Vjc3jIeP+uyBWRf8clwB2blM+W7+KrlMYQnftA==
- dependencies:
- "@types/mdast" "^3.0.0"
- "@types/unist" "^2.0.0"
- longest-streak "^3.0.0"
- mdast-util-to-string "^3.0.0"
- micromark-util-decode-string "^1.0.0"
- unist-util-visit "^4.0.0"
- zwitch "^2.0.0"
-
-mdast-util-to-string@^3.0.0, mdast-util-to-string@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz#56c506d065fbf769515235e577b5a261552d56e9"
- integrity sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA==
-
-mdn-data@2.0.14:
- version "2.0.14"
- resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50"
- integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==
-
-mdn-data@2.0.4:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b"
- integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==
-
-media-typer@0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
- integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
-
-memfs@^3.1.2, memfs@^3.4.3:
- version "3.4.4"
- resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.4.tgz#e8973cd8060548916adcca58a248e7805c715e89"
- integrity sha512-W4gHNUE++1oSJVn8Y68jPXi+mkx3fXR5ITE/Ubz6EQ3xRpCN5k2CQ4AUR8094Z7211F876TyoBACGsIveqgiGA==
- dependencies:
- fs-monkey "1.0.3"
-
-merge-descriptors@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
- integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==
-
-merge-stream@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
- integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
-
-merge2@^1.3.0, merge2@^1.4.1:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
- integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
-
-mermaid@^9.0.0:
- version "9.4.3"
- resolved "https://npm.shopee.io/mermaid/-/mermaid-9.4.3.tgz#62cf210c246b74972ea98c19837519b6f03427f2"
- integrity sha512-TLkQEtqhRSuEHSE34lh5bCa94KATCyluAXmFnNI2PRZwOpXFeqiJWwZl+d2CcemE1RS6QbbueSSq9QIg8Uxcyw==
- dependencies:
- "@braintree/sanitize-url" "^6.0.0"
- cytoscape "^3.23.0"
- cytoscape-cose-bilkent "^4.1.0"
- cytoscape-fcose "^2.1.0"
- d3 "^7.4.0"
- dagre-d3-es "7.0.9"
- dayjs "^1.11.7"
- dompurify "2.4.3"
- elkjs "^0.8.2"
- khroma "^2.0.0"
- lodash-es "^4.17.21"
- non-layered-tidy-tree-layout "^2.0.2"
- stylis "^4.1.2"
- ts-dedent "^2.2.0"
- uuid "^9.0.0"
- web-worker "^1.2.0"
-
-methods@~1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
- integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
-
-micromark-core-commonmark@^1.0.0, micromark-core-commonmark@^1.0.1:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz#edff4c72e5993d93724a3c206970f5a15b0585ad"
- integrity sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA==
- dependencies:
- decode-named-character-reference "^1.0.0"
- micromark-factory-destination "^1.0.0"
- micromark-factory-label "^1.0.0"
- micromark-factory-space "^1.0.0"
- micromark-factory-title "^1.0.0"
- micromark-factory-whitespace "^1.0.0"
- micromark-util-character "^1.0.0"
- micromark-util-chunked "^1.0.0"
- micromark-util-classify-character "^1.0.0"
- micromark-util-html-tag-name "^1.0.0"
- micromark-util-normalize-identifier "^1.0.0"
- micromark-util-resolve-all "^1.0.0"
- micromark-util-subtokenize "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.1"
- uvu "^0.5.0"
-
-micromark-extension-directive@^2.0.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/micromark-extension-directive/-/micromark-extension-directive-2.1.1.tgz#d2dae9219618fcce06226d0b63b7a2a23fbe23ec"
- integrity sha512-+7MYZ3a10cpPrQRg3530srFMSBx0EL7gQaJ3ekguOQFSlJHLikW15AphBmNxvCNdRSWTX1R8RepzjKQra8INQw==
- dependencies:
- micromark-factory-space "^1.0.0"
- micromark-factory-whitespace "^1.0.0"
- micromark-util-character "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
- parse-entities "^4.0.0"
- uvu "^0.5.0"
-
-micromark-extension-gfm-autolink-literal@^1.0.0:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-1.0.3.tgz#dc589f9c37eaff31a175bab49f12290edcf96058"
- integrity sha512-i3dmvU0htawfWED8aHMMAzAVp/F0Z+0bPh3YrbTPPL1v4YAlCZpy5rBO5p0LPYiZo0zFVkoYh7vDU7yQSiCMjg==
- dependencies:
- micromark-util-character "^1.0.0"
- micromark-util-sanitize-uri "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
- uvu "^0.5.0"
-
-micromark-extension-gfm-footnote@^1.0.0:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.4.tgz#cbfd8873b983e820c494498c6dac0105920818d5"
- integrity sha512-E/fmPmDqLiMUP8mLJ8NbJWJ4bTw6tS+FEQS8CcuDtZpILuOb2kjLqPEeAePF1djXROHXChM/wPJw0iS4kHCcIg==
- dependencies:
- micromark-core-commonmark "^1.0.0"
- micromark-factory-space "^1.0.0"
- micromark-util-character "^1.0.0"
- micromark-util-normalize-identifier "^1.0.0"
- micromark-util-sanitize-uri "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
- uvu "^0.5.0"
-
-micromark-extension-gfm-strikethrough@^1.0.0:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.4.tgz#162232c284ffbedd8c74e59c1525bda217295e18"
- integrity sha512-/vjHU/lalmjZCT5xt7CcHVJGq8sYRm80z24qAKXzaHzem/xsDYb2yLL+NNVbYvmpLx3O7SYPuGL5pzusL9CLIQ==
- dependencies:
- micromark-util-chunked "^1.0.0"
- micromark-util-classify-character "^1.0.0"
- micromark-util-resolve-all "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
- uvu "^0.5.0"
-
-micromark-extension-gfm-table@^1.0.0:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.5.tgz#7b708b728f8dc4d95d486b9e7a2262f9cddbcbb4"
- integrity sha512-xAZ8J1X9W9K3JTJTUL7G6wSKhp2ZYHrFk5qJgY/4B33scJzE2kpfRL6oiw/veJTbt7jiM/1rngLlOKPWr1G+vg==
- dependencies:
- micromark-factory-space "^1.0.0"
- micromark-util-character "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
- uvu "^0.5.0"
-
-micromark-extension-gfm-tagfilter@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-1.0.1.tgz#fb2e303f7daf616db428bb6a26e18fda14a90a4d"
- integrity sha512-Ty6psLAcAjboRa/UKUbbUcwjVAv5plxmpUTy2XC/3nJFL37eHej8jrHrRzkqcpipJliuBH30DTs7+3wqNcQUVA==
- dependencies:
- micromark-util-types "^1.0.0"
-
-micromark-extension-gfm-task-list-item@^1.0.0:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.3.tgz#7683641df5d4a09795f353574d7f7f66e47b7fc4"
- integrity sha512-PpysK2S1Q/5VXi72IIapbi/jliaiOFzv7THH4amwXeYXLq3l1uo8/2Be0Ac1rEwK20MQEsGH2ltAZLNY2KI/0Q==
- dependencies:
- micromark-factory-space "^1.0.0"
- micromark-util-character "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
- uvu "^0.5.0"
-
-micromark-extension-gfm@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/micromark-extension-gfm/-/micromark-extension-gfm-2.0.1.tgz#40f3209216127a96297c54c67f5edc7ef2d1a2a2"
- integrity sha512-p2sGjajLa0iYiGQdT0oelahRYtMWvLjy8J9LOCxzIQsllMCGLbsLW+Nc+N4vi02jcRJvedVJ68cjelKIO6bpDA==
- dependencies:
- micromark-extension-gfm-autolink-literal "^1.0.0"
- micromark-extension-gfm-footnote "^1.0.0"
- micromark-extension-gfm-strikethrough "^1.0.0"
- micromark-extension-gfm-table "^1.0.0"
- micromark-extension-gfm-tagfilter "^1.0.0"
- micromark-extension-gfm-task-list-item "^1.0.0"
- micromark-util-combine-extensions "^1.0.0"
- micromark-util-types "^1.0.0"
-
-micromark-factory-destination@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz#fef1cb59ad4997c496f887b6977aa3034a5a277e"
- integrity sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw==
- dependencies:
- micromark-util-character "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
-
-micromark-factory-label@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz#6be2551fa8d13542fcbbac478258fb7a20047137"
- integrity sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg==
- dependencies:
- micromark-util-character "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
- uvu "^0.5.0"
-
-micromark-factory-space@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz#cebff49968f2b9616c0fcb239e96685cb9497633"
- integrity sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew==
- dependencies:
- micromark-util-character "^1.0.0"
- micromark-util-types "^1.0.0"
-
-micromark-factory-title@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz#7e09287c3748ff1693930f176e1c4a328382494f"
- integrity sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A==
- dependencies:
- micromark-factory-space "^1.0.0"
- micromark-util-character "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
- uvu "^0.5.0"
-
-micromark-factory-whitespace@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz#e991e043ad376c1ba52f4e49858ce0794678621c"
- integrity sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A==
- dependencies:
- micromark-factory-space "^1.0.0"
- micromark-util-character "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
-
-micromark-util-character@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-1.1.0.tgz#d97c54d5742a0d9611a68ca0cd4124331f264d86"
- integrity sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg==
- dependencies:
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
-
-micromark-util-chunked@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz#5b40d83f3d53b84c4c6bce30ed4257e9a4c79d06"
- integrity sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g==
- dependencies:
- micromark-util-symbol "^1.0.0"
-
-micromark-util-classify-character@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz#cbd7b447cb79ee6997dd274a46fc4eb806460a20"
- integrity sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA==
- dependencies:
- micromark-util-character "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
-
-micromark-util-combine-extensions@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz#91418e1e74fb893e3628b8d496085639124ff3d5"
- integrity sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==
- dependencies:
- micromark-util-chunked "^1.0.0"
- micromark-util-types "^1.0.0"
-
-micromark-util-decode-numeric-character-reference@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz#dcc85f13b5bd93ff8d2868c3dba28039d490b946"
- integrity sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w==
- dependencies:
- micromark-util-symbol "^1.0.0"
-
-micromark-util-decode-string@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz#942252ab7a76dec2dbf089cc32505ee2bc3acf02"
- integrity sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q==
- dependencies:
- decode-named-character-reference "^1.0.0"
- micromark-util-character "^1.0.0"
- micromark-util-decode-numeric-character-reference "^1.0.0"
- micromark-util-symbol "^1.0.0"
-
-micromark-util-encode@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz#2c1c22d3800870ad770ece5686ebca5920353383"
- integrity sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA==
-
-micromark-util-html-tag-name@^1.0.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz#eb227118befd51f48858e879b7a419fc0df20497"
- integrity sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA==
-
-micromark-util-normalize-identifier@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz#4a3539cb8db954bbec5203952bfe8cedadae7828"
- integrity sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg==
- dependencies:
- micromark-util-symbol "^1.0.0"
-
-micromark-util-resolve-all@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz#a7c363f49a0162e931960c44f3127ab58f031d88"
- integrity sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw==
- dependencies:
- micromark-util-types "^1.0.0"
-
-micromark-util-sanitize-uri@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.0.0.tgz#27dc875397cd15102274c6c6da5585d34d4f12b2"
- integrity sha512-cCxvBKlmac4rxCGx6ejlIviRaMKZc0fWm5HdCHEeDWRSkn44l6NdYVRyU+0nT1XC72EQJMZV8IPHF+jTr56lAg==
- dependencies:
- micromark-util-character "^1.0.0"
- micromark-util-encode "^1.0.0"
- micromark-util-symbol "^1.0.0"
-
-micromark-util-subtokenize@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz#ff6f1af6ac836f8bfdbf9b02f40431760ad89105"
- integrity sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA==
- dependencies:
- micromark-util-chunked "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.0"
- uvu "^0.5.0"
-
-micromark-util-symbol@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz#b90344db62042ce454f351cf0bebcc0a6da4920e"
- integrity sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ==
-
-micromark-util-types@^1.0.0, micromark-util-types@^1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-1.0.2.tgz#f4220fdb319205812f99c40f8c87a9be83eded20"
- integrity sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w==
-
-micromark@^3.0.0:
- version "3.0.10"
- resolved "https://registry.yarnpkg.com/micromark/-/micromark-3.0.10.tgz#1eac156f0399d42736458a14b0ca2d86190b457c"
- integrity sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg==
- dependencies:
- "@types/debug" "^4.0.0"
- debug "^4.0.0"
- decode-named-character-reference "^1.0.0"
- micromark-core-commonmark "^1.0.1"
- micromark-factory-space "^1.0.0"
- micromark-util-character "^1.0.0"
- micromark-util-chunked "^1.0.0"
- micromark-util-combine-extensions "^1.0.0"
- micromark-util-decode-numeric-character-reference "^1.0.0"
- micromark-util-encode "^1.0.0"
- micromark-util-normalize-identifier "^1.0.0"
- micromark-util-resolve-all "^1.0.0"
- micromark-util-sanitize-uri "^1.0.0"
- micromark-util-subtokenize "^1.0.0"
- micromark-util-symbol "^1.0.0"
- micromark-util-types "^1.0.1"
- uvu "^0.5.0"
-
-micromatch@^4.0.2, micromatch@^4.0.4:
- version "4.0.5"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
- integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
- dependencies:
- braces "^3.0.2"
- picomatch "^2.3.1"
-
-mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
- version "1.52.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
- integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
-
-mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
- version "2.1.35"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
- integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
- dependencies:
- mime-db "1.52.0"
-
-mime@1.6.0, mime@^1.4.1:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
- integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
-
-mimic-fn@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
- integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-
-mini-create-react-context@^0.4.0:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz#072171561bfdc922da08a60c2197a497cc2d1d5e"
- integrity sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==
- dependencies:
- "@babel/runtime" "^7.12.1"
- tiny-warning "^1.0.3"
-
-mini-css-extract-plugin@^2.4.5:
- version "2.6.0"
- resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.0.tgz#578aebc7fc14d32c0ad304c2c34f08af44673f5e"
- integrity sha512-ndG8nxCEnAemsg4FSgS+yNyHKgkTB4nPKqCOgh65j3/30qqC5RaSQQXMm++Y6sb6E1zRSxPkztj9fqxhS1Eo6w==
- dependencies:
- schema-utils "^4.0.0"
-
-minimalistic-assert@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7"
- integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==
-
-minimatch@3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
- integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
- dependencies:
- brace-expansion "^1.1.7"
-
-minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
- integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
- dependencies:
- brace-expansion "^1.1.7"
-
-minimatch@^5.0.1:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7"
- integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==
- dependencies:
- brace-expansion "^2.0.1"
-
-minimist@^1.2.0, minimist@^1.2.6:
- version "1.2.6"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
- integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
-
-mkdirp@~0.5.1:
- version "0.5.6"
- resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
- integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
- dependencies:
- minimist "^1.2.6"
-
-mri@^1.1.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
- integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
-
-ms@2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
- integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
-
-ms@2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
- integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
-
-ms@2.1.3, ms@^2.1.1:
- version "2.1.3"
- resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
- integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
-
-multicast-dns@^7.2.4:
- version "7.2.5"
- resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced"
- integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==
- dependencies:
- dns-packet "^5.2.2"
- thunky "^1.0.2"
-
-nanoid@^3.1.25, nanoid@^3.3.4:
- version "3.3.4"
- resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
- integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
-
-natural-compare@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
- integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
-
-needle@^2.5.2:
- version "2.9.1"
- resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684"
- integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==
- dependencies:
- debug "^3.2.6"
- iconv-lite "^0.4.4"
- sax "^1.2.4"
-
-negotiator@0.6.3:
- version "0.6.3"
- resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
- integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
-
-neo-async@^2.6.2:
- version "2.6.2"
- resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
- integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
-
-no-case@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
- integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
- dependencies:
- lower-case "^2.0.2"
- tslib "^2.0.3"
-
-node-emoji@^1.10.0, node-emoji@^1.11.0:
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.11.0.tgz#69a0150e6946e2f115e9d7ea4df7971e2628301c"
- integrity sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==
- dependencies:
- lodash "^4.17.21"
-
-node-forge@^1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
- integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==
-
-node-int64@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
- integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
-
-node-releases@^2.0.3:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666"
- integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==
-
-non-layered-tidy-tree-layout@^2.0.2:
- version "2.0.2"
- resolved "https://npm.shopee.io/non-layered-tidy-tree-layout/-/non-layered-tidy-tree-layout-2.0.2.tgz#57d35d13c356643fc296a55fb11ac15e74da7804"
- integrity sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==
-
-normalize-path@^3.0.0, normalize-path@~3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
- integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-
-normalize-range@^0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
- integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==
-
-normalize-url@^6.0.1:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
- integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
-
-npm-run-path@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
- integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
- dependencies:
- path-key "^3.0.0"
-
-nth-check@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
- integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==
- dependencies:
- boolbase "~1.0.0"
-
-nth-check@^2.0.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
- integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
- dependencies:
- boolbase "^1.0.0"
-
-nwsapi@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
- integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
-
-object-assign@^4.1.1:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
- integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
-
-object-hash@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
- integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
-
-object-inspect@^1.12.0, object-inspect@^1.9.0:
- version "1.12.2"
- resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
- integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
-
-object-keys@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
- integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
-
-object.assign@^4.1.0, object.assign@^4.1.2:
- version "4.1.2"
- resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
- integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
- dependencies:
- call-bind "^1.0.0"
- define-properties "^1.1.3"
- has-symbols "^1.0.1"
- object-keys "^1.1.1"
-
-object.entries@^1.1.5:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861"
- integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
- es-abstract "^1.19.1"
-
-object.fromentries@^2.0.5:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251"
- integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
- es-abstract "^1.19.1"
-
-object.getownpropertydescriptors@^2.1.0:
- version "2.1.4"
- resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37"
- integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==
- dependencies:
- array.prototype.reduce "^1.0.4"
- call-bind "^1.0.2"
- define-properties "^1.1.4"
- es-abstract "^1.20.1"
-
-object.hasown@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3"
- integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A==
- dependencies:
- define-properties "^1.1.4"
- es-abstract "^1.19.5"
-
-object.values@^1.1.0, object.values@^1.1.5:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac"
- integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
- es-abstract "^1.19.1"
-
-obuf@^1.0.0, obuf@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
- integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==
-
-on-finished@2.4.1:
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
- integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
- dependencies:
- ee-first "1.1.1"
-
-on-headers@~1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
- integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
-
-once@^1.3.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
- integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
- dependencies:
- wrappy "1"
-
-onetime@^5.1.2:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
- integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
- dependencies:
- mimic-fn "^2.1.0"
-
-open@^8.0.9, open@^8.4.0:
- version "8.4.0"
- resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8"
- integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==
- dependencies:
- define-lazy-prop "^2.0.0"
- is-docker "^2.1.1"
- is-wsl "^2.2.0"
-
-optionator@^0.8.1:
- version "0.8.3"
- resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
- integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
- dependencies:
- deep-is "~0.1.3"
- fast-levenshtein "~2.0.6"
- levn "~0.3.0"
- prelude-ls "~1.1.2"
- type-check "~0.3.2"
- word-wrap "~1.2.3"
-
-optionator@^0.9.1:
- version "0.9.1"
- resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
- integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
- dependencies:
- deep-is "^0.1.3"
- fast-levenshtein "^2.0.6"
- levn "^0.4.1"
- prelude-ls "^1.2.1"
- type-check "^0.4.0"
- word-wrap "^1.2.3"
-
-orderedmap@^1.1.0:
- version "1.1.8"
- resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-1.1.8.tgz#9652b2584f721c1032fa04cb60d442b3d4aa097c"
- integrity sha512-eWEYOAggZZpZbJ9CTsqAKOTxlbBHdHZ8pzcfEvNTxGrjQ/m+Q25nSWUiMlT9MTbgpB6FOiBDKqsgJ2FlLDVNaw==
-
-p-limit@^1.1.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
- integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
- dependencies:
- p-try "^1.0.0"
-
-p-limit@^2.0.0, p-limit@^2.2.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
- integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
- dependencies:
- p-try "^2.0.0"
-
-p-limit@^3.0.2:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
- integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
- dependencies:
- yocto-queue "^0.1.0"
-
-p-locate@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
- integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==
- dependencies:
- p-limit "^1.1.0"
-
-p-locate@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4"
- integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==
- dependencies:
- p-limit "^2.0.0"
-
-p-locate@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
- integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
- dependencies:
- p-limit "^2.2.0"
-
-p-locate@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
- integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
- dependencies:
- p-limit "^3.0.2"
-
-p-retry@^4.5.0:
- version "4.6.2"
- resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16"
- integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==
- dependencies:
- "@types/retry" "0.12.0"
- retry "^0.13.1"
-
-p-try@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
- integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==
-
-p-try@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
- integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
-
-param-case@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
- integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==
- dependencies:
- dot-case "^3.0.4"
- tslib "^2.0.3"
-
-parent-module@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
- integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
- dependencies:
- callsites "^3.0.0"
-
-parse-entities@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.0.tgz#f67c856d4e3fe19b1a445c3fabe78dcdc1053eeb"
- integrity sha512-5nk9Fn03x3rEhGaX1FU6IDwG/k+GxLXlFAkgrbM1asuAFl3BhdQWvASaIsmwWypRNcZKHPYnIuOSfIWEyEQnPQ==
- dependencies:
- "@types/unist" "^2.0.0"
- character-entities "^2.0.0"
- character-entities-legacy "^3.0.0"
- character-reference-invalid "^2.0.0"
- decode-named-character-reference "^1.0.0"
- is-alphanumerical "^2.0.0"
- is-decimal "^2.0.0"
- is-hexadecimal "^2.0.0"
-
-parse-json@^5.0.0, parse-json@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
- integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
- dependencies:
- "@babel/code-frame" "^7.0.0"
- error-ex "^1.3.1"
- json-parse-even-better-errors "^2.3.0"
- lines-and-columns "^1.1.6"
-
-parse-node-version@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b"
- integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==
-
-parse5@6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
- integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
-
-parseurl@~1.3.2, parseurl@~1.3.3:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
- integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
-
-pascal-case@^3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
- integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==
- dependencies:
- no-case "^3.0.4"
- tslib "^2.0.3"
-
-path-exists@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
- integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==
-
-path-exists@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
- integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
-
-path-is-absolute@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
- integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
-
-path-key@^3.0.0, path-key@^3.1.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
- integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
-
-path-parse@^1.0.6, path-parse@^1.0.7:
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
- integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
-
-path-to-regexp@0.1.7:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
- integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==
-
-path-to-regexp@^1.7.0:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
- integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
- dependencies:
- isarray "0.0.1"
-
-path-type@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
- integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
-
-performance-now@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
- integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
-
-picocolors@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f"
- integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==
-
-picocolors@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
- integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
-
-picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
- integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
-
-pify@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
- integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==
-
-pirates@^4.0.4:
- version "4.0.5"
- resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"
- integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==
-
-pkg-dir@^4.1.0, pkg-dir@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
- integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
- dependencies:
- find-up "^4.0.0"
-
-pkg-up@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
- integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
- dependencies:
- find-up "^3.0.0"
-
-postcss-attribute-case-insensitive@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.0.tgz#39cbf6babf3ded1e4abf37d09d6eda21c644105c"
- integrity sha512-b4g9eagFGq9T5SWX4+USfVyjIb3liPnjhHHRMP7FMB2kFVpYyfEscV0wP3eaXhKlcHKUut8lt5BGoeylWA/dBQ==
- dependencies:
- postcss-selector-parser "^6.0.2"
-
-postcss-browser-comments@^4:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz#bcfc86134df5807f5d3c0eefa191d42136b5e72a"
- integrity sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==
-
-postcss-calc@^8.2.3:
- version "8.2.4"
- resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-8.2.4.tgz#77b9c29bfcbe8a07ff6693dc87050828889739a5"
- integrity sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==
- dependencies:
- postcss-selector-parser "^6.0.9"
- postcss-value-parser "^4.2.0"
-
-postcss-clamp@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/postcss-clamp/-/postcss-clamp-4.1.0.tgz#7263e95abadd8c2ba1bd911b0b5a5c9c93e02363"
- integrity sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-color-functional-notation@^4.2.3:
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.3.tgz#23c9d73c76113b75473edcf66f443c6f1872bd0f"
- integrity sha512-5fbr6FzFzjwHXKsVnkmEYrJYG8VNNzvD1tAXaPPWR97S6rhKI5uh2yOfV5TAzhDkZoq4h+chxEplFDc8GeyFtw==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-color-hex-alpha@^8.0.3:
- version "8.0.3"
- resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.3.tgz#61a0fd151d28b128aa6a8a21a2dad24eebb34d52"
- integrity sha512-fESawWJCrBV035DcbKRPAVmy21LpoyiXdPTuHUfWJ14ZRjY7Y7PA6P4g8z6LQGYhU1WAxkTxjIjurXzoe68Glw==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-color-rebeccapurple@^7.0.2:
- version "7.0.2"
- resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.0.2.tgz#5d397039424a58a9ca628762eb0b88a61a66e079"
- integrity sha512-SFc3MaocHaQ6k3oZaFwH8io6MdypkUtEy/eXzXEB1vEQlO3S3oDc/FSZA8AsS04Z25RirQhlDlHLh3dn7XewWw==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-colormin@^5.3.0:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.0.tgz#3cee9e5ca62b2c27e84fce63affc0cfb5901956a"
- integrity sha512-WdDO4gOFG2Z8n4P8TWBpshnL3JpmNmJwdnfP2gbk2qBA8PWwOYcmjmI/t3CmMeL72a7Hkd+x/Mg9O2/0rD54Pg==
- dependencies:
- browserslist "^4.16.6"
- caniuse-api "^3.0.0"
- colord "^2.9.1"
- postcss-value-parser "^4.2.0"
-
-postcss-convert-values@^5.1.2:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.1.2.tgz#31586df4e184c2e8890e8b34a0b9355313f503ab"
- integrity sha512-c6Hzc4GAv95B7suy4udszX9Zy4ETyMCgFPUDtWjdFTKH1SE9eFY/jEpHSwTH1QPuwxHpWslhckUQWbNRM4ho5g==
- dependencies:
- browserslist "^4.20.3"
- postcss-value-parser "^4.2.0"
-
-postcss-custom-media@^8.0.0:
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-8.0.0.tgz#1be6aff8be7dc9bf1fe014bde3b71b92bb4552f1"
- integrity sha512-FvO2GzMUaTN0t1fBULDeIvxr5IvbDXcIatt6pnJghc736nqNgsGao5NT+5+WVLAQiTt6Cb3YUms0jiPaXhL//g==
-
-postcss-custom-properties@^12.1.7:
- version "12.1.7"
- resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-12.1.7.tgz#ca470fd4bbac5a87fd868636dafc084bc2a78b41"
- integrity sha512-N/hYP5gSoFhaqxi2DPCmvto/ZcRDVjE3T1LiAMzc/bg53hvhcHOLpXOHb526LzBBp5ZlAUhkuot/bfpmpgStJg==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-custom-selectors@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-6.0.0.tgz#022839e41fbf71c47ae6e316cb0e6213012df5ef"
- integrity sha512-/1iyBhz/W8jUepjGyu7V1OPcGbc636snN1yXEQCinb6Bwt7KxsiU7/bLQlp8GwAXzCh7cobBU5odNn/2zQWR8Q==
- dependencies:
- postcss-selector-parser "^6.0.4"
-
-postcss-dir-pseudo-class@^6.0.4:
- version "6.0.4"
- resolved "https://registry.yarnpkg.com/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.4.tgz#9afe49ea631f0cb36fa0076e7c2feb4e7e3f049c"
- integrity sha512-I8epwGy5ftdzNWEYok9VjW9whC4xnelAtbajGv4adql4FIF09rnrxnA9Y8xSHN47y7gqFIv10C5+ImsLeJpKBw==
- dependencies:
- postcss-selector-parser "^6.0.9"
-
-postcss-discard-comments@^5.1.2:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz#8df5e81d2925af2780075840c1526f0660e53696"
- integrity sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==
-
-postcss-discard-duplicates@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz#9eb4fe8456706a4eebd6d3b7b777d07bad03e848"
- integrity sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==
-
-postcss-discard-empty@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz#e57762343ff7f503fe53fca553d18d7f0c369c6c"
- integrity sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==
-
-postcss-discard-overridden@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz#7e8c5b53325747e9d90131bb88635282fb4a276e"
- integrity sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==
-
-postcss-double-position-gradients@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.1.tgz#a12cfdb7d11fa1a99ccecc747f0c19718fb37152"
- integrity sha512-jM+CGkTs4FcG53sMPjrrGE0rIvLDdCrqMzgDC5fLI7JHDO7o6QG8C5TQBtExb13hdBdoH9C2QVbG4jo2y9lErQ==
- dependencies:
- "@csstools/postcss-progressive-custom-properties" "^1.1.0"
- postcss-value-parser "^4.2.0"
-
-postcss-env-function@^4.0.6:
- version "4.0.6"
- resolved "https://registry.yarnpkg.com/postcss-env-function/-/postcss-env-function-4.0.6.tgz#7b2d24c812f540ed6eda4c81f6090416722a8e7a"
- integrity sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-flexbugs-fixes@^5.0.2:
- version "5.0.2"
- resolved "https://registry.yarnpkg.com/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz#2028e145313074fc9abe276cb7ca14e5401eb49d"
- integrity sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==
-
-postcss-focus-visible@^6.0.4:
- version "6.0.4"
- resolved "https://registry.yarnpkg.com/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz#50c9ea9afa0ee657fb75635fabad25e18d76bf9e"
- integrity sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==
- dependencies:
- postcss-selector-parser "^6.0.9"
-
-postcss-focus-within@^5.0.4:
- version "5.0.4"
- resolved "https://registry.yarnpkg.com/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz#5b1d2ec603195f3344b716c0b75f61e44e8d2e20"
- integrity sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==
- dependencies:
- postcss-selector-parser "^6.0.9"
-
-postcss-font-variant@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz#efd59b4b7ea8bb06127f2d031bfbb7f24d32fa66"
- integrity sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==
-
-postcss-gap-properties@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/postcss-gap-properties/-/postcss-gap-properties-3.0.3.tgz#6401bb2f67d9cf255d677042928a70a915e6ba60"
- integrity sha512-rPPZRLPmEKgLk/KlXMqRaNkYTUpE7YC+bOIQFN5xcu1Vp11Y4faIXv6/Jpft6FMnl6YRxZqDZG0qQOW80stzxQ==
-
-postcss-image-set-function@^4.0.6:
- version "4.0.6"
- resolved "https://registry.yarnpkg.com/postcss-image-set-function/-/postcss-image-set-function-4.0.6.tgz#bcff2794efae778c09441498f40e0c77374870a9"
- integrity sha512-KfdC6vg53GC+vPd2+HYzsZ6obmPqOk6HY09kttU19+Gj1nC3S3XBVEXDHxkhxTohgZqzbUb94bKXvKDnYWBm/A==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-initial@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-4.0.1.tgz#529f735f72c5724a0fb30527df6fb7ac54d7de42"
- integrity sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==
-
-postcss-js@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.0.tgz#31db79889531b80dc7bc9b0ad283e418dce0ac00"
- integrity sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==
- dependencies:
- camelcase-css "^2.0.1"
-
-postcss-lab-function@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/postcss-lab-function/-/postcss-lab-function-4.2.0.tgz#e054e662c6480202f5760887ec1ae0d153357123"
- integrity sha512-Zb1EO9DGYfa3CP8LhINHCcTTCTLI+R3t7AX2mKsDzdgVQ/GkCpHOTgOr6HBHslP7XDdVbqgHW5vvRPMdVANQ8w==
- dependencies:
- "@csstools/postcss-progressive-custom-properties" "^1.1.0"
- postcss-value-parser "^4.2.0"
-
-postcss-load-config@^3.1.4:
- version "3.1.4"
- resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855"
- integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
- dependencies:
- lilconfig "^2.0.5"
- yaml "^1.10.2"
-
-postcss-loader@^6.2.1:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-6.2.1.tgz#0895f7346b1702103d30fdc66e4d494a93c008ef"
- integrity sha512-WbbYpmAaKcux/P66bZ40bpWsBucjx/TTgVVzRZ9yUO8yQfVBlameJ0ZGVaPfH64hNSBh63a+ICP5nqOpBA0w+Q==
- dependencies:
- cosmiconfig "^7.0.0"
- klona "^2.0.5"
- semver "^7.3.5"
-
-postcss-logical@^5.0.4:
- version "5.0.4"
- resolved "https://registry.yarnpkg.com/postcss-logical/-/postcss-logical-5.0.4.tgz#ec75b1ee54421acc04d5921576b7d8db6b0e6f73"
- integrity sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==
-
-postcss-media-minmax@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz#7140bddec173e2d6d657edbd8554a55794e2a5b5"
- integrity sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==
-
-postcss-merge-longhand@^5.1.5:
- version "5.1.5"
- resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-5.1.5.tgz#b0e03bee3b964336f5f33c4fc8eacae608e91c05"
- integrity sha512-NOG1grw9wIO+60arKa2YYsrbgvP6tp+jqc7+ZD5/MalIw234ooH2C6KlR6FEn4yle7GqZoBxSK1mLBE9KPur6w==
- dependencies:
- postcss-value-parser "^4.2.0"
- stylehacks "^5.1.0"
-
-postcss-merge-rules@^5.1.2:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.2.tgz#7049a14d4211045412116d79b751def4484473a5"
- integrity sha512-zKMUlnw+zYCWoPN6yhPjtcEdlJaMUZ0WyVcxTAmw3lkkN/NDMRkOkiuctQEoWAOvH7twaxUUdvBWl0d4+hifRQ==
- dependencies:
- browserslist "^4.16.6"
- caniuse-api "^3.0.0"
- cssnano-utils "^3.1.0"
- postcss-selector-parser "^6.0.5"
-
-postcss-minify-font-values@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz#f1df0014a726083d260d3bd85d7385fb89d1f01b"
- integrity sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-minify-gradients@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz#f1fe1b4f498134a5068240c2f25d46fcd236ba2c"
- integrity sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==
- dependencies:
- colord "^2.9.1"
- cssnano-utils "^3.1.0"
- postcss-value-parser "^4.2.0"
-
-postcss-minify-params@^5.1.3:
- version "5.1.3"
- resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-5.1.3.tgz#ac41a6465be2db735099bbd1798d85079a6dc1f9"
- integrity sha512-bkzpWcjykkqIujNL+EVEPOlLYi/eZ050oImVtHU7b4lFS82jPnsCb44gvC6pxaNt38Els3jWYDHTjHKf0koTgg==
- dependencies:
- browserslist "^4.16.6"
- cssnano-utils "^3.1.0"
- postcss-value-parser "^4.2.0"
-
-postcss-minify-selectors@^5.2.1:
- version "5.2.1"
- resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz#d4e7e6b46147b8117ea9325a915a801d5fe656c6"
- integrity sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==
- dependencies:
- postcss-selector-parser "^6.0.5"
-
-postcss-modules-extract-imports@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d"
- integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==
-
-postcss-modules-local-by-default@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz#ebbb54fae1598eecfdf691a02b3ff3b390a5a51c"
- integrity sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==
- dependencies:
- icss-utils "^5.0.0"
- postcss-selector-parser "^6.0.2"
- postcss-value-parser "^4.1.0"
-
-postcss-modules-scope@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06"
- integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==
- dependencies:
- postcss-selector-parser "^6.0.4"
-
-postcss-modules-values@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz#d7c5e7e68c3bb3c9b27cbf48ca0bb3ffb4602c9c"
- integrity sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==
- dependencies:
- icss-utils "^5.0.0"
-
-postcss-nested@5.0.6:
- version "5.0.6"
- resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.6.tgz#466343f7fc8d3d46af3e7dba3fcd47d052a945bc"
- integrity sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==
- dependencies:
- postcss-selector-parser "^6.0.6"
-
-postcss-nesting@^10.1.7:
- version "10.1.7"
- resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-10.1.7.tgz#0101bd6c7d386e7ad8e2e86ebcc0e0109833b86e"
- integrity sha512-Btho5XzDTpl117SmB3tvUHP8txg5n7Ayv7vQ5m4b1zXkfs1Y52C67uZjZ746h7QvOJ+rLRg50OlhhjFW+IQY6A==
- dependencies:
- "@csstools/selector-specificity" "1.0.0"
- postcss-selector-parser "^6.0.10"
-
-postcss-normalize-charset@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz#9302de0b29094b52c259e9b2cf8dc0879879f0ed"
- integrity sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==
-
-postcss-normalize-display-values@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz#72abbae58081960e9edd7200fcf21ab8325c3da8"
- integrity sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-positions@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-5.1.0.tgz#902a7cb97cf0b9e8b1b654d4a43d451e48966458"
- integrity sha512-8gmItgA4H5xiUxgN/3TVvXRoJxkAWLW6f/KKhdsH03atg0cB8ilXnrB5PpSshwVu/dD2ZsRFQcR1OEmSBDAgcQ==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-repeat-style@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.0.tgz#f6d6fd5a54f51a741cc84a37f7459e60ef7a6398"
- integrity sha512-IR3uBjc+7mcWGL6CtniKNQ4Rr5fTxwkaDHwMBDGGs1x9IVRkYIT/M4NelZWkAOBdV6v3Z9S46zqaKGlyzHSchw==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-string@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz#411961169e07308c82c1f8c55f3e8a337757e228"
- integrity sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-timing-functions@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz#d5614410f8f0b2388e9f240aa6011ba6f52dafbb"
- integrity sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-unicode@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.0.tgz#3d23aede35e160089a285e27bf715de11dc9db75"
- integrity sha512-J6M3MizAAZ2dOdSjy2caayJLQT8E8K9XjLce8AUQMwOrCvjCHv24aLC/Lps1R1ylOfol5VIDMaM/Lo9NGlk1SQ==
- dependencies:
- browserslist "^4.16.6"
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-url@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz#ed9d88ca82e21abef99f743457d3729a042adcdc"
- integrity sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==
- dependencies:
- normalize-url "^6.0.1"
- postcss-value-parser "^4.2.0"
-
-postcss-normalize-whitespace@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz#08a1a0d1ffa17a7cc6efe1e6c9da969cc4493cfa"
- integrity sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-normalize@^10.0.1:
- version "10.0.1"
- resolved "https://registry.yarnpkg.com/postcss-normalize/-/postcss-normalize-10.0.1.tgz#464692676b52792a06b06880a176279216540dd7"
- integrity sha512-+5w18/rDev5mqERcG3W5GZNMJa1eoYYNGo8gB7tEwaos0ajk3ZXAI4mHGcNT47NE+ZnZD1pEpUOFLvltIwmeJA==
- dependencies:
- "@csstools/normalize.css" "*"
- postcss-browser-comments "^4"
- sanitize.css "*"
-
-postcss-opacity-percentage@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz#bd698bb3670a0a27f6d657cc16744b3ebf3b1145"
- integrity sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==
-
-postcss-ordered-values@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.1.1.tgz#0b41b610ba02906a3341e92cab01ff8ebc598adb"
- integrity sha512-7lxgXF0NaoMIgyihL/2boNAEZKiW0+HkMhdKMTD93CjW8TdCy2hSdj8lsAo+uwm7EDG16Da2Jdmtqpedl0cMfw==
- dependencies:
- cssnano-utils "^3.1.0"
- postcss-value-parser "^4.2.0"
-
-postcss-overflow-shorthand@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.3.tgz#ebcfc0483a15bbf1b27fdd9b3c10125372f4cbc2"
- integrity sha512-CxZwoWup9KXzQeeIxtgOciQ00tDtnylYIlJBBODqkgS/PU2jISuWOL/mYLHmZb9ZhZiCaNKsCRiLp22dZUtNsg==
-
-postcss-page-break@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/postcss-page-break/-/postcss-page-break-3.0.4.tgz#7fbf741c233621622b68d435babfb70dd8c1ee5f"
- integrity sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==
-
-postcss-place@^7.0.4:
- version "7.0.4"
- resolved "https://registry.yarnpkg.com/postcss-place/-/postcss-place-7.0.4.tgz#eb026650b7f769ae57ca4f938c1addd6be2f62c9"
- integrity sha512-MrgKeiiu5OC/TETQO45kV3npRjOFxEHthsqGtkh3I1rPbZSbXGD/lZVi9j13cYh+NA8PIAPyk6sGjT9QbRyvSg==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-preset-env@^7.0.1:
- version "7.7.0"
- resolved "https://registry.yarnpkg.com/postcss-preset-env/-/postcss-preset-env-7.7.0.tgz#bcc9be9725a85d34e72a8fa69dc5e1130abee301"
- integrity sha512-2Q9YARQju+j2BVgAyDnW1pIWIMlaHZqbaGISPMmalznNlWcNFIZFQsJfRLXS+WHmHJDCmV7wIWpVf9JNKR4Elw==
- dependencies:
- "@csstools/postcss-cascade-layers" "^1.0.2"
- "@csstools/postcss-color-function" "^1.1.0"
- "@csstools/postcss-font-format-keywords" "^1.0.0"
- "@csstools/postcss-hwb-function" "^1.0.1"
- "@csstools/postcss-ic-unit" "^1.0.0"
- "@csstools/postcss-is-pseudo-class" "^2.0.4"
- "@csstools/postcss-normalize-display-values" "^1.0.0"
- "@csstools/postcss-oklab-function" "^1.1.0"
- "@csstools/postcss-progressive-custom-properties" "^1.3.0"
- "@csstools/postcss-stepped-value-functions" "^1.0.0"
- "@csstools/postcss-trigonometric-functions" "^1.0.0"
- "@csstools/postcss-unset-value" "^1.0.1"
- autoprefixer "^10.4.7"
- browserslist "^4.20.3"
- css-blank-pseudo "^3.0.3"
- css-has-pseudo "^3.0.4"
- css-prefers-color-scheme "^6.0.3"
- cssdb "^6.6.2"
- postcss-attribute-case-insensitive "^5.0.0"
- postcss-clamp "^4.1.0"
- postcss-color-functional-notation "^4.2.3"
- postcss-color-hex-alpha "^8.0.3"
- postcss-color-rebeccapurple "^7.0.2"
- postcss-custom-media "^8.0.0"
- postcss-custom-properties "^12.1.7"
- postcss-custom-selectors "^6.0.0"
- postcss-dir-pseudo-class "^6.0.4"
- postcss-double-position-gradients "^3.1.1"
- postcss-env-function "^4.0.6"
- postcss-focus-visible "^6.0.4"
- postcss-focus-within "^5.0.4"
- postcss-font-variant "^5.0.0"
- postcss-gap-properties "^3.0.3"
- postcss-image-set-function "^4.0.6"
- postcss-initial "^4.0.1"
- postcss-lab-function "^4.2.0"
- postcss-logical "^5.0.4"
- postcss-media-minmax "^5.0.0"
- postcss-nesting "^10.1.7"
- postcss-opacity-percentage "^1.1.2"
- postcss-overflow-shorthand "^3.0.3"
- postcss-page-break "^3.0.4"
- postcss-place "^7.0.4"
- postcss-pseudo-class-any-link "^7.1.4"
- postcss-replace-overflow-wrap "^4.0.0"
- postcss-selector-not "^5.0.0"
- postcss-value-parser "^4.2.0"
-
-postcss-pseudo-class-any-link@^7.1.4:
- version "7.1.4"
- resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.4.tgz#ac72aac4fe11fc4a0a368691f8fd5fe89e95aba4"
- integrity sha512-JxRcLXm96u14N3RzFavPIE9cRPuOqLDuzKeBsqi4oRk4vt8n0A7I0plFs/VXTg7U2n7g/XkQi0OwqTO3VWBfEg==
- dependencies:
- postcss-selector-parser "^6.0.10"
-
-postcss-reduce-initial@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.0.tgz#fc31659ea6e85c492fb2a7b545370c215822c5d6"
- integrity sha512-5OgTUviz0aeH6MtBjHfbr57tml13PuedK/Ecg8szzd4XRMbYxH4572JFG067z+FqBIf6Zp/d+0581glkvvWMFw==
- dependencies:
- browserslist "^4.16.6"
- caniuse-api "^3.0.0"
-
-postcss-reduce-transforms@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz#333b70e7758b802f3dd0ddfe98bb1ccfef96b6e9"
- integrity sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==
- dependencies:
- postcss-value-parser "^4.2.0"
-
-postcss-replace-overflow-wrap@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz#d2df6bed10b477bf9c52fab28c568b4b29ca4319"
- integrity sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==
-
-postcss-selector-not@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-5.0.0.tgz#ac5fc506f7565dd872f82f5314c0f81a05630dc7"
- integrity sha512-/2K3A4TCP9orP4TNS7u3tGdRFVKqz/E6pX3aGnriPG0jU78of8wsUcqE4QAhWEU0d+WnMSF93Ah3F//vUtK+iQ==
- dependencies:
- balanced-match "^1.0.0"
-
-postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.6, postcss-selector-parser@^6.0.9:
- version "6.0.10"
- resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
- integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
- dependencies:
- cssesc "^3.0.0"
- util-deprecate "^1.0.2"
-
-postcss-svgo@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.1.0.tgz#0a317400ced789f233a28826e77523f15857d80d"
- integrity sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==
- dependencies:
- postcss-value-parser "^4.2.0"
- svgo "^2.7.0"
-
-postcss-unique-selectors@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz#a9f273d1eacd09e9aa6088f4b0507b18b1b541b6"
- integrity sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==
- dependencies:
- postcss-selector-parser "^6.0.5"
-
-postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
- integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
-
-postcss@^7.0.35:
- version "7.0.39"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309"
- integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==
- dependencies:
- picocolors "^0.2.1"
- source-map "^0.6.1"
-
-postcss@^8.3.5, postcss@^8.4.12, postcss@^8.4.4, postcss@^8.4.7:
- version "8.4.14"
- resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf"
- integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
- dependencies:
- nanoid "^3.3.4"
- picocolors "^1.0.0"
- source-map-js "^1.0.2"
-
-prelude-ls@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
- integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
-
-prelude-ls@~1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
- integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==
-
-pretty-bytes@^5.3.0, pretty-bytes@^5.4.1:
- version "5.6.0"
- resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
- integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
-
-pretty-error@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-4.0.0.tgz#90a703f46dd7234adb46d0f84823e9d1cb8f10d6"
- integrity sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==
- dependencies:
- lodash "^4.17.20"
- renderkid "^3.0.0"
-
-pretty-format@^27.5.1:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
- integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
- dependencies:
- ansi-regex "^5.0.1"
- ansi-styles "^5.0.0"
- react-is "^17.0.1"
-
-pretty-format@^28.1.0:
- version "28.1.0"
- resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.0.tgz#8f5836c6a0dfdb834730577ec18029052191af55"
- integrity sha512-79Z4wWOYCdvQkEoEuSlBhHJqWeZ8D8YRPiPctJFCtvuaClGpiwiQYSCUOE6IEKUbbFukKOTFIUAXE8N4EQTo1Q==
- dependencies:
- "@jest/schemas" "^28.0.2"
- ansi-regex "^5.0.1"
- ansi-styles "^5.0.0"
- react-is "^18.0.0"
-
-process-nextick-args@~2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
- integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
-
-promise@^8.1.0:
- version "8.1.0"
- resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e"
- integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==
- dependencies:
- asap "~2.0.6"
-
-prompts@^2.0.1, prompts@^2.4.2:
- version "2.4.2"
- resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
- integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==
- dependencies:
- kleur "^3.0.3"
- sisteransi "^1.0.5"
-
-prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
- version "15.8.1"
- resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
- integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
- dependencies:
- loose-envify "^1.4.0"
- object-assign "^4.1.1"
- react-is "^16.13.1"
-
-property-information@^6.0.0:
- version "6.1.1"
- resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.1.1.tgz#5ca85510a3019726cb9afed4197b7b8ac5926a22"
- integrity sha512-hrzC564QIl0r0vy4l6MvRLhafmUowhO/O3KgVSoXIbbA2Sz4j8HGpJc6T2cubRVwMwpdiG/vKGfhT4IixmKN9w==
-
-prosemirror-commands@^1.2.2:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.3.0.tgz#361b2e2b2a347ce7453386459f97c3f549a1113b"
- integrity sha512-BwBbZ5OAScPcm0x7H8SPbqjuEJnCU2RJT9LDyOiiIl/3NbL1nJZI4SFNHwU2e/tRr2Xe7JsptpzseqvZvToLBQ==
- dependencies:
- prosemirror-model "^1.0.0"
- prosemirror-state "^1.0.0"
- prosemirror-transform "^1.0.0"
-
-prosemirror-dropcursor@^1.3.5:
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.5.0.tgz#edbc61d6f71f9f924130eec8e85b0861357957c9"
- integrity sha512-vy7i77ddKyXlu8kKBB3nlxLBnsWyKUmQIPB5x8RkYNh01QNp/qqGmdd5yZefJs0s3rtv5r7Izfu2qbtr+tYAMQ==
- dependencies:
- prosemirror-state "^1.0.0"
- prosemirror-transform "^1.1.0"
- prosemirror-view "^1.1.0"
-
-prosemirror-gapcursor@^1.1.5:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.0.tgz#e07c22ad959b86ec0c4cfc590cc5f484dd984d56"
- integrity sha512-9Tdx83xB2W4Oqchm12FtCkSizbqvi64cjs1I9TRPblqdA5TUWoVZ4ZI+t71Jh6HSEh4cDMPzx3UwfryJtKlb/w==
- dependencies:
- prosemirror-keymap "^1.0.0"
- prosemirror-model "^1.0.0"
- prosemirror-state "^1.0.0"
- prosemirror-view "^1.0.0"
-
-prosemirror-history@^1.1.3:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.3.0.tgz#bf5a1ff7759aca759ddf0c722c2fa5b14fb0ddc1"
- integrity sha512-qo/9Wn4B/Bq89/YD+eNWFbAytu6dmIM85EhID+fz9Jcl9+DfGEo8TTSrRhP15+fFEoaPqpHSxlvSzSEbmlxlUA==
- dependencies:
- prosemirror-state "^1.2.2"
- prosemirror-transform "^1.0.0"
- rope-sequence "^1.3.0"
-
-prosemirror-inputrules@^1.1.3:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/prosemirror-inputrules/-/prosemirror-inputrules-1.2.0.tgz#476dde2dc244050b3aca00cf58a82adfad6749e7"
- integrity sha512-eAW/M/NTSSzpCOxfR8Abw6OagdG0MiDAiWHQMQveIsZtoKVYzm0AflSPq/ymqJd56/Su1YPbwy9lM13wgHOFmQ==
- dependencies:
- prosemirror-state "^1.0.0"
- prosemirror-transform "^1.0.0"
-
-prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.1.5:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.2.0.tgz#d5cc9da9b712020690a994b50b92a0e448a60bf5"
- integrity sha512-TdSfu+YyLDd54ufN/ZeD1VtBRYpgZnTPnnbY+4R08DDgs84KrIPEPbJL8t1Lm2dkljFx6xeBE26YWH3aIzkPKg==
- dependencies:
- prosemirror-state "^1.0.0"
- w3c-keyname "^2.2.0"
-
-prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.16.1, prosemirror-model@^1.8.1:
- version "1.17.0"
- resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.17.0.tgz#f99fe93e9b45e48559ebf0f50262ef900a6246b7"
- integrity sha512-RJBDgZs/W26yyx1itrk5b3H9FxIro3K7Xjc2QWJI99Gu1nxYAnIggqI3fIOD8Jd/6QZfM+t6elZFJPycVexMTA==
- dependencies:
- orderedmap "^1.1.0"
-
-prosemirror-schema-list@^1.1.6:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.2.0.tgz#1932268593a7396c0ac168cbe31f28187406ce24"
- integrity sha512-8PT/9xOx1HHdC7fDNNfhQ50Z8Mzu7nKyA1KCDltSpcZVZIbB0k7KtsHrnXyuIhbLlScoymBiLZ00c5MH6wdFsA==
- dependencies:
- prosemirror-model "^1.0.0"
- prosemirror-state "^1.0.0"
- prosemirror-transform "^1.0.0"
-
-prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.3.4:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.4.0.tgz#0b0c23b38d9f5bb23d19ad043cf8453d876c0414"
- integrity sha512-mVDZdjNX/YT5FvypiwbphJe9psA5h+j9apsSszVRFc6oKFoIInvzdujh8QW9f9lwHtSYajLxNiM1hPhd0Sl1XA==
- dependencies:
- prosemirror-model "^1.0.0"
- prosemirror-transform "^1.0.0"
-
-prosemirror-tables@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.1.1.tgz#ad66300cc49500455cf1243bb129c9e7d883321e"
- integrity sha512-LmCz4jrlqQZRsYRDzCRYf/pQ5CUcSOyqZlAj5kv67ZWBH1SVLP2U9WJEvQfimWgeRlIz0y0PQVqO1arRm1+woA==
- dependencies:
- prosemirror-keymap "^1.1.2"
- prosemirror-model "^1.8.1"
- prosemirror-state "^1.3.1"
- prosemirror-transform "^1.2.1"
- prosemirror-view "^1.13.3"
-
-prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.4.2:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.6.0.tgz#8162dbfaf124f9253a7ab28605a9460411a96a53"
- integrity sha512-MAp7AjsjEGEqQY0sSMufNIUuEyB1ZR9Fqlm8dTwwWwpEJRv/plsKjWXBbx52q3Ml8MtaMcd7ic14zAHVB3WaMw==
- dependencies:
- prosemirror-model "^1.0.0"
-
-prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.23.12:
- version "1.25.0"
- resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.25.0.tgz#a0d7fc427c41276bab643369d2f37eb6dd3addb6"
- integrity sha512-9eJ7VYOqUl/l2P3Q126PoUhfrWAGF0GU4zHXZssbVnhqLZBKpHTcTYx1W9DMg1PCuS69sHLMJdm3UFHmD5SGdw==
- dependencies:
- prosemirror-model "^1.16.0"
- prosemirror-state "^1.0.0"
- prosemirror-transform "^1.1.0"
-
-proxy-addr@~2.0.7:
- version "2.0.7"
- resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
- integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
- dependencies:
- forwarded "0.2.0"
- ipaddr.js "1.9.1"
-
-prr@~1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
- integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==
-
-psl@^1.1.33:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
- integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
-
-punycode@^2.1.0, punycode@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
- integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
-
-q@^1.1.2:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
- integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==
-
-qs@6.10.3:
- version "6.10.3"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
- integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
- dependencies:
- side-channel "^1.0.4"
-
-queue-microtask@^1.2.2:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
- integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
-
-quick-lru@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
- integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
-
-raf@^3.4.1:
- version "3.4.1"
- resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
- integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
- dependencies:
- performance-now "^2.1.0"
-
-randombytes@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
- integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
- dependencies:
- safe-buffer "^5.1.0"
-
-range-parser@^1.2.1, range-parser@~1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
- integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
-
-raw-body@2.5.1:
- version "2.5.1"
- resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857"
- integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
- dependencies:
- bytes "3.1.2"
- http-errors "2.0.0"
- iconv-lite "0.4.24"
- unpipe "1.0.0"
-
-react-app-polyfill@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz#95221e0a9bd259e5ca6b177c7bb1cb6768f68fd7"
- integrity sha512-sZ41cxiU5llIB003yxxQBYrARBqe0repqPTTYBTmMqTz9szeBbE37BehCE891NZsmdZqqP+xWKdT3eo3vOzN8w==
- dependencies:
- core-js "^3.19.2"
- object-assign "^4.1.1"
- promise "^8.1.0"
- raf "^3.4.1"
- regenerator-runtime "^0.13.9"
- whatwg-fetch "^3.6.2"
-
-react-app-rewired@^2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/react-app-rewired/-/react-app-rewired-2.2.1.tgz#84901ee1e3f26add0377ebec0b41bcdfce9fc211"
- integrity sha512-uFQWTErXeLDrMzOJHKp0h8P1z0LV9HzPGsJ6adOtGlA/B9WfT6Shh4j2tLTTGlXOfiVx6w6iWpp7SOC5pvk+gA==
- dependencies:
- semver "^5.6.0"
-
-react-dev-utils@^12.0.1:
- version "12.0.1"
- resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-12.0.1.tgz#ba92edb4a1f379bd46ccd6bcd4e7bc398df33e73"
- integrity sha512-84Ivxmr17KjUupyqzFode6xKhjwuEJDROWKJy/BthkL7Wn6NJ8h4WE6k/exAv6ImS+0oZLRRW5j/aINMHyeGeQ==
- dependencies:
- "@babel/code-frame" "^7.16.0"
- address "^1.1.2"
- browserslist "^4.18.1"
- chalk "^4.1.2"
- cross-spawn "^7.0.3"
- detect-port-alt "^1.1.6"
- escape-string-regexp "^4.0.0"
- filesize "^8.0.6"
- find-up "^5.0.0"
- fork-ts-checker-webpack-plugin "^6.5.0"
- global-modules "^2.0.0"
- globby "^11.0.4"
- gzip-size "^6.0.0"
- immer "^9.0.7"
- is-root "^2.1.0"
- loader-utils "^3.2.0"
- open "^8.4.0"
- pkg-up "^3.1.0"
- prompts "^2.4.2"
- react-error-overlay "^6.0.11"
- recursive-readdir "^2.2.2"
- shell-quote "^1.7.3"
- strip-ansi "^6.0.1"
- text-table "^0.2.0"
-
-react-dom@17.0.2:
- version "17.0.2"
- resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23"
- integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==
- dependencies:
- loose-envify "^1.1.0"
- object-assign "^4.1.1"
- scheduler "^0.20.2"
-
-react-error-overlay@6.0.9, react-error-overlay@^6.0.11:
- version "6.0.9"
- resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a"
- integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==
-
-react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0:
- version "16.13.1"
- resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
- integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
-
-react-is@^17.0.1, react-is@^17.0.2:
- version "17.0.2"
- resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
- integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-
-react-is@^18.0.0:
- version "18.1.0"
- resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.1.0.tgz#61aaed3096d30eacf2a2127118b5b41387d32a67"
- integrity sha512-Fl7FuabXsJnV5Q1qIOQwx/sagGF18kogb4gpfcG4gjLBWO0WDiiz1ko/ExayuxE7InyQkBLkxRFG5oxY6Uu3Kg==
-
-react-redux@^7.2.6:
- version "7.2.8"
- resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.8.tgz#a894068315e65de5b1b68899f9c6ee0923dd28de"
- integrity sha512-6+uDjhs3PSIclqoCk0kd6iX74gzrGc3W5zcAjbrFgEdIjRSQObdIwfx80unTkVUYvbQ95Y8Av3OvFHq1w5EOUw==
- dependencies:
- "@babel/runtime" "^7.15.4"
- "@types/react-redux" "^7.1.20"
- hoist-non-react-statics "^3.3.2"
- loose-envify "^1.4.0"
- prop-types "^15.7.2"
- react-is "^17.0.2"
-
-react-refresh@^0.11.0:
- version "0.11.0"
- resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
- integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==
-
-react-router-dom@^5.3.0:
- version "5.3.3"
- resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.3.3.tgz#8779fc28e6691d07afcaf98406d3812fe6f11199"
- integrity sha512-Ov0tGPMBgqmbu5CDmN++tv2HQ9HlWDuWIIqn4b88gjlAN5IHI+4ZUZRcpz9Hl0azFIwihbLDYw1OiHGRo7ZIng==
- dependencies:
- "@babel/runtime" "^7.12.13"
- history "^4.9.0"
- loose-envify "^1.3.1"
- prop-types "^15.6.2"
- react-router "5.3.3"
- tiny-invariant "^1.0.2"
- tiny-warning "^1.0.0"
-
-react-router@5.3.3:
- version "5.3.3"
- resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.3.3.tgz#8e3841f4089e728cf82a429d92cdcaa5e4a3a288"
- integrity sha512-mzQGUvS3bM84TnbtMYR8ZjKnuPJ71IjSzR+DE6UkUqvN4czWIqEs17yLL8xkAycv4ev0AiN+IGrWu88vJs/p2w==
- dependencies:
- "@babel/runtime" "^7.12.13"
- history "^4.9.0"
- hoist-non-react-statics "^3.1.0"
- loose-envify "^1.3.1"
- mini-create-react-context "^0.4.0"
- path-to-regexp "^1.7.0"
- prop-types "^15.6.2"
- react-is "^16.6.0"
- tiny-invariant "^1.0.2"
- tiny-warning "^1.0.0"
-
-react-scripts@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-5.0.1.tgz#6285dbd65a8ba6e49ca8d651ce30645a6d980003"
- integrity sha512-8VAmEm/ZAwQzJ+GOMLbBsTdDKOpuZh7RPs0UymvBR2vRk4iZWCskjbFnxqjrzoIvlNNRZ3QJFx6/qDSi6zSnaQ==
- dependencies:
- "@babel/core" "^7.16.0"
- "@pmmmwh/react-refresh-webpack-plugin" "^0.5.3"
- "@svgr/webpack" "^5.5.0"
- babel-jest "^27.4.2"
- babel-loader "^8.2.3"
- babel-plugin-named-asset-import "^0.3.8"
- babel-preset-react-app "^10.0.1"
- bfj "^7.0.2"
- browserslist "^4.18.1"
- camelcase "^6.2.1"
- case-sensitive-paths-webpack-plugin "^2.4.0"
- css-loader "^6.5.1"
- css-minimizer-webpack-plugin "^3.2.0"
- dotenv "^10.0.0"
- dotenv-expand "^5.1.0"
- eslint "^8.3.0"
- eslint-config-react-app "^7.0.1"
- eslint-webpack-plugin "^3.1.1"
- file-loader "^6.2.0"
- fs-extra "^10.0.0"
- html-webpack-plugin "^5.5.0"
- identity-obj-proxy "^3.0.0"
- jest "^27.4.3"
- jest-resolve "^27.4.2"
- jest-watch-typeahead "^1.0.0"
- mini-css-extract-plugin "^2.4.5"
- postcss "^8.4.4"
- postcss-flexbugs-fixes "^5.0.2"
- postcss-loader "^6.2.1"
- postcss-normalize "^10.0.1"
- postcss-preset-env "^7.0.1"
- prompts "^2.4.2"
- react-app-polyfill "^3.0.0"
- react-dev-utils "^12.0.1"
- react-refresh "^0.11.0"
- resolve "^1.20.0"
- resolve-url-loader "^4.0.0"
- sass-loader "^12.3.0"
- semver "^7.3.5"
- source-map-loader "^3.0.0"
- style-loader "^3.3.1"
- tailwindcss "^3.0.2"
- terser-webpack-plugin "^5.2.5"
- webpack "^5.64.4"
- webpack-dev-server "^4.6.0"
- webpack-manifest-plugin "^4.0.2"
- workbox-webpack-plugin "^6.4.1"
- optionalDependencies:
- fsevents "^2.3.2"
-
-react@17.0.2:
- version "17.0.2"
- resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037"
- integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==
- dependencies:
- loose-envify "^1.1.0"
- object-assign "^4.1.1"
-
-readable-stream@^2.0.1:
- version "2.3.7"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
- integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
- dependencies:
- core-util-is "~1.0.0"
- inherits "~2.0.3"
- isarray "~1.0.0"
- process-nextick-args "~2.0.0"
- safe-buffer "~5.1.1"
- string_decoder "~1.1.1"
- util-deprecate "~1.0.1"
-
-readable-stream@^3.0.6:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
- integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
- dependencies:
- inherits "^2.0.3"
- string_decoder "^1.1.1"
- util-deprecate "^1.0.1"
-
-readdirp@~3.6.0:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
- integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
- dependencies:
- picomatch "^2.2.1"
-
-recursive-readdir@^2.2.2:
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f"
- integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg==
- dependencies:
- minimatch "3.0.4"
-
-redux-thunk@^2.4.1:
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714"
- integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q==
-
-redux@^4.0.0, redux@^4.1.2:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.0.tgz#46f10d6e29b6666df758780437651eeb2b969f13"
- integrity sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==
- dependencies:
- "@babel/runtime" "^7.9.2"
-
-refractor@^4.0.0:
- version "4.7.0"
- resolved "https://registry.yarnpkg.com/refractor/-/refractor-4.7.0.tgz#aad649d7857acdc0d5792f1a7900867256941ac0"
- integrity sha512-X3JUDE7nq1csWs7Etg5v7hW10RzF4lYesEn/KDbllocj0itZrs3paO2ZEgYUXrlgXzY3IN+eDRByyIvzcfF9Tg==
- dependencies:
- "@types/hast" "^2.0.0"
- "@types/prismjs" "^1.0.0"
- hastscript "^7.0.0"
- parse-entities "^4.0.0"
-
-regenerate-unicode-properties@^10.0.1:
- version "10.0.1"
- resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56"
- integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==
- dependencies:
- regenerate "^1.4.2"
-
-regenerate@^1.4.2:
- version "1.4.2"
- resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
- integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
-
-regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.9:
- version "0.13.9"
- resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
- integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
-
-regenerator-transform@^0.15.0:
- version "0.15.0"
- resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537"
- integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==
- dependencies:
- "@babel/runtime" "^7.8.4"
-
-regex-parser@^2.2.11:
- version "2.2.11"
- resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58"
- integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==
-
-regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3:
- version "1.4.3"
- resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac"
- integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
- functions-have-names "^1.2.2"
-
-regexpp@^3.2.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
- integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
-
-regexpu-core@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3"
- integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==
- dependencies:
- regenerate "^1.4.2"
- regenerate-unicode-properties "^10.0.1"
- regjsgen "^0.6.0"
- regjsparser "^0.8.2"
- unicode-match-property-ecmascript "^2.0.0"
- unicode-match-property-value-ecmascript "^2.0.0"
-
-regjsgen@^0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d"
- integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==
-
-regjsparser@^0.8.2:
- version "0.8.4"
- resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f"
- integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==
- dependencies:
- jsesc "~0.5.0"
-
-relateurl@^0.2.7:
- version "0.2.7"
- resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
- integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
-
-remark-directive@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/remark-directive/-/remark-directive-2.0.1.tgz#1c32d9df8d839a75ba3478112d21fe883635b48e"
- integrity sha512-oosbsUAkU/qmUE78anLaJePnPis4ihsE7Agp0T/oqTzvTea8pOiaYEtfInU/+xMOVTS9PN5AhGOiaIVe4GD8gw==
- dependencies:
- "@types/mdast" "^3.0.0"
- mdast-util-directive "^2.0.0"
- micromark-extension-directive "^2.0.0"
- unified "^10.0.0"
-
-remark-emoji@^3.0.1:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/remark-emoji/-/remark-emoji-3.0.2.tgz#786e88af1ecae682d74d7e1219989f34708205da"
- integrity sha512-hEgxEv2sBtvhT3tNG/tQeeFY3EbslftaOoG14dDZndLo25fWJ6Fbg4ukFbIotOWWrfXyASjXjyHT+6n366k3mg==
- dependencies:
- emoticon "^4.0.0"
- node-emoji "^1.11.0"
- unist-util-visit "^4.1.0"
-
-remark-gfm@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/remark-gfm/-/remark-gfm-3.0.1.tgz#0b180f095e3036545e9dddac0e8df3fa5cfee54f"
- integrity sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==
- dependencies:
- "@types/mdast" "^3.0.0"
- mdast-util-gfm "^2.0.0"
- micromark-extension-gfm "^2.0.0"
- unified "^10.0.0"
-
-remark-inline-links@^6.0.0:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/remark-inline-links/-/remark-inline-links-6.0.1.tgz#83ef8d4fc91aff04c8c8fa209104a8cac07c8724"
- integrity sha512-etdk1A0kRs+bXtT41XEFfyePOu583cmuHDF8bhAUfHJeCAPbPZpqmqZHD/wLhijIJV3ldjIvO4irM0jRGb1Dhg==
- dependencies:
- "@types/mdast" "^3.0.0"
- mdast-util-definitions "^5.0.0"
- unified "^10.0.0"
- unist-util-visit "^4.0.0"
-
-remark-parse@^10.0.0:
- version "10.0.1"
- resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-10.0.1.tgz#6f60ae53edbf0cf38ea223fe643db64d112e0775"
- integrity sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw==
- dependencies:
- "@types/mdast" "^3.0.0"
- mdast-util-from-markdown "^1.0.0"
- unified "^10.0.0"
-
-remark-stringify@^10.0.0:
- version "10.0.2"
- resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-10.0.2.tgz#50414a6983f5008eb9e72eed05f980582d1f69d7"
- integrity sha512-6wV3pvbPvHkbNnWB0wdDvVFHOe1hBRAx1Q/5g/EpH4RppAII6J8Gnwe7VbHuXaoKIF6LAg6ExTel/+kNqSQ7lw==
- dependencies:
- "@types/mdast" "^3.0.0"
- mdast-util-to-markdown "^1.0.0"
- unified "^10.0.0"
-
-remark@^14.0.1:
- version "14.0.2"
- resolved "https://registry.yarnpkg.com/remark/-/remark-14.0.2.tgz#4a1833f7441a5c29e44b37bb1843fb820797b40f"
- integrity sha512-A3ARm2V4BgiRXaUo5K0dRvJ1lbogrbXnhkJRmD0yw092/Yl0kOCZt1k9ZeElEwkZsWGsMumz6qL5MfNJH9nOBA==
- dependencies:
- "@types/mdast" "^3.0.0"
- remark-parse "^10.0.0"
- remark-stringify "^10.0.0"
- unified "^10.0.0"
-
-renderkid@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-3.0.0.tgz#5fd823e4d6951d37358ecc9a58b1f06836b6268a"
- integrity sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==
- dependencies:
- css-select "^4.1.3"
- dom-converter "^0.2.0"
- htmlparser2 "^6.1.0"
- lodash "^4.17.21"
- strip-ansi "^6.0.1"
-
-require-directory@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
- integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
-
-require-from-string@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
- integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
-
-requires-port@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
- integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
-
-reselect@^4.1.5:
- version "4.1.5"
- resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6"
- integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==
-
-resolve-cwd@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
- integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
- dependencies:
- resolve-from "^5.0.0"
-
-resolve-from@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
- integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
-
-resolve-from@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
- integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
-
-resolve-pathname@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd"
- integrity sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==
-
-resolve-url-loader@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz#d50d4ddc746bb10468443167acf800dcd6c3ad57"
- integrity sha512-05VEMczVREcbtT7Bz+C+96eUO5HDNvdthIiMB34t7FcF8ehcu4wC0sSgPUubs3XW2Q3CNLJk/BJrCU9wVRymiA==
- dependencies:
- adjust-sourcemap-loader "^4.0.0"
- convert-source-map "^1.7.0"
- loader-utils "^2.0.0"
- postcss "^7.0.35"
- source-map "0.6.1"
-
-resolve.exports@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9"
- integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==
-
-resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.0:
- version "1.22.0"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
- integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
- dependencies:
- is-core-module "^2.8.1"
- path-parse "^1.0.7"
- supports-preserve-symlinks-flag "^1.0.0"
-
-resolve@^2.0.0-next.3:
- version "2.0.0-next.3"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46"
- integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==
- dependencies:
- is-core-module "^2.2.0"
- path-parse "^1.0.6"
-
-retry@^0.13.1:
- version "0.13.1"
- resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
- integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
-
-reusify@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
- integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
-
-rimraf@^3.0.0, rimraf@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
- integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
- dependencies:
- glob "^7.1.3"
-
-robust-predicates@^3.0.2:
- version "3.0.2"
- resolved "https://npm.shopee.io/robust-predicates/-/robust-predicates-3.0.2.tgz#d5b28528c4824d20fc48df1928d41d9efa1ad771"
- integrity sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==
-
-rollup-plugin-terser@^7.0.0:
- version "7.0.2"
- resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d"
- integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==
- dependencies:
- "@babel/code-frame" "^7.10.4"
- jest-worker "^26.2.1"
- serialize-javascript "^4.0.0"
- terser "^5.0.0"
-
-rollup@^2.43.1:
- version "2.75.5"
- resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.75.5.tgz#7985c1962483235dd07966f09fdad5c5f89f16d0"
- integrity sha512-JzNlJZDison3o2mOxVmb44Oz7t74EfSd1SQrplQk0wSaXV7uLQXtVdHbxlcT3w+8tZ1TL4r/eLfc7nAbz38BBA==
- optionalDependencies:
- fsevents "~2.3.2"
-
-rope-sequence@^1.3.0:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.3.tgz#3f67fc106288b84b71532b4a5fd9d4881e4457f0"
- integrity sha512-85aZYCxweiD5J8yTEbw+E6A27zSnLPNDL0WfPdw3YYodq7WjnTKo0q4dtyQ2gz23iPT8Q9CUyJtAaUNcTxRf5Q==
-
-run-parallel@^1.1.9:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
- integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
- dependencies:
- queue-microtask "^1.2.2"
-
-rw@1:
- version "1.3.3"
- resolved "https://npm.shopee.io/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
- integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=
-
-sade@^1.7.3:
- version "1.8.1"
- resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701"
- integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==
- dependencies:
- mri "^1.1.0"
-
-safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
- version "5.1.2"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
- integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
-
-safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
- version "5.2.1"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
- integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
-
-"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
- integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
-
-sanitize.css@*:
- version "13.0.0"
- resolved "https://registry.yarnpkg.com/sanitize.css/-/sanitize.css-13.0.0.tgz#2675553974b27964c75562ade3bd85d79879f173"
- integrity sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==
-
-sass-loader@^12.3.0:
- version "12.6.0"
- resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-12.6.0.tgz#5148362c8e2cdd4b950f3c63ac5d16dbfed37bcb"
- integrity sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==
- dependencies:
- klona "^2.0.4"
- neo-async "^2.6.2"
-
-sax@^1.2.4, sax@~1.2.4:
- version "1.2.4"
- resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
- integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
-
-saxes@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d"
- integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==
- dependencies:
- xmlchars "^2.2.0"
-
-scheduler@^0.20.2:
- version "0.20.2"
- resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91"
- integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==
- dependencies:
- loose-envify "^1.1.0"
- object-assign "^4.1.1"
-
-schema-utils@2.7.0:
- version "2.7.0"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.0.tgz#17151f76d8eae67fbbf77960c33c676ad9f4efc7"
- integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==
- dependencies:
- "@types/json-schema" "^7.0.4"
- ajv "^6.12.2"
- ajv-keywords "^3.4.1"
-
-schema-utils@^2.6.5:
- version "2.7.1"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"
- integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==
- dependencies:
- "@types/json-schema" "^7.0.5"
- ajv "^6.12.4"
- ajv-keywords "^3.5.2"
-
-schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
- integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
- dependencies:
- "@types/json-schema" "^7.0.8"
- ajv "^6.12.5"
- ajv-keywords "^3.5.2"
-
-schema-utils@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.0.tgz#60331e9e3ae78ec5d16353c467c34b3a0a1d3df7"
- integrity sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==
- dependencies:
- "@types/json-schema" "^7.0.9"
- ajv "^8.8.0"
- ajv-formats "^2.1.1"
- ajv-keywords "^5.0.0"
-
-scroll-into-view-if-needed@^2.2.28:
- version "2.2.29"
- resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885"
- integrity sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg==
- dependencies:
- compute-scroll-into-view "^1.0.17"
-
-select-hose@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
- integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=
-
-select@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
- integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
-
-selfsigned@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.0.1.tgz#8b2df7fa56bf014d19b6007655fff209c0ef0a56"
- integrity sha512-LmME957M1zOsUhG+67rAjKfiWFox3SBxE/yymatMZsAx+oMrJ0YQ8AToOnyCm7xbeg2ep37IHLxdu0o2MavQOQ==
- dependencies:
- node-forge "^1"
-
-semver@7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
- integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
-
-semver@^5.6.0:
- version "5.7.1"
- resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
- integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
-
-semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
- version "6.3.0"
- resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
- integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
-
-semver@^7.3.2, semver@^7.3.5, semver@^7.3.7:
- version "7.3.7"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
- integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
- dependencies:
- lru-cache "^6.0.0"
-
-send@0.18.0:
- version "0.18.0"
- resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be"
- integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==
- dependencies:
- debug "2.6.9"
- depd "2.0.0"
- destroy "1.2.0"
- encodeurl "~1.0.2"
- escape-html "~1.0.3"
- etag "~1.8.1"
- fresh "0.5.2"
- http-errors "2.0.0"
- mime "1.6.0"
- ms "2.1.3"
- on-finished "2.4.1"
- range-parser "~1.2.1"
- statuses "2.0.1"
-
-serialize-javascript@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa"
- integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==
- dependencies:
- randombytes "^2.1.0"
-
-serialize-javascript@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
- integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
- dependencies:
- randombytes "^2.1.0"
-
-serve-index@^1.9.1:
- version "1.9.1"
- resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239"
- integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=
- dependencies:
- accepts "~1.3.4"
- batch "0.6.1"
- debug "2.6.9"
- escape-html "~1.0.3"
- http-errors "~1.6.2"
- mime-types "~2.1.17"
- parseurl "~1.3.2"
-
-serve-static@1.15.0:
- version "1.15.0"
- resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540"
- integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
- dependencies:
- encodeurl "~1.0.2"
- escape-html "~1.0.3"
- parseurl "~1.3.3"
- send "0.18.0"
-
-setprototypeof@1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
- integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
-
-setprototypeof@1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
- integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
-
-shebang-command@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
- integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
- dependencies:
- shebang-regex "^3.0.0"
-
-shebang-regex@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
- integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
-
-shell-quote@^1.7.3:
- version "1.7.3"
- resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123"
- integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==
-
-side-channel@^1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
- integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
- dependencies:
- call-bind "^1.0.0"
- get-intrinsic "^1.0.2"
- object-inspect "^1.9.0"
-
-signal-exit@^3.0.2, signal-exit@^3.0.3:
- version "3.0.7"
- resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
- integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
-
-sisteransi@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
- integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
-
-slash@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
- integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
-
-slash@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
- integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
-
-smooth-scroll-into-view-if-needed@^1.1.32:
- version "1.1.33"
- resolved "https://registry.yarnpkg.com/smooth-scroll-into-view-if-needed/-/smooth-scroll-into-view-if-needed-1.1.33.tgz#2c7b88c82784c69030cb0489b9df584e94e01533"
- integrity sha512-crS8NfAaoPrtVYOCMSAnO2vHRgUp22NiiDgEQ7YiaAy5xe2jmR19Jm+QdL8+97gO8ENd7PUyQIAQojJyIiyRHw==
- dependencies:
- scroll-into-view-if-needed "^2.2.28"
-
-sockjs@^0.3.24:
- version "0.3.24"
- resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce"
- integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==
- dependencies:
- faye-websocket "^0.11.3"
- uuid "^8.3.2"
- websocket-driver "^0.7.4"
-
-source-list-map@^2.0.0, source-list-map@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
- integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
-
-source-map-js@^1.0.1, source-map-js@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
- integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
-
-source-map-loader@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-3.0.1.tgz#9ae5edc7c2d42570934be4c95d1ccc6352eba52d"
- integrity sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA==
- dependencies:
- abab "^2.0.5"
- iconv-lite "^0.6.3"
- source-map-js "^1.0.1"
-
-source-map-support@^0.5.6, source-map-support@~0.5.20:
- version "0.5.21"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
- integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
- dependencies:
- buffer-from "^1.0.0"
- source-map "^0.6.0"
-
-source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
- version "0.6.1"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
- integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
-
-source-map@^0.5.7:
- version "0.5.7"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
- integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
-
-source-map@^0.7.3:
- version "0.7.3"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
- integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
-
-source-map@^0.8.0-beta.0:
- version "0.8.0-beta.0"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11"
- integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==
- dependencies:
- whatwg-url "^7.0.0"
-
-sourcemap-codec@^1.4.8:
- version "1.4.8"
- resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
- integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
-
-space-separated-tokens@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.1.tgz#43193cec4fb858a2ce934b7f98b7f2c18107098b"
- integrity sha512-ekwEbFp5aqSPKaqeY1PGrlGQxPNaq+Cnx4+bE2D8sciBQrHpbwoBbawqTN2+6jPs9IdWxxiUcN0K2pkczD3zmw==
-
-spdy-transport@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31"
- integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==
- dependencies:
- debug "^4.1.0"
- detect-node "^2.0.4"
- hpack.js "^2.1.6"
- obuf "^1.1.2"
- readable-stream "^3.0.6"
- wbuf "^1.7.3"
-
-spdy@^4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b"
- integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==
- dependencies:
- debug "^4.1.0"
- handle-thing "^2.0.0"
- http-deceiver "^1.2.7"
- select-hose "^2.0.0"
- spdy-transport "^3.0.0"
-
-sprintf-js@~1.0.2:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
- integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
-
-stable@^0.1.8:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
- integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
-
-stack-utils@^2.0.3:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5"
- integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==
- dependencies:
- escape-string-regexp "^2.0.0"
-
-stackframe@^1.1.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.1.tgz#1033a3473ee67f08e2f2fc8eba6aef4f845124e1"
- integrity sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg==
-
-statuses@2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
- integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
-
-"statuses@>= 1.4.0 < 2":
- version "1.5.0"
- resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
- integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
-
-string-length@^4.0.1:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
- integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==
- dependencies:
- char-regex "^1.0.2"
- strip-ansi "^6.0.0"
-
-string-length@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/string-length/-/string-length-5.0.1.tgz#3d647f497b6e8e8d41e422f7e0b23bc536c8381e"
- integrity sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==
- dependencies:
- char-regex "^2.0.0"
- strip-ansi "^7.0.1"
-
-string-natural-compare@^3.0.1:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4"
- integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw==
-
-string-width@^4.1.0, string-width@^4.2.0:
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
- integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
- dependencies:
- emoji-regex "^8.0.0"
- is-fullwidth-code-point "^3.0.0"
- strip-ansi "^6.0.1"
-
-string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.7:
- version "4.0.7"
- resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d"
- integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.3"
- es-abstract "^1.19.1"
- get-intrinsic "^1.1.1"
- has-symbols "^1.0.3"
- internal-slot "^1.0.3"
- regexp.prototype.flags "^1.4.1"
- side-channel "^1.0.4"
-
-string.prototype.trimend@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0"
- integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.4"
- es-abstract "^1.19.5"
-
-string.prototype.trimstart@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef"
- integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==
- dependencies:
- call-bind "^1.0.2"
- define-properties "^1.1.4"
- es-abstract "^1.19.5"
-
-string_decoder@^1.1.1:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
- integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
- dependencies:
- safe-buffer "~5.2.0"
-
-string_decoder@~1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
- integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
- dependencies:
- safe-buffer "~5.1.0"
-
-stringify-entities@^4.0.0:
- version "4.0.3"
- resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.3.tgz#cfabd7039d22ad30f3cc435b0ca2c1574fc88ef8"
- integrity sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==
- dependencies:
- character-entities-html4 "^2.0.0"
- character-entities-legacy "^3.0.0"
-
-stringify-object@^3.3.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629"
- integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==
- dependencies:
- get-own-enumerable-property-symbols "^3.0.0"
- is-obj "^1.0.1"
- is-regexp "^1.0.0"
-
-strip-ansi@^6.0.0, strip-ansi@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
- integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
- dependencies:
- ansi-regex "^5.0.1"
-
-strip-ansi@^7.0.1:
- version "7.0.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.0.1.tgz#61740a08ce36b61e50e65653f07060d000975fb2"
- integrity sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==
- dependencies:
- ansi-regex "^6.0.1"
-
-strip-bom@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
- integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
-
-strip-bom@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
- integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==
-
-strip-comments@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-2.0.1.tgz#4ad11c3fbcac177a67a40ac224ca339ca1c1ba9b"
- integrity sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==
-
-strip-final-newline@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
- integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
-
-strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
- integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
-
-style-loader@^3.3.1:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575"
- integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==
-
-style-mod@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.0.0.tgz#97e7c2d68b592975f2ca7a63d0dd6fcacfe35a01"
- integrity sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw==
-
-stylehacks@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-5.1.0.tgz#a40066490ca0caca04e96c6b02153ddc39913520"
- integrity sha512-SzLmvHQTrIWfSgljkQCw2++C9+Ne91d/6Sp92I8c5uHTcy/PgeHamwITIbBW9wnFTY/3ZfSXR9HIL6Ikqmcu6Q==
- dependencies:
- browserslist "^4.16.6"
- postcss-selector-parser "^6.0.4"
-
-stylis@4.0.13:
- version "4.0.13"
- resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91"
- integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==
-
-stylis@^4.1.2:
- version "4.3.5"
- resolved "https://npm.shopee.io/stylis/-/stylis-4.3.5.tgz#432cc99c81e28d7062c88d979d2163891e860489"
- integrity sha512-K7npNOKGRYuhAFFzkzMGfxFDpN6gDwf8hcMiE+uveTVbBgm93HrNP3ZDUpKqzZ4pG7TP6fmb+EMAQPjq9FqqvA==
-
-supports-color@^5.3.0:
- version "5.5.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
- integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
- dependencies:
- has-flag "^3.0.0"
-
-supports-color@^7.0.0, supports-color@^7.1.0:
- version "7.2.0"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
- integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
- dependencies:
- has-flag "^4.0.0"
-
-supports-color@^8.0.0:
- version "8.1.1"
- resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
- integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
- dependencies:
- has-flag "^4.0.0"
-
-supports-hyperlinks@^2.0.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz#4f77b42488765891774b70c79babd87f9bd594bb"
- integrity sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==
- dependencies:
- has-flag "^4.0.0"
- supports-color "^7.0.0"
-
-supports-preserve-symlinks-flag@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
- integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
-
-svg-parser@^2.0.2:
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5"
- integrity sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==
-
-svgo@^1.2.2:
- version "1.3.2"
- resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167"
- integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==
- dependencies:
- chalk "^2.4.1"
- coa "^2.0.2"
- css-select "^2.0.0"
- css-select-base-adapter "^0.1.1"
- css-tree "1.0.0-alpha.37"
- csso "^4.0.2"
- js-yaml "^3.13.1"
- mkdirp "~0.5.1"
- object.values "^1.1.0"
- sax "~1.2.4"
- stable "^0.1.8"
- unquote "~1.1.1"
- util.promisify "~1.0.0"
-
-svgo@^2.7.0:
- version "2.8.0"
- resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
- integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==
- dependencies:
- "@trysound/sax" "0.2.0"
- commander "^7.2.0"
- css-select "^4.1.3"
- css-tree "^1.1.3"
- csso "^4.2.0"
- picocolors "^1.0.0"
- stable "^0.1.8"
-
-symbol-tree@^3.2.4:
- version "3.2.4"
- resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
- integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
-
-tailwindcss@^3.0.2:
- version "3.0.24"
- resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.24.tgz#22e31e801a44a78a1d9a81ecc52e13b69d85704d"
- integrity sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig==
- dependencies:
- arg "^5.0.1"
- chokidar "^3.5.3"
- color-name "^1.1.4"
- detective "^5.2.0"
- didyoumean "^1.2.2"
- dlv "^1.1.3"
- fast-glob "^3.2.11"
- glob-parent "^6.0.2"
- is-glob "^4.0.3"
- lilconfig "^2.0.5"
- normalize-path "^3.0.0"
- object-hash "^3.0.0"
- picocolors "^1.0.0"
- postcss "^8.4.12"
- postcss-js "^4.0.0"
- postcss-load-config "^3.1.4"
- postcss-nested "5.0.6"
- postcss-selector-parser "^6.0.10"
- postcss-value-parser "^4.2.0"
- quick-lru "^5.1.1"
- resolve "^1.22.0"
-
-tapable@^1.0.0:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
- integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
-
-tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
- integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
-
-temp-dir@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-2.0.0.tgz#bde92b05bdfeb1516e804c9c00ad45177f31321e"
- integrity sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==
-
-tempy@^0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.6.0.tgz#65e2c35abc06f1124a97f387b08303442bde59f3"
- integrity sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==
- dependencies:
- is-stream "^2.0.0"
- temp-dir "^2.0.0"
- type-fest "^0.16.0"
- unique-string "^2.0.0"
-
-terminal-link@^2.0.0:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994"
- integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==
- dependencies:
- ansi-escapes "^4.2.1"
- supports-hyperlinks "^2.0.0"
-
-terser-webpack-plugin@^5.1.3, terser-webpack-plugin@^5.2.5:
- version "5.3.3"
- resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz#8033db876dd5875487213e87c627bca323e5ed90"
- integrity sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==
- dependencies:
- "@jridgewell/trace-mapping" "^0.3.7"
- jest-worker "^27.4.5"
- schema-utils "^3.1.1"
- serialize-javascript "^6.0.0"
- terser "^5.7.2"
-
-terser@^5.0.0, terser@^5.10.0, terser@^5.7.2:
- version "5.14.0"
- resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.0.tgz#eefeec9af5153f55798180ee2617f390bdd285e2"
- integrity sha512-JC6qfIEkPBd9j1SMO3Pfn+A6w2kQV54tv+ABQLgZr7dA3k/DL/OBoYSWxzVpZev3J+bUHXfr55L8Mox7AaNo6g==
- dependencies:
- "@jridgewell/source-map" "^0.3.2"
- acorn "^8.5.0"
- commander "^2.20.0"
- source-map-support "~0.5.20"
-
-test-exclude@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
- integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==
- dependencies:
- "@istanbuljs/schema" "^0.1.2"
- glob "^7.1.4"
- minimatch "^3.0.4"
-
-text-table@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
- integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
-
-throat@^6.0.1:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375"
- integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==
-
-thunky@^1.0.2:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
- integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
-
-tiny-emitter@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
- integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
-
-tiny-invariant@^1.0.2:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9"
- integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==
-
-tiny-warning@^1.0.0, tiny-warning@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754"
- integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
-
-tmpl@1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
- integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
-
-to-fast-properties@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
- integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
-
-to-regex-range@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
- integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
- dependencies:
- is-number "^7.0.0"
-
-toidentifier@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
- integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
-
-tough-cookie@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
- integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==
- dependencies:
- psl "^1.1.33"
- punycode "^2.1.1"
- universalify "^0.1.2"
-
-tr46@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
- integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
- dependencies:
- punycode "^2.1.0"
-
-tr46@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240"
- integrity sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==
- dependencies:
- punycode "^2.1.1"
-
-trough@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876"
- integrity sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==
-
-tryer@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
- integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==
-
-ts-dedent@^2.2.0:
- version "2.2.0"
- resolved "https://npm.shopee.io/ts-dedent/-/ts-dedent-2.2.0.tgz#39e4bd297cd036292ae2394eb3412be63f563bb5"
- integrity sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==
-
-tsconfig-paths@^3.14.1:
- version "3.14.1"
- resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"
- integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==
- dependencies:
- "@types/json5" "^0.0.29"
- json5 "^1.0.1"
- minimist "^1.2.6"
- strip-bom "^3.0.0"
-
-tslib@^1.8.1:
- version "1.14.1"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
- integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-
-tslib@^2.0.3, tslib@^2.3.0, tslib@^2.3.1:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
- integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
-
-tsutils@^3.21.0:
- version "3.21.0"
- resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
- integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
- dependencies:
- tslib "^1.8.1"
-
-twemoji-parser@14.0.0:
- version "14.0.0"
- resolved "https://registry.yarnpkg.com/twemoji-parser/-/twemoji-parser-14.0.0.tgz#13dabcb6d3a261d9efbf58a1666b182033bf2b62"
- integrity sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA==
-
-twemoji@^14.0.1:
- version "14.0.2"
- resolved "https://registry.yarnpkg.com/twemoji/-/twemoji-14.0.2.tgz#c53adb01dab22bf4870f648ca8cc347ce99ee37e"
- integrity sha512-BzOoXIe1QVdmsUmZ54xbEH+8AgtOKUiG53zO5vVP2iUu6h5u9lN15NcuS6te4OY96qx0H7JK9vjjl9WQbkTRuA==
- dependencies:
- fs-extra "^8.0.1"
- jsonfile "^5.0.0"
- twemoji-parser "14.0.0"
- universalify "^0.1.2"
-
-type-check@^0.4.0, type-check@~0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
- integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
- dependencies:
- prelude-ls "^1.2.1"
-
-type-check@~0.3.2:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
- integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
- dependencies:
- prelude-ls "~1.1.2"
-
-type-detect@4.0.8:
- version "4.0.8"
- resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
- integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
-
-type-fest@^0.16.0:
- version "0.16.0"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860"
- integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==
-
-type-fest@^0.20.2:
- version "0.20.2"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
- integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
-
-type-fest@^0.21.3:
- version "0.21.3"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
- integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
-
-type-is@~1.6.18:
- version "1.6.18"
- resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
- integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
- dependencies:
- media-typer "0.3.0"
- mime-types "~2.1.24"
-
-typedarray-to-buffer@^3.1.5:
- version "3.1.5"
- resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
- integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
- dependencies:
- is-typedarray "^1.0.0"
-
-typescript@^4.4.4:
- version "4.7.2"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.2.tgz#1f9aa2ceb9af87cca227813b4310fff0b51593c4"
- integrity sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A==
-
-unbox-primitive@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
- integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==
- dependencies:
- call-bind "^1.0.2"
- has-bigints "^1.0.2"
- has-symbols "^1.0.3"
- which-boxed-primitive "^1.0.2"
-
-unicode-canonical-property-names-ecmascript@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
- integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==
-
-unicode-match-property-ecmascript@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3"
- integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==
- dependencies:
- unicode-canonical-property-names-ecmascript "^2.0.0"
- unicode-property-aliases-ecmascript "^2.0.0"
-
-unicode-match-property-value-ecmascript@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714"
- integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==
-
-unicode-property-aliases-ecmascript@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8"
- integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==
-
-unified@^10.0.0, unified@^10.1.0:
- version "10.1.2"
- resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df"
- integrity sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==
- dependencies:
- "@types/unist" "^2.0.0"
- bail "^2.0.0"
- extend "^3.0.0"
- is-buffer "^2.0.0"
- is-plain-obj "^4.0.0"
- trough "^2.0.0"
- vfile "^5.0.0"
-
-unique-string@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d"
- integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==
- dependencies:
- crypto-random-string "^2.0.0"
-
-unist-util-is@^5.0.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.1.1.tgz#e8aece0b102fa9bc097b0fef8f870c496d4a6236"
- integrity sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ==
-
-unist-util-stringify-position@^3.0.0:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz#5c6aa07c90b1deffd9153be170dce628a869a447"
- integrity sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg==
- dependencies:
- "@types/unist" "^2.0.0"
-
-unist-util-visit-parents@^4.0.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-4.1.1.tgz#e83559a4ad7e6048a46b1bdb22614f2f3f4724f2"
- integrity sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw==
- dependencies:
- "@types/unist" "^2.0.0"
- unist-util-is "^5.0.0"
-
-unist-util-visit-parents@^5.0.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.0.tgz#44bbc5d25f2411e7dfc5cecff12de43296aa8521"
- integrity sha512-y+QVLcY5eR/YVpqDsLf/xh9R3Q2Y4HxkZTp7ViLDU6WtJCEcPmRzW1gpdWDCDIqIlhuPDXOgttqPlykrHYDekg==
- dependencies:
- "@types/unist" "^2.0.0"
- unist-util-is "^5.0.0"
-
-unist-util-visit@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-3.1.0.tgz#9420d285e1aee938c7d9acbafc8e160186dbaf7b"
- integrity sha512-Szoh+R/Ll68QWAyQyZZpQzZQm2UPbxibDvaY8Xc9SUtYgPsDzx5AWSk++UUt2hJuow8mvwR+rG+LQLw+KsuAKA==
- dependencies:
- "@types/unist" "^2.0.0"
- unist-util-is "^5.0.0"
- unist-util-visit-parents "^4.0.0"
-
-unist-util-visit@^4.0.0, unist-util-visit@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.0.tgz#f41e407a9e94da31594e6b1c9811c51ab0b3d8f5"
- integrity sha512-n7lyhFKJfVZ9MnKtqbsqkQEk5P1KShj0+//V7mAcoI6bpbUjh3C/OG8HVD+pBihfh6Ovl01m8dkcv9HNqYajmQ==
- dependencies:
- "@types/unist" "^2.0.0"
- unist-util-is "^5.0.0"
- unist-util-visit-parents "^5.0.0"
-
-universalify@^0.1.0, universalify@^0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
- integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
-
-universalify@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
- integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
-
-unpipe@1.0.0, unpipe@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
- integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
-
-unquote@~1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
- integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=
-
-upath@^1.2.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
- integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==
-
-uri-js@^4.2.2:
- version "4.4.1"
- resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
- integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
- dependencies:
- punycode "^2.1.0"
-
-util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
- integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
-
-util.promisify@~1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee"
- integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==
- dependencies:
- define-properties "^1.1.3"
- es-abstract "^1.17.2"
- has-symbols "^1.0.1"
- object.getownpropertydescriptors "^2.1.0"
-
-utila@~0.4:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c"
- integrity sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=
-
-utils-merge@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
- integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
-
-uuid@^8.3.2:
- version "8.3.2"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
- integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
-
-uuid@^9.0.0:
- version "9.0.1"
- resolved "https://npm.shopee.io/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
- integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
-
-uvu@^0.5.0:
- version "0.5.3"
- resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.3.tgz#3d83c5bc1230f153451877bfc7f4aea2392219ae"
- integrity sha512-brFwqA3FXzilmtnIyJ+CxdkInkY/i4ErvP7uV0DnUVxQcQ55reuHphorpF+tZoVHK2MniZ/VJzI7zJQoc9T9Yw==
- dependencies:
- dequal "^2.0.0"
- diff "^5.0.0"
- kleur "^4.0.3"
- sade "^1.7.3"
-
-v8-compile-cache@^2.0.3:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
- integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
-
-v8-to-istanbul@^8.1.0:
- version "8.1.1"
- resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed"
- integrity sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==
- dependencies:
- "@types/istanbul-lib-coverage" "^2.0.1"
- convert-source-map "^1.6.0"
- source-map "^0.7.3"
-
-value-equal@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c"
- integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==
-
-vary@~1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
- integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
-
-vfile-message@^3.0.0:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-3.1.2.tgz#a2908f64d9e557315ec9d7ea3a910f658ac05f7d"
- integrity sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA==
- dependencies:
- "@types/unist" "^2.0.0"
- unist-util-stringify-position "^3.0.0"
-
-vfile@^5.0.0:
- version "5.3.2"
- resolved "https://registry.yarnpkg.com/vfile/-/vfile-5.3.2.tgz#b499fbc50197ea50ad3749e9b60beb16ca5b7c54"
- integrity sha512-w0PLIugRY3Crkgw89TeMvHCzqCs/zpreR31hl4D92y6SOE07+bfJe+dK5Q2akwS+i/c801kzjoOr9gMcTe6IAA==
- dependencies:
- "@types/unist" "^2.0.0"
- is-buffer "^2.0.0"
- unist-util-stringify-position "^3.0.0"
- vfile-message "^3.0.0"
-
-w3c-hr-time@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
- integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==
- dependencies:
- browser-process-hrtime "^1.0.0"
-
-w3c-keyname@^2.2.0, w3c-keyname@^2.2.4:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.4.tgz#4ade6916f6290224cdbd1db8ac49eab03d0eef6b"
- integrity sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==
-
-w3c-xmlserializer@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a"
- integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==
- dependencies:
- xml-name-validator "^3.0.0"
-
-walker@^1.0.7:
- version "1.0.8"
- resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
- integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==
- dependencies:
- makeerror "1.0.12"
-
-watchpack@^2.3.1:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
- integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
- dependencies:
- glob-to-regexp "^0.4.1"
- graceful-fs "^4.1.2"
-
-wbuf@^1.1.0, wbuf@^1.7.3:
- version "1.7.3"
- resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df"
- integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==
- dependencies:
- minimalistic-assert "^1.0.0"
-
-web-worker@^1.2.0:
- version "1.3.0"
- resolved "https://npm.shopee.io/web-worker/-/web-worker-1.3.0.tgz#e5f2df5c7fe356755a5fb8f8410d4312627e6776"
- integrity sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==
-
-webidl-conversions@^4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
- integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
-
-webidl-conversions@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
- integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==
-
-webidl-conversions@^6.1.0:
- version "6.1.0"
- resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
- integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
-
-webpack-dev-middleware@^5.3.1:
- version "5.3.3"
- resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f"
- integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==
- dependencies:
- colorette "^2.0.10"
- memfs "^3.4.3"
- mime-types "^2.1.31"
- range-parser "^1.2.1"
- schema-utils "^4.0.0"
-
-webpack-dev-server@^4.6.0:
- version "4.9.1"
- resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.9.1.tgz#184607b0287c791aeaa45e58e8fe75fcb4d7e2a8"
- integrity sha512-CTMfu2UMdR/4OOZVHRpdy84pNopOuigVIsRbGX3LVDMWNP8EUgC5mUBMErbwBlHTEX99ejZJpVqrir6EXAEajA==
- dependencies:
- "@types/bonjour" "^3.5.9"
- "@types/connect-history-api-fallback" "^1.3.5"
- "@types/express" "^4.17.13"
- "@types/serve-index" "^1.9.1"
- "@types/sockjs" "^0.3.33"
- "@types/ws" "^8.5.1"
- ansi-html-community "^0.0.8"
- bonjour-service "^1.0.11"
- chokidar "^3.5.3"
- colorette "^2.0.10"
- compression "^1.7.4"
- connect-history-api-fallback "^1.6.0"
- default-gateway "^6.0.3"
- express "^4.17.3"
- graceful-fs "^4.2.6"
- html-entities "^2.3.2"
- http-proxy-middleware "^2.0.3"
- ipaddr.js "^2.0.1"
- open "^8.0.9"
- p-retry "^4.5.0"
- rimraf "^3.0.2"
- schema-utils "^4.0.0"
- selfsigned "^2.0.1"
- serve-index "^1.9.1"
- sockjs "^0.3.24"
- spdy "^4.0.2"
- webpack-dev-middleware "^5.3.1"
- ws "^8.4.2"
-
-webpack-manifest-plugin@^4.0.2:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz#10f8dbf4714ff93a215d5a45bcc416d80506f94f"
- integrity sha512-YXUAwxtfKIJIKkhg03MKuiFAD72PlrqCiwdwO4VEXdRO5V0ORCNwaOwAZawPZalCbmH9kBDmXnNeQOw+BIEiow==
- dependencies:
- tapable "^2.0.0"
- webpack-sources "^2.2.0"
-
-webpack-sources@^1.4.3:
- version "1.4.3"
- resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933"
- integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==
- dependencies:
- source-list-map "^2.0.0"
- source-map "~0.6.1"
-
-webpack-sources@^2.2.0:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.1.tgz#570de0af163949fe272233c2cefe1b56f74511fd"
- integrity sha512-y9EI9AO42JjEcrTJFOYmVywVZdKVUfOvDUPsJea5GIr1JOEGFVqwlY2K098fFoIjOkDzHn2AjRvM8dsBZu+gCA==
- dependencies:
- source-list-map "^2.0.1"
- source-map "^0.6.1"
-
-webpack-sources@^3.2.3:
- version "3.2.3"
- resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
- integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
-
-webpack@^5.64.4:
- version "5.73.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38"
- integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==
- dependencies:
- "@types/eslint-scope" "^3.7.3"
- "@types/estree" "^0.0.51"
- "@webassemblyjs/ast" "1.11.1"
- "@webassemblyjs/wasm-edit" "1.11.1"
- "@webassemblyjs/wasm-parser" "1.11.1"
- acorn "^8.4.1"
- acorn-import-assertions "^1.7.6"
- browserslist "^4.14.5"
- chrome-trace-event "^1.0.2"
- enhanced-resolve "^5.9.3"
- es-module-lexer "^0.9.0"
- eslint-scope "5.1.1"
- events "^3.2.0"
- glob-to-regexp "^0.4.1"
- graceful-fs "^4.2.9"
- json-parse-even-better-errors "^2.3.1"
- loader-runner "^4.2.0"
- mime-types "^2.1.27"
- neo-async "^2.6.2"
- schema-utils "^3.1.0"
- tapable "^2.1.1"
- terser-webpack-plugin "^5.1.3"
- watchpack "^2.3.1"
- webpack-sources "^3.2.3"
-
-websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
- version "0.7.4"
- resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760"
- integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==
- dependencies:
- http-parser-js ">=0.5.1"
- safe-buffer ">=5.1.0"
- websocket-extensions ">=0.1.1"
-
-websocket-extensions@>=0.1.1:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42"
- integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==
-
-whatwg-encoding@^1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
- integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==
- dependencies:
- iconv-lite "0.4.24"
-
-whatwg-fetch@^3.6.2:
- version "3.6.2"
- resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"
- integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==
-
-whatwg-mimetype@^2.3.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
- integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
-
-whatwg-url@^7.0.0:
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
- integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==
- dependencies:
- lodash.sortby "^4.7.0"
- tr46 "^1.0.1"
- webidl-conversions "^4.0.2"
-
-whatwg-url@^8.0.0, whatwg-url@^8.5.0:
- version "8.7.0"
- resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
- integrity sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==
- dependencies:
- lodash "^4.7.0"
- tr46 "^2.1.0"
- webidl-conversions "^6.1.0"
-
-which-boxed-primitive@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
- integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
- dependencies:
- is-bigint "^1.0.1"
- is-boolean-object "^1.1.0"
- is-number-object "^1.0.4"
- is-string "^1.0.5"
- is-symbol "^1.0.3"
-
-which@^1.3.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
- integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
- dependencies:
- isexe "^2.0.0"
-
-which@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
- integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
- dependencies:
- isexe "^2.0.0"
-
-word-wrap@^1.2.3, word-wrap@~1.2.3:
- version "1.2.3"
- resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
- integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
-
-workbox-background-sync@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.5.3.tgz#7c66c1836aeca6f3762dc48d17a1852a33b3168c"
- integrity sha512-0DD/V05FAcek6tWv9XYj2w5T/plxhDSpclIcAGjA/b7t/6PdaRkQ7ZgtAX6Q/L7kV7wZ8uYRJUoH11VjNipMZw==
- dependencies:
- idb "^6.1.4"
- workbox-core "6.5.3"
-
-workbox-broadcast-update@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.5.3.tgz#fc2ad79cf507e22950cda9baf1e9a0ccc43f31bc"
- integrity sha512-4AwCIA5DiDrYhlN+Miv/fp5T3/whNmSL+KqhTwRBTZIL6pvTgE4lVuRzAt1JltmqyMcQ3SEfCdfxczuI4kwFQg==
- dependencies:
- workbox-core "6.5.3"
-
-workbox-build@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.5.3.tgz#38e3f286d63d2745bff4d1478bb3a6ab5c8b1170"
- integrity sha512-8JNHHS7u13nhwIYCDea9MNXBNPHXCs5KDZPKI/ZNTr3f4sMGoD7hgFGecbyjX1gw4z6e9bMpMsOEJNyH5htA/w==
- dependencies:
- "@apideck/better-ajv-errors" "^0.3.1"
- "@babel/core" "^7.11.1"
- "@babel/preset-env" "^7.11.0"
- "@babel/runtime" "^7.11.2"
- "@rollup/plugin-babel" "^5.2.0"
- "@rollup/plugin-node-resolve" "^11.2.1"
- "@rollup/plugin-replace" "^2.4.1"
- "@surma/rollup-plugin-off-main-thread" "^2.2.3"
- ajv "^8.6.0"
- common-tags "^1.8.0"
- fast-json-stable-stringify "^2.1.0"
- fs-extra "^9.0.1"
- glob "^7.1.6"
- lodash "^4.17.20"
- pretty-bytes "^5.3.0"
- rollup "^2.43.1"
- rollup-plugin-terser "^7.0.0"
- source-map "^0.8.0-beta.0"
- stringify-object "^3.3.0"
- strip-comments "^2.0.1"
- tempy "^0.6.0"
- upath "^1.2.0"
- workbox-background-sync "6.5.3"
- workbox-broadcast-update "6.5.3"
- workbox-cacheable-response "6.5.3"
- workbox-core "6.5.3"
- workbox-expiration "6.5.3"
- workbox-google-analytics "6.5.3"
- workbox-navigation-preload "6.5.3"
- workbox-precaching "6.5.3"
- workbox-range-requests "6.5.3"
- workbox-recipes "6.5.3"
- workbox-routing "6.5.3"
- workbox-strategies "6.5.3"
- workbox-streams "6.5.3"
- workbox-sw "6.5.3"
- workbox-window "6.5.3"
-
-workbox-cacheable-response@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.5.3.tgz#b1f8c2bc599a7be8f7e3c262535629c558738e47"
- integrity sha512-6JE/Zm05hNasHzzAGKDkqqgYtZZL2H06ic2GxuRLStA4S/rHUfm2mnLFFXuHAaGR1XuuYyVCEey1M6H3PdZ7SQ==
- dependencies:
- workbox-core "6.5.3"
-
-workbox-core@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.5.3.tgz#bca038a9ef0d7a634a6db2a60f45313ed22ac249"
- integrity sha512-Bb9ey5n/M9x+l3fBTlLpHt9ASTzgSGj6vxni7pY72ilB/Pb3XtN+cZ9yueboVhD5+9cNQrC9n/E1fSrqWsUz7Q==
-
-workbox-expiration@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.5.3.tgz#efc0811f371a2ede1052b9de1c4f072b71d50503"
- integrity sha512-jzYopYR1zD04ZMdlbn/R2Ik6ixiXbi15c9iX5H8CTi6RPDz7uhvMLZPKEndZTpfgmUk8mdmT9Vx/AhbuCl5Sqw==
- dependencies:
- idb "^6.1.4"
- workbox-core "6.5.3"
-
-workbox-google-analytics@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.5.3.tgz#cc8c3a61f449131660a4ed2f5362d9a3599b18fe"
- integrity sha512-3GLCHotz5umoRSb4aNQeTbILETcrTVEozSfLhHSBaegHs1PnqCmN0zbIy2TjTpph2AGXiNwDrWGF0AN+UgDNTw==
- dependencies:
- workbox-background-sync "6.5.3"
- workbox-core "6.5.3"
- workbox-routing "6.5.3"
- workbox-strategies "6.5.3"
-
-workbox-navigation-preload@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.5.3.tgz#81b74f598b11aa07e2cf1c21af7a826a4f0f70b3"
- integrity sha512-bK1gDFTc5iu6lH3UQ07QVo+0ovErhRNGvJJO/1ngknT0UQ702nmOUhoN9qE5mhuQSrnK+cqu7O7xeaJ+Rd9Tmg==
- dependencies:
- workbox-core "6.5.3"
-
-workbox-precaching@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.5.3.tgz#c870312b2ef901d790ab9e48da084e776c62af47"
- integrity sha512-sjNfgNLSsRX5zcc63H/ar/hCf+T19fRtTqvWh795gdpghWb5xsfEkecXEvZ8biEi1QD7X/ljtHphdaPvXDygMQ==
- dependencies:
- workbox-core "6.5.3"
- workbox-routing "6.5.3"
- workbox-strategies "6.5.3"
-
-workbox-range-requests@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.5.3.tgz#e624ac82ff266a5e4f236d055797def07949d941"
- integrity sha512-pGCP80Bpn/0Q0MQsfETSfmtXsQcu3M2QCJwSFuJ6cDp8s2XmbUXkzbuQhCUzKR86ZH2Vex/VUjb2UaZBGamijA==
- dependencies:
- workbox-core "6.5.3"
-
-workbox-recipes@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.5.3.tgz#15beac9d8ae7a3a1c100218094a824b4dd3fd59a"
- integrity sha512-IcgiKYmbGiDvvf3PMSEtmwqxwfQ5zwI7OZPio3GWu4PfehA8jI8JHI3KZj+PCfRiUPZhjQHJ3v1HbNs+SiSkig==
- dependencies:
- workbox-cacheable-response "6.5.3"
- workbox-core "6.5.3"
- workbox-expiration "6.5.3"
- workbox-precaching "6.5.3"
- workbox-routing "6.5.3"
- workbox-strategies "6.5.3"
-
-workbox-routing@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.5.3.tgz#a0a699d8cc90b5692bd3df24679acbbda3913777"
- integrity sha512-DFjxcuRAJjjt4T34RbMm3MCn+xnd36UT/2RfPRfa8VWJGItGJIn7tG+GwVTdHmvE54i/QmVTJepyAGWtoLPTmg==
- dependencies:
- workbox-core "6.5.3"
-
-workbox-strategies@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.5.3.tgz#4bea9a48fee16cf43766e0d8138296773c8a9783"
- integrity sha512-MgmGRrDVXs7rtSCcetZgkSZyMpRGw8HqL2aguszOc3nUmzGZsT238z/NN9ZouCxSzDu3PQ3ZSKmovAacaIhu1w==
- dependencies:
- workbox-core "6.5.3"
-
-workbox-streams@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.5.3.tgz#b6860290031caa7d0e46ad7142315c94359c780b"
- integrity sha512-vN4Qi8o+b7zj1FDVNZ+PlmAcy1sBoV7SC956uhqYvZ9Sg1fViSbOpydULOssVJ4tOyKRifH/eoi6h99d+sJ33w==
- dependencies:
- workbox-core "6.5.3"
- workbox-routing "6.5.3"
-
-workbox-sw@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.5.3.tgz#cd2f0c086f4496acd25774ed02c48504189bebdd"
- integrity sha512-BQBzm092w+NqdIEF2yhl32dERt9j9MDGUTa2Eaa+o3YKL4Qqw55W9yQC6f44FdAHdAJrJvp0t+HVrfh8AiGj8A==
-
-workbox-webpack-plugin@^6.4.1:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.5.3.tgz#c37bb323be4952311565c07db51054fe59c87d73"
- integrity sha512-Es8Xr02Gi6Kc3zaUwR691ZLy61hz3vhhs5GztcklQ7kl5k2qAusPh0s6LF3wEtlpfs9ZDErnmy5SErwoll7jBA==
- dependencies:
- fast-json-stable-stringify "^2.1.0"
- pretty-bytes "^5.4.1"
- upath "^1.2.0"
- webpack-sources "^1.4.3"
- workbox-build "6.5.3"
-
-workbox-window@6.5.3:
- version "6.5.3"
- resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.5.3.tgz#4ade70056cb73477ef1cd8fea7cfd0ecbd825c7f"
- integrity sha512-GnJbx1kcKXDtoJBVZs/P7ddP0Yt52NNy4nocjBpYPiRhMqTpJCNrSL+fGHZ/i/oP6p/vhE8II0sA6AZGKGnssw==
- dependencies:
- "@types/trusted-types" "^2.0.2"
- workbox-core "6.5.3"
-
-wrap-ansi@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
- integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
- dependencies:
- ansi-styles "^4.0.0"
- string-width "^4.1.0"
- strip-ansi "^6.0.0"
-
-wrappy@1:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
- integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
-
-write-file-atomic@^3.0.0:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8"
- integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==
- dependencies:
- imurmurhash "^0.1.4"
- is-typedarray "^1.0.0"
- signal-exit "^3.0.2"
- typedarray-to-buffer "^3.1.5"
-
-ws@^7.4.6:
- version "7.5.8"
- resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.8.tgz#ac2729881ab9e7cbaf8787fe3469a48c5c7f636a"
- integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw==
-
-ws@^8.4.2:
- version "8.7.0"
- resolved "https://registry.yarnpkg.com/ws/-/ws-8.7.0.tgz#eaf9d874b433aa00c0e0d8752532444875db3957"
- integrity sha512-c2gsP0PRwcLFzUiA8Mkr37/MI7ilIlHQxaEAtd0uNMbVMoy8puJyafRlm0bV9MbGSabUPeLrRRaqIBcFcA2Pqg==
-
-xml-name-validator@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
- integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
-
-xmlchars@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
- integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
-
-xtend@^4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
- integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
-
-y18n@^5.0.5:
- version "5.0.8"
- resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
- integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
-
-yallist@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
- integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
-
-yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2:
- version "1.10.2"
- resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
- integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
-
-yargs-parser@^20.2.2:
- version "20.2.9"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
- integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
-
-yargs@^16.2.0:
- version "16.2.0"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
- integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
- dependencies:
- cliui "^7.0.2"
- escalade "^3.1.1"
- get-caller-file "^2.0.5"
- require-directory "^2.1.1"
- string-width "^4.2.0"
- y18n "^5.0.5"
- yargs-parser "^20.2.2"
-
-yocto-queue@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
- integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
-
-zwitch@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.2.tgz#91f8d0e901ffa3d66599756dde7f57b17c95dce1"
- integrity sha512-JZxotl7SxAJH0j7dN4pxsTV6ZLXoLdGME+PsjkL/DaBrVryK9kTGq06GfKrwcSOqypP+fdXGoCHE36b99fWVoA==
diff --git a/crates/.cargo/config.toml b/crates/.cargo/config.toml
new file mode 100644
index 0000000..8e7cc27
--- /dev/null
+++ b/crates/.cargo/config.toml
@@ -0,0 +1,20 @@
+# Cargo configuration for cross-compilation
+
+[target.x86_64-pc-windows-gnu]
+# Configure linker for Windows GNU target (cross-compilation from macOS)
+# This requires MinGW-w64 to be installed: brew install mingw-w64
+#
+# Installation steps:
+# 1. rustup target add x86_64-pc-windows-gnu
+# 2. brew install mingw-w64
+# 3. Add to PATH (if needed):
+# - Apple Silicon: export PATH="/opt/homebrew/bin:$PATH"
+# - Intel Mac: export PATH="/usr/local/bin:$PATH"
+linker = "x86_64-w64-mingw32-gcc"
+
+# Set compiler environment variables for cross-compilation
+[env]
+CC_x86_64_pc_windows_gnu = "x86_64-w64-mingw32-gcc"
+CXX_x86_64_pc_windows_gnu = "x86_64-w64-mingw32-g++"
+AR_x86_64_pc_windows_gnu = "x86_64-w64-mingw32-ar"
+
diff --git a/crates/.gitignore b/crates/.gitignore
new file mode 100644
index 0000000..9e4e2df
--- /dev/null
+++ b/crates/.gitignore
@@ -0,0 +1,4 @@
+/target
+target/
+logs/
+*.pid
diff --git a/crates/BUILD.md b/crates/BUILD.md
new file mode 100644
index 0000000..599dc39
--- /dev/null
+++ b/crates/BUILD.md
@@ -0,0 +1,129 @@
+# Build Guide
+
+This document explains how to build MDS-Server for different platforms.
+
+## Cross-Platform Build Script
+
+We provide a **cross-platform Node.js build script** (`build.js`) that works on Windows, macOS, and Linux:
+
+```bash
+# Build for current platform
+node build.js
+
+# Build for specific platform
+node build.js macos
+node build.js windows
+
+# Build for all platforms (if on macOS)
+node build.js all
+
+# macOS options
+node build.js macos --intel # Intel only
+node build.js macos --arm # Apple Silicon only
+node build.js macos --universal # Universal binary (default)
+
+# Windows options
+node build.js windows --gnu # GNU toolchain (default, cross-compile)
+node build.js windows --msvc # MSVC toolchain (Windows only)
+```
+
+Or via pnpm:
+```bash
+pnpm build:macos
+pnpm build:windows
+pnpm build:all
+```
+
+## Platform-Specific Scripts
+
+We also provide platform-specific shell scripts for Unix systems (macOS/Linux):
+
+- `build-macos.sh` - Bash script for macOS builds
+- `build-windows.sh` - Bash script for Windows cross-compilation
+
+These work on macOS and Linux, but not on Windows (use `build.js` instead).
+
+## Cross-Compilation Requirements
+
+### Building Windows from macOS/Linux
+
+**Requirements:**
+1. Install Rust target: `rustup target add x86_64-pc-windows-gnu`
+2. Install MinGW-w64:
+ - **macOS**: `brew install mingw-w64`
+ - **Linux**: `sudo apt-get install mingw-w64` (Debian/Ubuntu) or `sudo yum install mingw64-gcc` (RHEL/CentOS)
+
+**Usage:**
+```bash
+node build.js windows
+# or
+./build-windows.sh
+```
+
+### Building macOS from Windows/Linux
+
+**Note:** Building macOS binaries from non-macOS systems is **not recommended** and requires complex setup (osxcross).
+
+**Recommended approach:**
+- Use GitHub Actions with macOS runners (see `.github/workflows/build.yml`)
+- Or build natively on macOS
+
+**If you must cross-compile:**
+1. Install Rust targets: `rustup target add x86_64-apple-darwin aarch64-apple-darwin`
+2. Set up osxcross (complex, see osxcross documentation)
+3. Configure Cargo to use osxcross toolchain
+
+### Building macOS from macOS
+
+**Requirements:**
+1. Install Rust targets:
+ ```bash
+ rustup target add x86_64-apple-darwin
+ rustup target add aarch64-apple-darwin
+ ```
+
+**Usage:**
+```bash
+node build.js macos
+# or
+./build-macos.sh
+```
+
+## GitHub CI
+
+The project includes a GitHub Actions workflow (`.github/workflows/build.yml`) that:
+
+- Builds macOS binaries on macOS runners
+- Builds Windows binaries on Windows runners
+- Builds Linux binaries and cross-compiles Windows on Linux runners
+
+This is the **recommended way** to build for all platforms reliably.
+
+## Output
+
+All builds create artifacts in the `dist/` directory:
+
+- **macOS**: `dist/MDS-Server.app` and `dist/mds-macos.zip`
+- **Windows**: `dist/mds.exe` and `dist/mds-windows.zip`
+- **Linux**: `target/release/mds` (binary only)
+
+## Troubleshooting
+
+### MinGW not found on macOS
+```bash
+brew install mingw-w64
+export PATH="/opt/homebrew/bin:$PATH" # Apple Silicon
+# or
+export PATH="/usr/local/bin:$PATH" # Intel Mac
+```
+
+### Rust target not installed
+```bash
+rustup target add
+```
+
+### Permission denied on scripts
+```bash
+chmod +x build-macos.sh build-windows.sh build.js
+```
+
diff --git a/crates/Cargo.lock b/crates/Cargo.lock
new file mode 100644
index 0000000..b30d31e
--- /dev/null
+++ b/crates/Cargo.lock
@@ -0,0 +1,2365 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anstream"
+version = "0.6.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
+
+[[package]]
+name = "anstyle-parse"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
+[[package]]
+name = "axum"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871"
+dependencies = [
+ "axum-core",
+ "axum-macros",
+ "bytes",
+ "form_urlencoded",
+ "futures-util",
+ "http",
+ "http-body",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "itoa",
+ "matchit",
+ "memchr",
+ "mime",
+ "multer",
+ "percent-encoding",
+ "pin-project-lite",
+ "serde_core",
+ "serde_json",
+ "serde_path_to_error",
+ "serde_urlencoded",
+ "sync_wrapper",
+ "tokio",
+ "tower",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-core"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "http-body-util",
+ "mime",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "axum-macros"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "bstr"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
+dependencies = [
+ "memchr",
+ "regex-automata",
+ "serde",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+
+[[package]]
+name = "bytes"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
+
+[[package]]
+name = "cc"
+version = "1.2.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd405d82c84ff7f35739f175f67d8b9fb7687a0e84ccdc78bd3568839827cf07"
+dependencies = [
+ "find-msvc-tools",
+ "jobserver",
+ "libc",
+ "shlex",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "clap"
+version = "4.5.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.5.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.5.49"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "daemonize"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab8bfdaacb3c887a54d41bdf48d3af8873b3f5566469f8ba21b92057509f116e"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "deranged"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "dirs"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "encoding_rs"
+version = "0.8.35"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "encoding_rs_io"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83"
+dependencies = [
+ "encoding_rs",
+]
+
+[[package]]
+name = "errno"
+version = "0.3.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "fastrand"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+dependencies = [
+ "percent-encoding",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasip2",
+]
+
+[[package]]
+name = "git2"
+version = "0.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110"
+dependencies = [
+ "bitflags 2.10.0",
+ "libc",
+ "libgit2-sys",
+ "log",
+ "openssl-probe",
+ "openssl-sys",
+ "url",
+]
+
+[[package]]
+name = "globset"
+version = "0.4.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "log",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "grep-matcher"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36d7b71093325ab22d780b40d7df3066ae4aebb518ba719d38c697a8228a8023"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "grep-regex"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce0c256c3ad82bcc07b812c15a45ec1d398122e8e15124f96695234db7112ef"
+dependencies = [
+ "bstr",
+ "grep-matcher",
+ "log",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "grep-searcher"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac63295322dc48ebb20a25348147905d816318888e64f531bfc2a2bc0577dc34"
+dependencies = [
+ "bstr",
+ "encoding_rs",
+ "encoding_rs_io",
+ "grep-matcher",
+ "log",
+ "memchr",
+ "memmap2",
+]
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "http"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565"
+dependencies = [
+ "bytes",
+ "fnv",
+ "itoa",
+]
+
+[[package]]
+name = "http-body"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
+dependencies = [
+ "bytes",
+ "http",
+]
+
+[[package]]
+name = "http-body-util"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "httpdate"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
+
+[[package]]
+name = "hyper"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "futures-channel",
+ "futures-core",
+ "http",
+ "http-body",
+ "httparse",
+ "httpdate",
+ "itoa",
+ "pin-project-lite",
+ "pin-utils",
+ "smallvec",
+ "tokio",
+]
+
+[[package]]
+name = "hyper-util"
+version = "0.1.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "http",
+ "http-body",
+ "hyper",
+ "pin-project-lite",
+ "tokio",
+ "tower-service",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
+dependencies = [
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
+
+[[package]]
+name = "icu_properties"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99"
+dependencies = [
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899"
+
+[[package]]
+name = "icu_provider"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "ignore"
+version = "0.4.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3d782a365a015e0f5c04902246139249abf769125006fbe7649e2ee88169b4a"
+dependencies = [
+ "crossbeam-deque",
+ "globset",
+ "log",
+ "memchr",
+ "regex-automata",
+ "same-file",
+ "walkdir",
+ "winapi-util",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
+
+[[package]]
+name = "itoa"
+version = "1.0.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+
+[[package]]
+name = "jobserver"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
+dependencies = [
+ "getrandom 0.3.4",
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.81"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
+dependencies = [
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "libc"
+version = "0.2.177"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
+
+[[package]]
+name = "libgit2-sys"
+version = "0.18.2+1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222"
+dependencies = [
+ "cc",
+ "libc",
+ "libssh2-sys",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+]
+
+[[package]]
+name = "libredox"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50"
+dependencies = [
+ "bitflags 2.10.0",
+ "libc",
+]
+
+[[package]]
+name = "libssh2-sys"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9"
+dependencies = [
+ "cc",
+ "libc",
+ "libz-sys",
+ "openssl-sys",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "libz-sys"
+version = "1.1.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
+
+[[package]]
+name = "listenfd"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b87bc54a4629b4294d0b3ef041b64c40c611097a677d9dc07b2c67739fe39dba"
+dependencies = [
+ "libc",
+ "uuid",
+ "winapi",
+]
+
+[[package]]
+name = "litemap"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
+
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
+
+[[package]]
+name = "matchers"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
+dependencies = [
+ "regex-automata",
+]
+
+[[package]]
+name = "matchit"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
+
+[[package]]
+name = "md-server"
+version = "1.0.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "daemonize",
+ "dirs",
+ "nix",
+ "server",
+ "sysinfo",
+ "tokio",
+ "tracing",
+ "tracing-appender",
+ "tracing-subscriber",
+ "windows-service",
+ "winreg",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
+
+[[package]]
+name = "memmap2"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "mime"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
+
+[[package]]
+name = "mio"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "multer"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83e87776546dc87511aa5ee218730c92b666d7264ab6ed41f9d215af9cd5224b"
+dependencies = [
+ "bytes",
+ "encoding_rs",
+ "futures-util",
+ "http",
+ "httparse",
+ "memchr",
+ "mime",
+ "spin",
+ "version_check",
+]
+
+[[package]]
+name = "nix"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
+dependencies = [
+ "bitflags 2.10.0",
+ "cfg-if",
+ "cfg_aliases",
+ "libc",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.50.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+
+[[package]]
+name = "once_cell"
+version = "1.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
+
+[[package]]
+name = "openssl-src"
+version = "300.5.4+3.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.111"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
+dependencies = [
+ "cc",
+ "libc",
+ "openssl-src",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "parking_lot"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-link",
+]
+
+[[package]]
+name = "partial_struct"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4105d8aa4abd4e22c00d72955c14848b227eaa74c6e17542ced04ad5de08b6bc"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
+name = "potential_utf"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "r-efi"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
+
+[[package]]
+name = "rayon"
+version = "1.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags 2.10.0",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
+dependencies = [
+ "getrandom 0.2.16",
+ "libredox",
+ "thiserror",
+]
+
+[[package]]
+name = "regex"
+version = "1.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
+
+[[package]]
+name = "rustix"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
+dependencies = [
+ "bitflags 2.10.0",
+ "errno",
+ "libc",
+ "linux-raw-sys",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "ryu"
+version = "1.0.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.145"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
+dependencies = [
+ "itoa",
+ "memchr",
+ "ryu",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "serde_path_to_error"
+version = "0.1.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
+dependencies = [
+ "itoa",
+ "serde",
+ "serde_core",
+]
+
+[[package]]
+name = "serde_urlencoded"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
+dependencies = [
+ "form_urlencoded",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "server"
+version = "1.0.0"
+dependencies = [
+ "anyhow",
+ "axum",
+ "dirs",
+ "git2",
+ "grep-matcher",
+ "grep-regex",
+ "grep-searcher",
+ "ignore",
+ "listenfd",
+ "partial_struct",
+ "percent-encoding",
+ "regex",
+ "serde",
+ "serde_json",
+ "sha2",
+ "struct-patch",
+ "tempfile",
+ "tokio",
+ "tower",
+ "tower-http",
+ "tracing",
+ "tracing-appender",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "socket2"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881"
+dependencies = [
+ "libc",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "spin"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
+
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "struct-patch"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e986d2cf6e819bd843319120453d837dfdfa31497c3fee4cefa614b2d182d8c"
+dependencies = [
+ "struct-patch-derive",
+]
+
+[[package]]
+name = "struct-patch-derive"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68c6387c1c7b53060605101b63d93edca618c6cf7ce61839f2ec2a527419fdb5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.107"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "sync_wrapper"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
+
+[[package]]
+name = "synstructure"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.32.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c33cd241af0f2e9e3b5c32163b873b29956890b5342e6745b917ce9d490f4af"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+ "memchr",
+ "ntapi",
+ "rayon",
+ "windows",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16"
+dependencies = [
+ "fastrand",
+ "getrandom 0.3.4",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "time"
+version = "0.3.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
+
+[[package]]
+name = "time-macros"
+version = "0.2.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tinystr"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
+[[package]]
+name = "tokio"
+version = "1.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408"
+dependencies = [
+ "bytes",
+ "libc",
+ "mio",
+ "parking_lot",
+ "pin-project-lite",
+ "signal-hook-registry",
+ "socket2",
+ "tokio-macros",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tower"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9"
+dependencies = [
+ "futures-core",
+ "futures-util",
+ "pin-project-lite",
+ "sync_wrapper",
+ "tokio",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+]
+
+[[package]]
+name = "tower-http"
+version = "0.6.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2"
+dependencies = [
+ "bitflags 2.10.0",
+ "bytes",
+ "http",
+ "http-body",
+ "pin-project-lite",
+ "tower-layer",
+ "tower-service",
+ "tracing",
+ "uuid",
+]
+
+[[package]]
+name = "tower-layer"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
+
+[[package]]
+name = "tower-service"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+
+[[package]]
+name = "tracing"
+version = "0.1.41"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+dependencies = [
+ "log",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-appender"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf"
+dependencies = [
+ "crossbeam-channel",
+ "thiserror",
+ "time",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex-automata",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "typenum"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
+
+[[package]]
+name = "url"
+version = "2.5.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
+name = "utf8parse"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
+
+[[package]]
+name = "uuid"
+version = "1.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
+dependencies = [
+ "getrandom 0.3.4",
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasip2"
+version = "1.0.1+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
+dependencies = [
+ "wit-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
+dependencies = [
+ "bumpalo",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "widestring"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "windows"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
+dependencies = [
+ "windows-core",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-result",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.57.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-result"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-service"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd9db37ecb5b13762d95468a2fc6009d4b2c62801243223aabd44fca13ad13c8"
+dependencies = [
+ "bitflags 1.3.2",
+ "widestring",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
+dependencies = [
+ "windows-targets 0.53.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
+ "windows_i686_gnullvm 0.52.6",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.53.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
+dependencies = [
+ "windows-link",
+ "windows_aarch64_gnullvm 0.53.1",
+ "windows_aarch64_msvc 0.53.1",
+ "windows_i686_gnu 0.53.1",
+ "windows_i686_gnullvm 0.53.1",
+ "windows_i686_msvc 0.53.1",
+ "windows_x86_64_gnu 0.53.1",
+ "windows_x86_64_gnullvm 0.53.1",
+ "windows_x86_64_msvc 0.53.1",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.53.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
+
+[[package]]
+name = "winreg"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "wit-bindgen"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
+
+[[package]]
+name = "writeable"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
+
+[[package]]
+name = "yoke"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
+dependencies = [
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerotrie"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/crates/Cargo.toml b/crates/Cargo.toml
new file mode 100644
index 0000000..6f8a77d
--- /dev/null
+++ b/crates/Cargo.toml
@@ -0,0 +1,47 @@
+[workspace]
+members = ["server", "cli"]
+resolver = "2"
+
+[workspace.package]
+version = "1.0.0"
+edition = "2024"
+
+[workspace.dependencies]
+# Server dependencies
+anyhow = "1.0.100"
+axum = { version = "0.8.6", features = ["macros", "multipart"] }
+git2 = { version = "0.20", features = ["vendored-openssl"] }
+listenfd = "1.0.2"
+partial_struct = "0.4.5"
+percent-encoding = "2.3"
+regex = "1.10"
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0.145"
+struct-patch = "0.10.4"
+tokio = { version = "1.0", features = ["full"] }
+tower = "0.5.2"
+tower-http = { version = "0.6", features = ["trace", "request-id", "normalize-path"] }
+tracing = "0.1"
+tracing-appender = "0.2"
+tracing-subscriber = { version = "0.3", features = ["env-filter"] }
+sha2 = "0.10"
+ignore = "0.4"
+grep-regex = "0.1"
+grep-searcher = "0.1"
+grep-matcher = "0.1"
+
+# CLI dependencies
+clap = { version = "4.5", features = ["derive"] }
+daemonize = "0.5"
+dirs = "6.0"
+nix = { version = "0.29", features = ["signal", "process"] }
+sysinfo = "0.32"
+
+# Dev dependencies
+tempfile = "3.10"
+
+[profile.dev]
+opt-level = 0
+
+[profile.release]
+opt-level = 3
diff --git a/crates/Makefile.toml b/crates/Makefile.toml
new file mode 100644
index 0000000..b15eea8
--- /dev/null
+++ b/crates/Makefile.toml
@@ -0,0 +1,117 @@
+[config]
+# Don't run tasks in workspace members by default
+default_to_workspace = false
+
+[tasks.default]
+description = "Run the default task (dev)"
+command = "cargo"
+args = ["run", "-p", "md-server", "--", "start"]
+
+[tasks.dev]
+description = "Run the development server with hot-reload"
+script = [
+ "systemfd --no-pid -s http::3025 -- watchexec -r -- cargo run -p md-server -- start"
+]
+
+[tasks.stop]
+description = "Stop the running server daemon"
+command = "cargo"
+args = ["run", "-p", "md-server", "--", "stop"]
+
+[tasks.build]
+description = "Build the workspace in debug mode"
+command = "cargo"
+args = ["build", "--workspace"]
+
+[tasks.release]
+description = "Build the workspace in release mode"
+command = "cargo"
+args = ["build", "--workspace", "--release"]
+
+# Platform-specific release builds
+[tasks.release-macos-intel]
+description = "Build release for macOS Intel (x86_64)"
+command = "cargo"
+args = ["build", "--workspace", "--release", "--target", "x86_64-apple-darwin"]
+
+[tasks.release-macos-arm]
+description = "Build release for macOS Apple Silicon (aarch64)"
+command = "cargo"
+args = ["build", "--workspace", "--release", "--target", "aarch64-apple-darwin"]
+
+[tasks.release-macos-universal]
+description = "Build universal macOS binary (Intel + Apple Silicon)"
+dependencies = ["release-macos-intel", "release-macos-arm"]
+script = [
+ "mkdir -p target/universal-apple-darwin/release",
+ "lipo -create -output target/universal-apple-darwin/release/mds target/x86_64-apple-darwin/release/mds target/aarch64-apple-darwin/release/mds"
+]
+
+[tasks.release-windows]
+description = "Build release for Windows using GNU toolchain (cross-compile from macOS/Linux)"
+dependencies = ["check-windows-deps"]
+command = "cargo"
+args = ["build", "--workspace", "--release", "--target", "x86_64-pc-windows-gnu"]
+
+[tasks.check-windows-deps]
+description = "Check if Windows cross-compilation dependencies are installed"
+script = [
+ "echo Checking Windows cross-compilation setup...",
+ "rustup target list --installed | grep -q x86_64-pc-windows-gnu || (echo 'ERROR: Windows GNU target not installed. Run: rustup target add x86_64-pc-windows-gnu' && exit 1)",
+ "which x86_64-w64-mingw32-gcc >/dev/null 2>&1 || (echo 'ERROR: MinGW-w64 not found. Install with: brew install mingw-w64' && echo 'After installation, add to PATH: export PATH=\"/opt/homebrew/bin:$PATH\" or export PATH=\"/usr/local/bin:$PATH\"' && exit 1)",
+ "echo '✓ All dependencies found'"
+]
+
+[tasks.release-windows-msvc]
+description = "Build release for Windows using MSVC toolchain (requires Windows or special setup)"
+command = "cargo"
+args = ["build", "--workspace", "--release", "--target", "x86_64-pc-windows-msvc"]
+
+[tasks.test]
+description = "Run all tests"
+command = "cargo"
+args = ["test", "--workspace"]
+
+[tasks.test-verbose]
+description = "Run all tests with verbose output"
+command = "cargo"
+args = ["test", "--workspace", "--", "--nocapture"]
+
+[tasks.fmt]
+description = "Format the codebase using rustfmt"
+command = "cargo"
+args = ["fmt", "--all"]
+
+[tasks.fmt-check]
+description = "Check if code is formatted without making changes"
+command = "cargo"
+args = ["fmt", "--all", "--", "--check"]
+
+[tasks.check]
+description = "Check the code without building"
+command = "cargo"
+args = ["check", "--workspace"]
+
+[tasks.clippy]
+description = "Run clippy linter"
+command = "cargo"
+args = ["clippy", "--workspace", "--", "-D", "warnings"]
+
+[tasks.clean]
+description = "Clean build artifacts"
+command = "cargo"
+args = ["clean"]
+
+[tasks.commit]
+description = "Run format, clippy, test, and build"
+dependencies = ["fmt", "check", "test"]
+script = ["git add ."]
+
+[tasks.ci]
+description = "Run format, test"
+dependencies = ["fmt", "check", "test"]
+
+[tasks.install]
+description = "Install the CLI binary"
+command = "cargo"
+args = ["install", "--path", "cli"]
diff --git a/crates/README.md b/crates/README.md
new file mode 100644
index 0000000..6f7c9ba
--- /dev/null
+++ b/crates/README.md
@@ -0,0 +1,138 @@
+# Markdown Editor Server
+
+A Rust-based server and CLI for the Markdown Editor.
+
+## Workspace Structure
+
+```
+crates/
+├── server/ # Library crate - Core Axum server logic
+└── cli/ # Binary crate - CLI for server management (md-server)
+```
+
+## Prerequisites
+
+Install the following tools:
+
+- [Rust](https://www.rust-lang.org/tools/install)
+- [cargo-make](https://crates.io/crates/cargo-make): `cargo install cargo-make`
+- [watchexec](https://github.com/watchexec/watchexec) (for hot-reload): `cargo install watchexec-cli`
+- [systemfd](https://github.com/mitsuhiko/systemfd) (optional, for socket passing): `cargo install systemfd`
+
+## CLI Usage
+
+### Build and Run
+
+```bash
+# Build the workspace
+cargo make build
+
+# Build for release
+cargo make release
+
+# Install the CLI globally
+cargo make install
+```
+
+### Server Management
+
+```bash
+# Start server in foreground
+cargo make start
+# or
+md-server start
+
+# Start server as daemon (background)
+cargo make start-daemon
+# or
+md-server start --daemon
+
+# Check server status
+cargo make status
+# or
+md-server status
+
+# Stop the server daemon
+cargo make stop
+# or
+md-server stop
+```
+
+### Logs
+
+```bash
+# View recent logs
+cargo make logs
+# or
+md-server logs
+
+# Follow logs in real-time
+cargo make logs-follow
+# or
+md-server logs --follow
+
+# Clear all logs
+cargo make logs-clear
+# or
+md-server logs clear
+```
+
+### Development
+
+```bash
+# Run with hot-reload (requires watchexec)
+cargo make dev
+```
+
+This will start the server and the CLI with hot-reload.
+
+To try the cli, can run `pnpm dev-cli` directly.
+
+If not runnable, try `chmod +x ./target/release/mds` and then run `pnpm dev-cli`.
+
+Others:
+
+```bash
+# Run tests
+cargo make test
+
+# Format code
+cargo make fmt
+
+# Run linter
+cargo make clippy
+```
+
+## CLI Options
+
+To show help:
+
+```bash
+mds -h
+```
+
+```
+mds
+
+Commands:
+ start Start the server
+ stop Stop a running daemon
+ status Check if the server is running
+ logs View or manage server logs
+
+Start Options:
+ -d, --daemon Run as a background daemon
+ --host Host to bind to [default: 127.0.0.1]
+ --port Port to listen on [default: 3024]
+
+Logs Options:
+ -t, --tail Show the last N lines [default: 50]
+ -f, --follow Follow the log output
+ clear Clear all log files
+```
+
+If no commands provided, it will start the server as a daemon with defaults.
+
+```bash
+mds
+```
diff --git a/crates/build-macos.sh b/crates/build-macos.sh
new file mode 100755
index 0000000..9d69ae3
--- /dev/null
+++ b/crates/build-macos.sh
@@ -0,0 +1,155 @@
+#!/bin/bash
+set -e
+
+# Build script for creating macOS .app bundle
+# Usage: ./crates/build-macos.sh [--intel|--arm|--universal]
+# --intel Build for Intel Macs only (x86_64)
+# --arm Build for Apple Silicon only (aarch64)
+# --universal Build universal binary (default, works on both)
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
+CRATES_DIR="${PROJECT_ROOT}/crates"
+DIST_DIR="${PROJECT_ROOT}/dist"
+APP_NAME="MDS-Server"
+BINARY_NAME="mds"
+BUNDLE_ID="com.markdown-editor.mds"
+
+# Parse arguments
+BUILD_TYPE="${1:-universal}"
+
+# Read version from project root package.json
+VERSION=$(node -p "require('${PROJECT_ROOT}/package.json').version" 2>/dev/null || echo "1.0.0")
+
+echo "Building MDS-Server v${VERSION}..."
+
+cd "$CRATES_DIR"
+
+# Detect host architecture
+HOST_ARCH=$(uname -m)
+if [ "$HOST_ARCH" = "arm64" ]; then
+ HOST_TARGET="aarch64-apple-darwin"
+else
+ HOST_TARGET="x86_64-apple-darwin"
+fi
+
+# Function to build for a specific target
+build_for_target() {
+ local target=$1
+ local target_name=$2
+
+ echo "Building release binary for ${target_name}..."
+
+ # Set up OpenSSL environment for cross-compilation if needed
+ if [ "$target" != "$HOST_TARGET" ]; then
+ # Try to find OpenSSL via Homebrew for cross-compilation
+ if command -v brew >/dev/null 2>&1; then
+ OPENSSL_DIR=$(brew --prefix openssl@3 2>/dev/null || brew --prefix openssl@1.1 2>/dev/null || echo "")
+ if [ -n "$OPENSSL_DIR" ] && [ -d "$OPENSSL_DIR" ]; then
+ export OPENSSL_DIR
+ export OPENSSL_LIB_DIR="${OPENSSL_DIR}/lib"
+ export OPENSSL_INCLUDE_DIR="${OPENSSL_DIR}/include"
+ echo " Using OpenSSL from: $OPENSSL_DIR"
+ fi
+ fi
+ fi
+
+ cargo build --release -p md-server --target "$target"
+}
+
+# Build based on selected type
+case "$BUILD_TYPE" in
+ --intel)
+ build_for_target "x86_64-apple-darwin" "Intel (x86_64)"
+ BINARY_PATH="target/x86_64-apple-darwin/release/${BINARY_NAME}"
+ ;;
+ --arm)
+ build_for_target "aarch64-apple-darwin" "Apple Silicon (aarch64)"
+ BINARY_PATH="target/aarch64-apple-darwin/release/${BINARY_NAME}"
+ ;;
+ --universal|*)
+ echo "Building universal binary (Intel + Apple Silicon)..."
+ echo " → Building for x86_64-apple-darwin..."
+ build_for_target "x86_64-apple-darwin" "Intel (x86_64)"
+ echo " → Building for aarch64-apple-darwin..."
+ build_for_target "aarch64-apple-darwin" "Apple Silicon (aarch64)"
+ echo " → Creating universal binary with lipo..."
+ mkdir -p target/universal-apple-darwin/release
+ lipo -create -output "target/universal-apple-darwin/release/${BINARY_NAME}" \
+ "target/x86_64-apple-darwin/release/${BINARY_NAME}" \
+ "target/aarch64-apple-darwin/release/${BINARY_NAME}"
+ BINARY_PATH="target/universal-apple-darwin/release/${BINARY_NAME}"
+ ;;
+esac
+
+echo "Creating app bundle..."
+
+# Create dist directory
+mkdir -p "$DIST_DIR"
+
+# Clean up previous build
+rm -rf "${DIST_DIR}/${APP_NAME}.app"
+rm -f "${DIST_DIR}/mds-macos.zip"
+
+# Create app bundle structure
+mkdir -p "${DIST_DIR}/${APP_NAME}.app/Contents/MacOS"
+mkdir -p "${DIST_DIR}/${APP_NAME}.app/Contents/Resources"
+
+# Copy binary
+cp "${BINARY_PATH}" "${DIST_DIR}/${APP_NAME}.app/Contents/MacOS/"
+
+# Create Info.plist
+cat > "${DIST_DIR}/${APP_NAME}.app/Contents/Info.plist" << EOF
+
+
+
+
+ CFBundleExecutable
+ ${BINARY_NAME}
+ CFBundleIdentifier
+ ${BUNDLE_ID}
+ CFBundleName
+ ${APP_NAME}
+ CFBundleDisplayName
+ Markdown Editor Server
+ CFBundleVersion
+ ${VERSION}
+ CFBundleShortVersionString
+ ${VERSION}
+ CFBundlePackageType
+ APPL
+ LSUIElement
+
+ LSMinimumSystemVersion
+ 10.13
+ NSHighResolutionCapable
+
+
+
+EOF
+
+# Create PkgInfo
+echo -n "APPL????" > "${DIST_DIR}/${APP_NAME}.app/Contents/PkgInfo"
+
+echo "Creating zip archive..."
+cd "$DIST_DIR"
+zip -r "mds-macos.zip" "${APP_NAME}.app"
+
+echo ""
+echo "✓ Build complete!"
+echo " Version: ${VERSION}"
+echo " Build type: ${BUILD_TYPE#--}"
+echo " App bundle: ${DIST_DIR}/${APP_NAME}.app"
+echo " Zip file: ${DIST_DIR}/mds-macos.zip"
+echo ""
+echo "To test locally:"
+echo " open '${DIST_DIR}/${APP_NAME}.app'"
+echo ""
+echo "To distribute:"
+echo " Upload dist/mds-macos.zip to GitHub Pages or your download server"
+echo ""
+echo "Build options:"
+echo " ./crates/build-macos.sh --intel # Intel Macs only"
+echo " ./crates/build-macos.sh --arm # Apple Silicon only"
+echo " ./crates/build-macos.sh --universal # Both (default)"
+
diff --git a/crates/build-windows.sh b/crates/build-windows.sh
new file mode 100755
index 0000000..8e9be44
--- /dev/null
+++ b/crates/build-windows.sh
@@ -0,0 +1,100 @@
+#!/bin/bash
+set -e
+
+# Build script for creating Windows executable
+# Usage: ./crates/build-windows.sh [--gnu|--msvc]
+# --gnu Build using GNU toolchain (cross-compile from macOS/Linux, default)
+# --msvc Build using MSVC toolchain (requires Windows or special setup)
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
+CRATES_DIR="${PROJECT_ROOT}/crates"
+DIST_DIR="${PROJECT_ROOT}/dist"
+BINARY_NAME="mds"
+EXE_NAME="${BINARY_NAME}.exe"
+
+# Parse arguments
+BUILD_TYPE="${1:-gnu}"
+
+# Read version from project root package.json
+VERSION=$(node -p "require('${PROJECT_ROOT}/package.json').version" 2>/dev/null || echo "1.0.0")
+
+echo "Building MDS-Server v${VERSION} for Windows..."
+
+cd "$CRATES_DIR"
+
+# Check dependencies before building
+echo "Checking dependencies..."
+
+# Check if building with GNU toolchain
+if [ "$BUILD_TYPE" = "--gnu" ] || [ "$BUILD_TYPE" = "gnu" ]; then
+ # Check if Windows GNU target is installed
+ if ! rustup target list --installed | grep -q "x86_64-pc-windows-gnu"; then
+ echo "ERROR: Windows GNU target not installed."
+ echo "Run: rustup target add x86_64-pc-windows-gnu"
+ exit 1
+ fi
+
+ # Check if MinGW-w64 compiler is available
+ if ! which x86_64-w64-mingw32-gcc >/dev/null 2>&1; then
+ echo "ERROR: MinGW-w64 not found."
+ echo "Install with: brew install mingw-w64"
+ echo "After installation, ensure it's in your PATH"
+ exit 1
+ fi
+
+ echo "✓ Dependencies found"
+fi
+
+# Build based on selected type
+case "$BUILD_TYPE" in
+ --gnu|gnu)
+ echo "Building release binary for Windows (GNU toolchain)..."
+ cargo build --release --workspace --target x86_64-pc-windows-gnu
+ BINARY_PATH="target/x86_64-pc-windows-gnu/release/${EXE_NAME}"
+ ;;
+ --msvc)
+ echo "Building release binary for Windows (MSVC toolchain)..."
+ cargo build --release --workspace --target x86_64-pc-windows-msvc
+ BINARY_PATH="target/x86_64-pc-windows-msvc/release/${EXE_NAME}"
+ ;;
+ *)
+ echo "Unknown build type: $BUILD_TYPE"
+ echo "Use --gnu (default) or --msvc"
+ exit 1
+ ;;
+esac
+
+# Check if binary was created
+if [ ! -f "$BINARY_PATH" ]; then
+ echo "ERROR: Binary not found at $BINARY_PATH"
+ exit 1
+fi
+
+echo "Creating dist directory..."
+mkdir -p "$DIST_DIR"
+
+# Clean up previous build
+rm -f "${DIST_DIR}/${EXE_NAME}"
+rm -f "${DIST_DIR}/mds-windows.zip"
+
+# Copy binary to dist
+echo "Copying binary to dist..."
+cp "$BINARY_PATH" "${DIST_DIR}/${EXE_NAME}"
+
+# Create zip archive
+echo "Creating zip archive..."
+cd "$DIST_DIR"
+zip -q "mds-windows.zip" "${EXE_NAME}"
+
+echo ""
+echo "✓ Build complete!"
+echo " Version: ${VERSION}"
+echo " Build type: ${BUILD_TYPE#--}"
+echo " Binary: ${DIST_DIR}/${EXE_NAME}"
+echo " Zip file: ${DIST_DIR}/mds-windows.zip"
+echo ""
+echo "Build options:"
+echo " ./crates/build-windows.sh --gnu # GNU toolchain (default, cross-compile)"
+echo " ./crates/build-windows.sh --msvc # MSVC toolchain (requires Windows)"
+
diff --git a/crates/build.js b/crates/build.js
new file mode 100755
index 0000000..9e31d0c
--- /dev/null
+++ b/crates/build.js
@@ -0,0 +1,367 @@
+#!/usr/bin/env node
+/* eslint-disable @typescript-eslint/no-var-requires */
+
+/**
+ * Cross-platform build script for MDS-Server
+ * Works on Windows, macOS, and Linux
+ * Usage: node build.js [platform] [options]
+ * platform: macos, windows, or all (default: current platform)
+ * options: --intel, --arm, --universal (for macOS), --gnu, --msvc (for Windows)
+ */
+
+const { execSync } = require('child_process');
+const fs = require('fs');
+const path = require('path');
+const os = require('os');
+
+const SCRIPT_DIR = __dirname;
+const PROJECT_ROOT = path.dirname(SCRIPT_DIR);
+const CRATES_DIR = SCRIPT_DIR;
+const DIST_DIR = path.join(PROJECT_ROOT, 'dist');
+const BINARY_NAME = 'mds';
+
+// Parse arguments
+const args = process.argv.slice(2);
+const platform = args.find((arg) => ['macos', 'windows', 'all'].includes(arg)) || getCurrentPlatform();
+const options = args.filter((arg) => arg.startsWith('--'));
+
+// Get version from package.json
+function getVersion() {
+ try {
+ const pkgPath = path.join(PROJECT_ROOT, 'package.json');
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
+ return pkg.version || '1.0.0';
+ } catch {
+ return '1.0.0';
+ }
+}
+
+function getCurrentPlatform() {
+ const platform = os.platform();
+ if (platform === 'darwin') return 'macos';
+ if (platform === 'win32') return 'windows';
+ return 'linux';
+}
+
+function exec(command, options = {}) {
+ try {
+ console.log(`> ${command}`);
+ execSync(command, {
+ stdio: 'inherit',
+ cwd: CRATES_DIR,
+ ...options,
+ });
+ } catch (error) {
+ console.error(`Error: ${error.message}`);
+ process.exit(1);
+ }
+}
+
+function checkCommand(command, errorMsg) {
+ try {
+ execSync(`which ${command}`, { stdio: 'ignore' });
+ return true;
+ } catch {
+ console.error(errorMsg);
+ return false;
+ }
+}
+
+function checkRustTarget(target, installCmd) {
+ try {
+ const installed = execSync('rustup target list --installed', { encoding: 'utf8' });
+ if (installed.includes(target)) {
+ return true;
+ }
+ console.error(`ERROR: Rust target ${target} not installed.`);
+ console.error(`Run: ${installCmd}`);
+ return false;
+ } catch {
+ console.error('ERROR: rustup not found. Please install Rust.');
+ return false;
+ }
+}
+
+function buildWindows(useGnu = true) {
+ const version = getVersion();
+ console.log(`\nBuilding MDS-Server v${version} for Windows...\n`);
+
+ if (useGnu) {
+ // Check dependencies
+ if (!checkRustTarget('x86_64-pc-windows-gnu', 'rustup target add x86_64-pc-windows-gnu')) {
+ process.exit(1);
+ }
+
+ // Check for MinGW (on Unix systems)
+ if (os.platform() !== 'win32') {
+ if (
+ !checkCommand(
+ 'x86_64-w64-mingw32-gcc',
+ 'ERROR: MinGW-w64 not found.\nInstall with: brew install mingw-w64 (macOS) or apt-get install mingw-w64 (Linux)',
+ )
+ ) {
+ process.exit(1);
+ }
+ }
+
+ console.log('Building release binary for Windows (GNU toolchain)...');
+ exec('cargo build --release --workspace --target x86_64-pc-windows-gnu');
+
+ const exePath = path.join(CRATES_DIR, 'target', 'x86_64-pc-windows-gnu', 'release', 'mds.exe');
+ copyToDist(exePath, 'mds.exe', 'mds-windows.zip');
+ } else {
+ // MSVC (only works on Windows natively)
+ if (os.platform() !== 'win32') {
+ console.error('ERROR: MSVC toolchain requires Windows OS.');
+ console.error('Use --gnu for cross-compilation from macOS/Linux.');
+ process.exit(1);
+ }
+
+ if (!checkRustTarget('x86_64-pc-windows-msvc', 'rustup target add x86_64-pc-windows-msvc')) {
+ process.exit(1);
+ }
+
+ console.log('Building release binary for Windows (MSVC toolchain)...');
+ exec('cargo build --release --workspace --target x86_64-pc-windows-msvc');
+
+ const exePath = path.join(CRATES_DIR, 'target', 'x86_64-pc-windows-msvc', 'release', 'mds.exe');
+ copyToDist(exePath, 'mds.exe', 'mds-windows.zip');
+ }
+}
+
+function buildMacOS(buildType = 'universal') {
+ const version = getVersion();
+ console.log(`\nBuilding MDS-Server v${version} for macOS...\n`);
+
+ // Check if we can build macOS from non-macOS (requires osxcross or similar)
+ if (os.platform() !== 'darwin') {
+ console.warn('WARNING: Building macOS binaries from non-macOS requires special setup.');
+ console.warn('Consider using GitHub Actions with macOS runners for reliable builds.');
+ console.warn('Continuing anyway...\n');
+ }
+
+ let binaryPath;
+ const appName = 'MDS-Server';
+ // const distAppPath = path.join(DIST_DIR, `${appName}.app`);
+
+ if (buildType === 'intel' || buildType === '--intel') {
+ if (!checkRustTarget('x86_64-apple-darwin', 'rustup target add x86_64-apple-darwin')) {
+ process.exit(1);
+ }
+ console.log('Building release binary for Intel (x86_64)...');
+ exec('cargo build --release -p md-server --target x86_64-apple-darwin');
+ binaryPath = path.join(CRATES_DIR, 'target', 'x86_64-apple-darwin', 'release', BINARY_NAME);
+ } else if (buildType === 'arm' || buildType === '--arm') {
+ if (!checkRustTarget('aarch64-apple-darwin', 'rustup target add aarch64-apple-darwin')) {
+ process.exit(1);
+ }
+ console.log('Building release binary for Apple Silicon (aarch64)...');
+ exec('cargo build --release -p md-server --target aarch64-apple-darwin');
+ binaryPath = path.join(CRATES_DIR, 'target', 'aarch64-apple-darwin', 'release', BINARY_NAME);
+ } else {
+ // Universal binary
+ if (!checkRustTarget('x86_64-apple-darwin', 'rustup target add x86_64-apple-darwin')) {
+ process.exit(1);
+ }
+ if (!checkRustTarget('aarch64-apple-darwin', 'rustup target add aarch64-apple-darwin')) {
+ process.exit(1);
+ }
+
+ console.log('Building universal binary (Intel + Apple Silicon)...');
+ console.log(' → Building for x86_64-apple-darwin...');
+ exec('cargo build --release -p md-server --target x86_64-apple-darwin');
+ console.log(' → Building for aarch64-apple-darwin...');
+ exec('cargo build --release -p md-server --target aarch64-apple-darwin');
+
+ // Create universal binary with lipo (macOS only)
+ if (os.platform() === 'darwin') {
+ console.log(' → Creating universal binary with lipo...');
+ const universalDir = path.join(CRATES_DIR, 'target', 'universal-apple-darwin', 'release');
+ if (!fs.existsSync(universalDir)) {
+ fs.mkdirSync(universalDir, { recursive: true });
+ }
+ const universalPath = path.join(universalDir, BINARY_NAME);
+ const intelPath = path.join(CRATES_DIR, 'target', 'x86_64-apple-darwin', 'release', BINARY_NAME);
+ const armPath = path.join(CRATES_DIR, 'target', 'aarch64-apple-darwin', 'release', BINARY_NAME);
+
+ exec(`lipo -create -output "${universalPath}" "${intelPath}" "${armPath}"`);
+ binaryPath = universalPath;
+ } else {
+ console.warn('WARNING: lipo not available. Using aarch64 binary only.');
+ binaryPath = path.join(CRATES_DIR, 'target', 'aarch64-apple-darwin', 'release', BINARY_NAME);
+ }
+ }
+
+ // Create macOS app bundle
+ createMacOSAppBundle(binaryPath, appName, version);
+}
+
+function createMacOSAppBundle(binaryPath, appName, version) {
+ console.log('Creating app bundle...');
+
+ if (!fs.existsSync(DIST_DIR)) {
+ fs.mkdirSync(DIST_DIR, { recursive: true });
+ }
+
+ const appPath = path.join(DIST_DIR, `${appName}.app`);
+ const contentsPath = path.join(appPath, 'Contents');
+ const macosPath = path.join(contentsPath, 'MacOS');
+ const resourcesPath = path.join(contentsPath, 'Resources');
+
+ // Clean up previous build
+ if (fs.existsSync(appPath)) {
+ fs.rmSync(appPath, { recursive: true, force: true });
+ }
+
+ // Create directory structure
+ fs.mkdirSync(macosPath, { recursive: true });
+ fs.mkdirSync(resourcesPath, { recursive: true });
+
+ // Copy binary
+ fs.copyFileSync(binaryPath, path.join(macosPath, BINARY_NAME));
+ fs.chmodSync(path.join(macosPath, BINARY_NAME), 0o755);
+
+ // Create Info.plist
+ const infoPlist = `
+
+
+
+ CFBundleExecutable
+ ${BINARY_NAME}
+ CFBundleIdentifier
+ com.markdown-editor.mds
+ CFBundleName
+ ${appName}
+ CFBundleDisplayName
+ Markdown Editor Server
+ CFBundleVersion
+ ${version}
+ CFBundleShortVersionString
+ ${version}
+ CFBundlePackageType
+ APPL
+ LSUIElement
+
+ LSMinimumSystemVersion
+ 10.13
+ NSHighResolutionCapable
+
+
+ `;
+ fs.writeFileSync(path.join(contentsPath, 'Info.plist'), infoPlist);
+
+ // Create PkgInfo
+ fs.writeFileSync(path.join(contentsPath, 'PkgInfo'), 'APPL????');
+
+ // Code sign the app bundle (ad-hoc signing helps with Gatekeeper)
+ if (os.platform() === 'darwin') {
+ console.log('Code signing app bundle...');
+ try {
+ // Sign the binary first
+ exec(`codesign --force --deep --sign - "${path.join(macosPath, BINARY_NAME)}"`);
+ // Then sign the entire app bundle
+ exec(`codesign --force --deep --sign - "${appPath}"`);
+
+ // Verify the signing worked
+ try {
+ execSync(`codesign --verify --verbose "${appPath}"`, { stdio: 'pipe' });
+ console.log('✓ App bundle signed and verified successfully');
+ } catch (verifyError) {
+ console.warn('Warning: Code signing verification failed, but signing may have succeeded.');
+ }
+ } catch (error) {
+ console.warn('Warning: Code signing failed. The app may be blocked by Gatekeeper.');
+ console.warn('Users may need to remove quarantine attribute: xattr -d com.apple.quarantine MDS-Server.app');
+ console.warn('Or right-click the app and select "Open" instead of double-clicking.');
+ }
+ }
+
+ // Create zip archive
+ console.log('Creating zip archive...');
+ const zipPath = path.join(DIST_DIR, 'mds-macos.zip');
+ if (fs.existsSync(zipPath)) {
+ fs.unlinkSync(zipPath);
+ }
+
+ // Use zip command (available on macOS/Linux, or 7z on Windows)
+ try {
+ if (os.platform() === 'win32') {
+ exec(`powershell Compress-Archive -Path "${appPath}" -DestinationPath "${zipPath}" -Force`);
+ } else {
+ exec(`cd "${DIST_DIR}" && zip -r "mds-macos.zip" "${appName}.app"`);
+ }
+ } catch (error) {
+ console.warn('Warning: Could not create zip archive. Install zip utility.');
+ }
+
+ console.log('\n✓ Build complete!');
+ console.log(` Version: ${version}`);
+ console.log(` App bundle: ${appPath}`);
+ console.log(` Zip file: ${zipPath}`);
+}
+
+function copyToDist(sourcePath, destName, zipName) {
+ if (!fs.existsSync(sourcePath)) {
+ console.error(`ERROR: Binary not found at ${sourcePath}`);
+ process.exit(1);
+ }
+
+ console.log('Creating dist directory...');
+ if (!fs.existsSync(DIST_DIR)) {
+ fs.mkdirSync(DIST_DIR, { recursive: true });
+ }
+
+ const destPath = path.join(DIST_DIR, destName);
+
+ // Clean up previous build
+ if (fs.existsSync(destPath)) {
+ fs.unlinkSync(destPath);
+ }
+ const zipPath = path.join(DIST_DIR, zipName);
+ if (fs.existsSync(zipPath)) {
+ fs.unlinkSync(zipPath);
+ }
+
+ // Copy binary
+ console.log('Copying binary to dist...');
+ fs.copyFileSync(sourcePath, destPath);
+
+ // Create zip archive
+ console.log('Creating zip archive...');
+ try {
+ if (os.platform() === 'win32') {
+ exec(`powershell Compress-Archive -Path "${destPath}" -DestinationPath "${zipPath}" -Force`);
+ } else {
+ exec(`cd "${DIST_DIR}" && zip -q "${zipName}" "${destName}"`);
+ }
+ } catch (error) {
+ console.warn('Warning: Could not create zip archive.');
+ }
+
+ const version = getVersion();
+ console.log('\n✓ Build complete!');
+ console.log(` Version: ${version}`);
+ console.log(` Binary: ${destPath}`);
+ console.log(` Zip file: ${zipPath}`);
+}
+
+// Main execution
+try {
+ if (platform === 'all') {
+ buildMacOS('universal');
+ buildWindows(true);
+ } else if (platform === 'macos') {
+ const buildType = options.find((opt) => ['--intel', '--arm', '--universal'].includes(opt)) || 'universal';
+ buildMacOS(buildType);
+ } else if (platform === 'windows') {
+ const useGnu = !options.includes('--msvc');
+ buildWindows(useGnu);
+ } else {
+ console.error(`Unknown platform: ${platform}`);
+ console.error('Usage: node build.js [macos|windows|all] [--intel|--arm|--universal|--gnu|--msvc]');
+ process.exit(1);
+ }
+} catch (error) {
+ console.error('Build failed:', error.message);
+ process.exit(1);
+}
diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml
new file mode 100644
index 0000000..be22bb9
--- /dev/null
+++ b/crates/cli/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "md-server"
+version = { workspace = true }
+edition = { workspace = true }
+
+[[bin]]
+name = "mds"
+path = "src/main.rs"
+
+[dependencies]
+server = { path = "../server" }
+anyhow = { workspace = true }
+clap = { workspace = true }
+dirs = { workspace = true }
+nix = { workspace = true }
+sysinfo = { workspace = true }
+tokio = { workspace = true }
+tracing = { workspace = true }
+tracing-appender = { workspace = true }
+tracing-subscriber = { workspace = true }
+
+[target.'cfg(unix)'.dependencies]
+daemonize = { workspace = true }
+
+[target.'cfg(windows)'.dependencies]
+winreg = "0.52"
+windows-service = "0.6"
diff --git a/crates/cli/src/commands/install.rs b/crates/cli/src/commands/install.rs
new file mode 100644
index 0000000..e509dc7
--- /dev/null
+++ b/crates/cli/src/commands/install.rs
@@ -0,0 +1,337 @@
+use std::fs;
+#[cfg(target_os = "macos")]
+use std::io::Write;
+use std::path::{Path, PathBuf};
+
+use anyhow::{Context, Result};
+
+use crate::commands::{cmd_start, cmd_uninstall};
+use crate::constants::{DEFAULT_HOST, DEFAULT_PORT};
+
+/// Get the installation directory for the binary
+fn get_install_dir() -> PathBuf {
+ #[cfg(target_os = "macos")]
+ {
+ dirs::home_dir()
+ .unwrap_or_else(|| PathBuf::from("."))
+ .join(".local/bin")
+ }
+ #[cfg(target_os = "windows")]
+ {
+ dirs::data_local_dir()
+ .unwrap_or_else(|| PathBuf::from("."))
+ .join("mds")
+ }
+}
+
+/// Get the path where the binary should be installed
+fn get_install_path() -> PathBuf {
+ #[cfg(target_os = "windows")]
+ {
+ get_install_dir().join("mds.exe")
+ }
+ #[cfg(not(target_os = "windows"))]
+ {
+ get_install_dir().join("mds")
+ }
+}
+
+/// Install the server: copy binary, add to PATH, register autostart, start daemon
+pub fn cmd_install() -> Result<()> {
+ println!("Installing Markdown Editor Server...");
+
+ let install_path = get_install_path();
+
+ if install_path.exists() {
+ println!(
+ "Previous installation detected at {}. Uninstalling...",
+ install_path.display()
+ );
+ cmd_uninstall()?;
+ println!("Previous version removed. Proceeding with fresh install...");
+ }
+
+ let current_exe = std::env::current_exe().context("Failed to get current executable path")?;
+ let install_dir = get_install_dir();
+
+ // Step 1: Copy binary to install location
+ install_binary(¤t_exe, &install_dir, &install_path)?;
+
+ // Step 2: Store the current user's home directory (Windows only)
+ // This ensures the service uses the same home dir as the installing user
+ #[cfg(target_os = "windows")]
+ {
+ if let Some(home) = dirs::home_dir() {
+ use crate::utils::store_home_dir;
+ store_home_dir(&home).ok(); // Best effort - don't fail installation if this fails
+ }
+ }
+
+ // Step 3: Add to PATH
+ add_to_path(&install_dir)?;
+
+ // Step 4: Register autostart
+ register_autostart(&install_path)?;
+
+ // Step 5: Start the daemon
+ println!("Starting server daemon...");
+ cmd_start(true, DEFAULT_HOST.to_string(), DEFAULT_PORT)?;
+
+ println!("\n✓ Installation complete!");
+ println!(" - Binary installed to: {}", install_path.display());
+ println!(
+ " - Server running on http://{}:{}",
+ DEFAULT_HOST, DEFAULT_PORT
+ );
+ println!(" - Server will auto-start on login");
+ println!(" - Use 'mds' command in a new terminal session");
+
+ Ok(())
+}
+
+/// Copy the binary to the install location
+fn install_binary(current_exe: &Path, install_dir: &Path, install_path: &Path) -> Result<()> {
+ // Create install directory if it doesn't exist
+ fs::create_dir_all(install_dir).with_context(|| {
+ format!(
+ "Failed to create install directory: {}",
+ install_dir.display()
+ )
+ })?;
+
+ // Skip if we're already running from the install location
+ if current_exe == install_path {
+ println!("Binary already at install location, overwriting.");
+ }
+
+ // Copy the binary
+ fs::copy(current_exe, install_path)
+ .with_context(|| format!("Failed to copy binary to {}", install_path.display()))?;
+
+ // Make executable on Unix
+ #[cfg(unix)]
+ {
+ use std::os::unix::fs::PermissionsExt;
+ let mut perms = fs::metadata(install_path)?.permissions();
+ perms.set_mode(0o755);
+ fs::set_permissions(install_path, perms)?;
+ }
+
+ println!("Binary installed to: {}", install_path.display());
+ Ok(())
+}
+
+/// Add the install directory to PATH
+#[cfg(target_os = "macos")]
+fn add_to_path(install_dir: &Path) -> Result<()> {
+ let symlink_path = Path::new("/usr/local/bin/mds");
+
+ // Try to create symlink to /usr/local/bin first
+ if !symlink_path.exists() {
+ let install_path = install_dir.join("mds");
+ if std::os::unix::fs::symlink(&install_path, symlink_path).is_ok() {
+ println!(
+ "Created symlink: {} -> {}",
+ symlink_path.display(),
+ install_path.display()
+ );
+ return Ok(());
+ }
+ // Symlink failed (likely no permissions), fall back to shell config
+ } else {
+ println!("Symlink already exists at {}", symlink_path.display());
+ return Ok(());
+ }
+
+ // Fall back to adding to shell config
+ add_to_shell_config(install_dir)?;
+ Ok(())
+}
+
+/// Add to shell config files (.zshrc, .bashrc)
+#[cfg(target_os = "macos")]
+fn add_to_shell_config(install_dir: &Path) -> Result<()> {
+ let home = dirs::home_dir().context("Could not find home directory")?;
+ let export_line = format!(
+ "\n# Markdown Editor Server\nexport PATH=\"{}:$PATH\"\n",
+ install_dir.display()
+ );
+
+ // Add to .zshrc (default shell on modern macOS)
+ let zshrc = home.join(".zshrc");
+ add_export_to_file(&zshrc, &export_line)?;
+
+ // Also add to .bashrc for bash users
+ let bashrc = home.join(".bashrc");
+ if bashrc.exists() {
+ add_export_to_file(&bashrc, &export_line)?;
+ }
+
+ println!("Added {} to PATH in shell config", install_dir.display());
+ println!("Note: Open a new terminal for the 'mds' command to be available");
+
+ Ok(())
+}
+
+#[cfg(target_os = "macos")]
+fn add_export_to_file(file_path: &Path, export_line: &str) -> Result<()> {
+ let content = fs::read_to_string(file_path).unwrap_or_default();
+
+ // Check if already added
+ if content.contains("Markdown Editor Server") {
+ return Ok(());
+ }
+
+ let mut file = fs::OpenOptions::new()
+ .create(true)
+ .append(true)
+ .open(file_path)
+ .with_context(|| format!("Failed to open {}", file_path.display()))?;
+
+ file.write_all(export_line.as_bytes())?;
+ Ok(())
+}
+
+/// Add the install directory to PATH (Windows)
+#[cfg(target_os = "windows")]
+fn add_to_path(install_dir: &Path) -> Result<()> {
+ use winreg::RegKey;
+ use winreg::enums::*;
+
+ let hkcu = RegKey::predef(HKEY_CURRENT_USER);
+ let env = hkcu
+ .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
+ .context("Failed to open Environment registry key")?;
+
+ let current_path: String = env.get_value("Path").unwrap_or_default();
+ let install_dir_str = install_dir.to_string_lossy();
+
+ if current_path
+ .to_lowercase()
+ .contains(&install_dir_str.to_lowercase())
+ {
+ println!("Install directory already in PATH");
+ return Ok(());
+ }
+
+ let new_path = if current_path.is_empty() {
+ install_dir_str.to_string()
+ } else {
+ format!("{};{}", current_path, install_dir_str)
+ };
+
+ env
+ .set_value("Path", &new_path)
+ .context("Failed to update PATH in registry")?;
+
+ println!("Added {} to user PATH", install_dir.display());
+ println!("Note: Open a new terminal for the 'mds' command to be available");
+
+ // Broadcast WM_SETTINGCHANGE to notify other applications
+ broadcast_environment_change();
+
+ Ok(())
+}
+
+/// Broadcast environment change on Windows
+#[cfg(target_os = "windows")]
+fn broadcast_environment_change() {
+ // The change will take effect in new terminal sessions regardless
+}
+
+/// Register autostart on login (macOS)
+#[cfg(target_os = "macos")]
+fn register_autostart(exe_path: &Path) -> Result<()> {
+ let plist_content = format!(
+ r#"
+
+
+
+ Label
+ com.markdown-editor.mds
+ ProgramArguments
+
+ {}
+ start
+ --daemon
+
+ RunAtLoad
+
+ KeepAlive
+
+
+ "#,
+ exe_path.display()
+ );
+
+ let launch_agents_dir = dirs::home_dir()
+ .context("Could not find home directory")?
+ .join("Library/LaunchAgents");
+
+ fs::create_dir_all(&launch_agents_dir)?;
+
+ let plist_path = launch_agents_dir.join("com.markdown-editor.mds.plist");
+ fs::write(&plist_path, plist_content)?;
+
+ // Load the LaunchAgent immediately
+ use crate::utils::system_commands;
+ let success = system_commands::load_launch_agent(plist_path.to_str().unwrap()).unwrap_or(false);
+
+ if success {
+ println!("Registered autostart via LaunchAgent");
+ } else {
+ println!("LaunchAgent created but could not load immediately");
+ }
+
+ Ok(())
+}
+
+/// Register autostart on login (Windows)
+#[cfg(target_os = "windows")]
+fn register_autostart(exe_path: &Path) -> Result<()> {
+ use crate::utils::{CheckAutoStartStatus, is_autostart_registered, system_commands};
+
+ let exe_path_str = exe_path.to_string_lossy();
+
+ let auto_start_status = is_autostart_registered()?;
+
+ match auto_start_status {
+ CheckAutoStartStatus::Registered => {
+ println!("Service already registered for auto-start");
+ return Ok(());
+ }
+ CheckAutoStartStatus::NotRegistered => {
+ println!("Updating service to auto-start...");
+ let status =
+ system_commands::config_windows_service_start_type("MarkdownEditorServer", "auto")?;
+ if status.success() {
+ println!("Service updated to auto-start");
+ } else {
+ anyhow::bail!(
+ "Failed to update service start type (exit code: {})",
+ status.code().unwrap_or(-1)
+ );
+ }
+ }
+ CheckAutoStartStatus::NotExist => {
+ // Service doesn't exist, create it with auto-start
+ println!("Creating service with auto-start...");
+ let created = system_commands::create_windows_service(
+ "MarkdownEditorServer",
+ &exe_path_str,
+ "Markdown Editor Server",
+ "auto",
+ )?;
+ if created {
+ println!("Service created with auto-start");
+ } else {
+ anyhow::bail!("Failed to create service");
+ }
+ }
+ CheckAutoStartStatus::Error => {
+ anyhow::bail!("Error checking autostart registration status");
+ }
+ }
+
+ Ok(())
+}
diff --git a/crates/cli/src/commands/location.rs b/crates/cli/src/commands/location.rs
new file mode 100644
index 0000000..fbd7b15
--- /dev/null
+++ b/crates/cli/src/commands/location.rs
@@ -0,0 +1,19 @@
+use std::env;
+
+use anyhow::{Context, Result};
+
+use crate::utils::app_data_dir;
+
+/// Show the current executable location and app data location
+pub fn cmd_location() -> Result<()> {
+ // Get the current executable path
+ let exe_path = env::current_exe().context("Failed to get current executable path")?;
+
+ // Get the app data directory
+ let app_data_path = app_data_dir();
+
+ println!("Executable location: {}", exe_path.display());
+ println!("App data location: {}", app_data_path.display());
+
+ Ok(())
+}
diff --git a/crates/cli/src/commands/logs.rs b/crates/cli/src/commands/logs.rs
new file mode 100644
index 0000000..d1eef99
--- /dev/null
+++ b/crates/cli/src/commands/logs.rs
@@ -0,0 +1,96 @@
+use std::fs;
+use std::io::{BufRead, BufReader, Read, Seek, SeekFrom};
+
+use anyhow::{Context, Result};
+
+use crate::constants::default_log_dir;
+
+/// View server logs
+pub fn cmd_logs_view(tail: usize, follow: bool) -> Result<()> {
+ let log_dir = default_log_dir();
+
+ // Find the most recent log file
+ let log_files: Vec<_> = fs::read_dir(&log_dir)
+ .context("Failed to read logs directory")?
+ .filter_map(|e| e.ok())
+ .filter(|e| {
+ e.path()
+ .file_name()
+ .and_then(|n| n.to_str())
+ .map(|n| n.starts_with("server.log"))
+ .unwrap_or(false)
+ })
+ .collect();
+
+ if log_files.is_empty() {
+ println!("No log files found in {:?}", log_dir);
+ return Ok(());
+ }
+
+ // Get the most recent log file
+ let mut log_files: Vec<_> = log_files.into_iter().map(|e| e.path()).collect();
+ log_files.sort();
+ let log_file = log_files.last().unwrap();
+
+ println!("Reading from: {:?}\n", log_file);
+
+ let file = fs::File::open(log_file)?;
+ let reader = BufReader::new(file);
+
+ // Read all lines and show the last N
+ let lines: Vec = reader.lines().filter_map(|l| l.ok()).collect();
+ let start = lines.len().saturating_sub(tail);
+
+ for line in &lines[start..] {
+ println!("{}", line);
+ }
+
+ if follow {
+ println!("\n--- Following log output (Ctrl+C to stop) ---\n");
+
+ let mut file = fs::File::open(log_file)?;
+ file.seek(SeekFrom::End(0))?;
+
+ let mut buffer = String::new();
+ loop {
+ buffer.clear();
+ match file.read_to_string(&mut buffer) {
+ Ok(0) => {
+ std::thread::sleep(std::time::Duration::from_millis(100));
+ }
+ Ok(_) => {
+ print!("{}", buffer);
+ }
+ Err(e) => {
+ eprintln!("Error reading log: {}", e);
+ break;
+ }
+ }
+ }
+ }
+
+ Ok(())
+}
+
+/// Clear all log files
+pub fn cmd_logs_clear() -> Result<()> {
+ let log_dir = default_log_dir();
+
+ if !log_dir.exists() {
+ println!("No logs directory found");
+ return Ok(());
+ }
+
+ let mut count = 0;
+ for entry in fs::read_dir(&log_dir)? {
+ let entry = entry?;
+ if entry.path().is_file() {
+ fs::remove_file(entry.path())?;
+ count += 1;
+ }
+ }
+
+ println!("Cleared {} log file(s)", count);
+
+ Ok(())
+}
diff --git a/crates/cli/src/commands/mod.rs b/crates/cli/src/commands/mod.rs
new file mode 100644
index 0000000..683fc81
--- /dev/null
+++ b/crates/cli/src/commands/mod.rs
@@ -0,0 +1,15 @@
+mod install;
+mod location;
+mod logs;
+mod start;
+mod status;
+mod stop;
+mod uninstall;
+
+pub use install::cmd_install;
+pub use location::cmd_location;
+pub use logs::{cmd_logs_clear, cmd_logs_view};
+pub use start::cmd_start;
+pub use status::cmd_status;
+pub use stop::cmd_stop;
+pub use uninstall::cmd_uninstall;
diff --git a/crates/cli/src/commands/start.rs b/crates/cli/src/commands/start.rs
new file mode 100644
index 0000000..d3e77a2
--- /dev/null
+++ b/crates/cli/src/commands/start.rs
@@ -0,0 +1,158 @@
+use std::fs;
+use std::path::PathBuf;
+
+use anyhow::Result;
+use server::ServerConfig;
+
+use crate::{
+ constants::{default_editor_settings_file, default_log_dir, default_pid_file},
+ utils::{is_process_running, read_pid_file},
+};
+
+/// Start the server (foreground or daemon mode)
+pub fn cmd_start(daemon: bool, host: String, port: u16) -> Result<()> {
+ let pid_file = default_pid_file();
+
+ // Check if already running
+ if let Some(pid) = read_pid_file(&pid_file) {
+ if is_process_running(pid) {
+ println!(
+ "Server is already running at {}:{} with PID {}",
+ host, port, pid
+ );
+ return Ok(());
+ }
+ // Stale PID file, remove it
+ let _ = fs::remove_file(&pid_file);
+ }
+
+ if daemon {
+ start_daemon(host, port, &pid_file)?;
+ } else {
+ start_foreground(host, port)?;
+ }
+
+ Ok(())
+}
+
+/// Start the server in foreground mode
+fn start_foreground(host: String, port: u16) -> Result<()> {
+ println!("Starting server on {}:{}...", host, port);
+
+ let config = ServerConfig {
+ host,
+ port,
+ log_dir: default_log_dir(),
+ log_to_terminal: true,
+ editor_settings_file: default_editor_settings_file(),
+ };
+
+ let rt = tokio::runtime::Runtime::new()?;
+ rt.block_on(server::run_server(config))?;
+
+ Ok(())
+}
+
+/// Start the server as a background daemon
+#[cfg(unix)]
+fn start_daemon(host: String, port: u16, pid_file: &PathBuf) -> Result<()> {
+ use daemonize::Daemonize;
+
+ println!("Starting server daemon on {}:{}...", host, port);
+
+ // Ensure PID file directory exists
+ if let Some(parent) = pid_file.parent() {
+ fs::create_dir_all(parent)?;
+ } else {
+ anyhow::bail!(
+ "Could not determine parent directory for PID file: {}",
+ pid_file.display()
+ );
+ }
+
+ let daemonize = Daemonize::new()
+ .pid_file(pid_file)
+ .chown_pid_file(true)
+ .working_directory(".");
+
+ // Daemonize - after this, we ARE the daemon process
+ match daemonize.start() {
+ Ok(_) => {
+ // Now running as daemon - start the server
+ let config = ServerConfig {
+ host,
+ port,
+ log_dir: default_log_dir(),
+ log_to_terminal: false,
+ editor_settings_file: default_editor_settings_file(),
+ };
+
+ let rt = tokio::runtime::Runtime::new()?;
+ rt.block_on(server::run_server(config))?;
+
+ println!("Server started.");
+ }
+ Err(e) => {
+ anyhow::bail!("Failed to daemonize: {}", e);
+ }
+ }
+
+ Ok(())
+}
+
+/// Start the server as a Windows service
+#[cfg(target_os = "windows")]
+fn start_daemon(host: String, port: u16, pid_file: &PathBuf) -> Result<()> {
+ use crate::utils::{get_and_write_service_pid, system_commands};
+
+ println!("Starting server service on {}:{}...", host, port);
+
+ // Check if service exists
+ let service_exists =
+ system_commands::query_windows_service("MarkdownEditorServer").unwrap_or(false);
+
+ if !service_exists {
+ // Service not installed, create it
+ println!("Service not installed, registering service...");
+ let exe_path = std::env::current_exe()?;
+ let created = system_commands::create_windows_service(
+ "MarkdownEditorServer",
+ &exe_path.to_string_lossy(),
+ "Markdown Editor Server",
+ "demand", // Manual start
+ )?;
+
+ if !created {
+ anyhow::bail!("Failed to create service");
+ }
+ println!("Service registered.");
+ }
+
+ // Start the Windows service
+ let status = system_commands::start_windows_service("MarkdownEditorServer")?;
+
+ if status.success() {
+ println!("Server service started.");
+ // Get the service PID and write to file
+ get_and_write_service_pid(pid_file)?;
+ } else {
+ if status.code() == Some(1056) {
+ println!("Server service is already running.");
+ // Get the service PID and write to file
+ get_and_write_service_pid(pid_file)?;
+ } else {
+ anyhow::bail!(
+ "Failed to start service (exit code: {})",
+ status.code().unwrap_or(-1)
+ );
+ }
+ }
+
+ Ok(())
+}
+
+/// Start the server as a background daemon (unsupported platforms)
+#[cfg(not(any(unix, windows)))]
+fn start_daemon(_host: String, _port: u16, _pid_file: &PathBuf) -> Result<()> {
+ anyhow::bail!("Daemon mode is not supported on this platform. Use foreground mode instead.");
+}
diff --git a/crates/cli/src/commands/status.rs b/crates/cli/src/commands/status.rs
new file mode 100644
index 0000000..2020978
--- /dev/null
+++ b/crates/cli/src/commands/status.rs
@@ -0,0 +1,63 @@
+use std::fs;
+
+use anyhow::Result;
+
+use crate::{
+ constants::default_pid_file,
+ utils::{is_process_running, read_pid_file},
+};
+
+/// Check if the server is running
+pub fn cmd_status() -> Result<()> {
+ #[cfg(target_os = "windows")]
+ {
+ // On Windows, first check if the service is running
+ use crate::utils::get_service_pid;
+
+ if let Some(pid) = get_service_pid() {
+ if is_process_running(pid) {
+ println!("Server service is running with PID {}", pid);
+ } else {
+ // Service exists but not running
+ println!("Server service exists but is not running");
+ }
+ }
+ }
+
+ // Fallback to PID file check (Unix or if service check failed)
+ let pid_file = default_pid_file();
+
+ match read_pid_file(&pid_file) {
+ Some(pid) => {
+ if is_process_running(pid) {
+ println!("Server is running with PID {}", pid);
+ } else {
+ println!("Server is not running (stale PID file)");
+ let _ = fs::remove_file(&pid_file);
+ }
+ }
+ None => {
+ println!("Server is not running (no PID file)");
+ }
+ }
+
+ // Show autostart status (only on Windows for now)
+ #[cfg(target_os = "windows")]
+ {
+ use crate::utils::{CheckAutoStartStatus, is_autostart_registered};
+
+ match is_autostart_registered() {
+ Ok(CheckAutoStartStatus::Registered) => {
+ println!("Server is registered for auto-start");
+ }
+ Ok(CheckAutoStartStatus::NotExist) => {
+ println!("Server does not exist");
+ }
+ _ => {
+ println!("Server is not registered for auto-start");
+ }
+ }
+ }
+
+ Ok(())
+}
diff --git a/crates/cli/src/commands/stop.rs b/crates/cli/src/commands/stop.rs
new file mode 100644
index 0000000..2f94a9e
--- /dev/null
+++ b/crates/cli/src/commands/stop.rs
@@ -0,0 +1,111 @@
+use std::fs;
+
+#[cfg(windows)]
+use std::path::PathBuf;
+
+use anyhow::{Context, Result};
+
+use crate::{
+ constants::default_pid_file,
+ utils::{is_process_running, read_pid_file},
+};
+
+#[cfg(windows)]
+fn stop_service(pid_file: &PathBuf) -> () {
+ // On Windows, stop the service
+ use crate::utils::system_commands;
+
+ println!("Stopping server service...");
+ match system_commands::stop_windows_service("MarkdownEditorServer") {
+ Ok(s) if s.success() => {
+ println!("Server service stopped.");
+ // Clean up PID file if it exists
+ let _ = fs::remove_file(&pid_file);
+ }
+ Ok(s) => {
+ if s.code() == Some(1062) {
+ println!("Server service is not running.");
+ // Clean up PID file if it exists
+ let _ = fs::remove_file(&pid_file);
+ } else {
+ println!(
+ "Warning: Failed to stop service (exit code: {})",
+ s.code().unwrap_or(-1)
+ );
+ }
+ }
+ Err(e) => {
+ println!("Warning: Failed to stop service: {}", e);
+ }
+ }
+}
+
+/// Stop a running daemon
+pub fn cmd_stop() -> Result<()> {
+ // Fallback to PID-based stop (for Unix or if service stop failed)
+ let pid_file = default_pid_file();
+
+ let pid = match read_pid_file(&pid_file) {
+ Some(p) => p,
+ None => {
+ #[cfg(target_os = "macos")]
+ {
+ println!("No PID file found. Server may not be running.");
+ return Ok(());
+ }
+
+ #[cfg(target_os = "windows")]
+ {
+ use crate::utils::get_service_pid;
+
+ println!("No PID file found. Attempting to get the service PID...");
+ get_service_pid().unwrap_or(0)
+ }
+ }
+ };
+
+ if !is_process_running(pid) {
+ println!("Server is not running (stale PID file)");
+ let _ = fs::remove_file(&pid_file);
+ return Ok(());
+ }
+
+ println!("Stopping server with PID {}...", pid);
+
+ #[cfg(unix)]
+ {
+ use nix::sys::signal::{Signal, kill};
+ use nix::unistd::Pid;
+
+ kill(Pid::from_raw(pid as i32), Signal::SIGTERM).context("Failed to send SIGTERM")?;
+ }
+
+ #[cfg(windows)]
+ {
+ use crate::utils::system_commands;
+ system_commands::kill_windows_process(pid).context("Failed to kill process")?;
+ }
+
+ // Wait a moment and verify
+ std::thread::sleep(std::time::Duration::from_millis(500));
+
+ if is_process_running(pid) {
+ println!("Server is still running. Sending SIGKILL...");
+ #[cfg(unix)]
+ {
+ use nix::sys::signal::{Signal, kill};
+ use nix::unistd::Pid;
+ let _ = kill(Pid::from_raw(pid as i32), Signal::SIGKILL);
+ }
+ }
+
+ let _ = fs::remove_file(&pid_file);
+ println!("Server stopped");
+
+ #[cfg(windows)]
+ {
+ stop_service(&pid_file);
+ }
+
+ Ok(())
+}
diff --git a/crates/cli/src/commands/uninstall.rs b/crates/cli/src/commands/uninstall.rs
new file mode 100644
index 0000000..05bf6c6
--- /dev/null
+++ b/crates/cli/src/commands/uninstall.rs
@@ -0,0 +1,199 @@
+#[cfg(target_os = "macos")]
+use std::path::Path;
+
+use std::fs;
+
+use std::path::PathBuf;
+
+use anyhow::{Context, Result};
+
+use crate::commands::cmd_stop;
+
+use crate::utils::remove_file_with_retry;
+
+/// Get the installation directory for the binary
+fn get_install_dir() -> PathBuf {
+ #[cfg(target_os = "macos")]
+ {
+ dirs::home_dir()
+ .unwrap_or_else(|| PathBuf::from("."))
+ .join(".local/bin")
+ }
+ #[cfg(target_os = "windows")]
+ {
+ dirs::data_local_dir()
+ .unwrap_or_else(|| PathBuf::from("."))
+ .join("mds")
+ }
+}
+
+/// Get the path where the binary is installed
+fn get_install_path() -> PathBuf {
+ #[cfg(target_os = "windows")]
+ {
+ get_install_dir().join("mds.exe")
+ }
+ #[cfg(not(target_os = "windows"))]
+ {
+ get_install_dir().join("mds")
+ }
+}
+
+/// Uninstall the server: stop daemon, remove autostart, remove from PATH, delete binary
+pub fn cmd_uninstall() -> Result<()> {
+ println!("Uninstalling Markdown Editor Server...");
+
+ // Step 1: Stop the running daemon
+ println!("Stopping server...");
+ let _ = cmd_stop(); // Ignore errors if not running
+
+ // Step 2: Remove autostart
+ remove_autostart()?;
+
+ // Step 3: Remove from PATH
+ remove_from_path()?;
+
+ // Step 4: Remove the binary
+ remove_binary()?;
+
+ // Step 5: Clean up app data (optional - keep user settings)
+ // We don't remove ~/.md-server to preserve user settings
+
+ println!("\n✓ Uninstallation complete!");
+ println!(" Note: User settings in ~/.md-server were preserved.");
+ println!(" To remove all data, delete ~/.md-server manually.");
+
+ Ok(())
+}
+
+/// Remove the installed binary
+fn remove_binary() -> Result<()> {
+ let install_path = get_install_path();
+
+ if !install_path.exists() {
+ println!("Binary not found at {}", install_path.display());
+ return Ok(());
+ }
+
+ // On Windows, try to remove read-only attributes first
+ #[cfg(target_os = "windows")]
+ {
+ use crate::utils::remove_readonly_attributes;
+ remove_readonly_attributes(&install_path)?;
+ }
+
+ remove_file_with_retry(&install_path)?;
+
+ // Try to remove the install directory if empty
+ let install_dir = get_install_dir();
+ if install_dir
+ .read_dir()
+ .map(|mut d| d.next().is_none())
+ .unwrap_or(false)
+ {
+ let _ = fs::remove_dir(&install_dir);
+ }
+
+ Ok(())
+}
+
+/// Remove autostart registration (macOS)
+#[cfg(target_os = "macos")]
+fn remove_autostart() -> Result<()> {
+ use crate::utils::system_commands;
+
+ let plist_path = dirs::home_dir()
+ .context("Could not find home directory")?
+ .join("Library/LaunchAgents/com.markdown-editor.mds.plist");
+
+ if plist_path.exists() {
+ // Unload the LaunchAgent first
+ let _ = system_commands::unload_launch_agent(plist_path.to_str().unwrap());
+
+ fs::remove_file(&plist_path)?;
+ println!("Removed LaunchAgent");
+ }
+
+ Ok(())
+}
+
+/// Remove autostart registration (Windows)
+#[cfg(target_os = "windows")]
+fn remove_autostart() -> Result<()> {
+ use crate::utils::system_commands;
+
+ // Delete the service
+ match system_commands::delete_windows_service("MarkdownEditorServer") {
+ Ok(s) if s.success() => {
+ println!("Removed service");
+ }
+ Ok(s) => {
+ println!(
+ "Warning: Failed to delete service (exit code: {}), it may need manual removal",
+ s.code().unwrap_or(-1)
+ );
+ }
+ Err(e) => {
+ println!("Warning: Failed to delete service: {}", e);
+ }
+ }
+
+ Ok(())
+}
+
+/// Remove from PATH (macOS)
+#[cfg(target_os = "macos")]
+fn remove_from_path() -> Result<()> {
+ use crate::utils::remove_from_shell_config;
+
+ // Remove symlink if it exists
+ let symlink_path = Path::new("/usr/local/bin/mds");
+ if symlink_path.exists() || symlink_path.is_symlink() {
+ match fs::remove_file(symlink_path) {
+ Ok(_) => println!("Removed symlink: {}", symlink_path.display()),
+ Err(_) => println!("Could not remove symlink (may need sudo)"),
+ }
+ }
+
+ // Remove from shell configs
+ let home = dirs::home_dir().context("Could not find home directory")?;
+ let install_dir = get_install_dir();
+
+ remove_from_shell_config(&home.join(".zshrc"), &install_dir)?;
+ remove_from_shell_config(&home.join(".bashrc"), &install_dir)?;
+
+ Ok(())
+}
+
+/// Remove from PATH (Windows)
+#[cfg(target_os = "windows")]
+fn remove_from_path() -> Result<()> {
+ use winreg::RegKey;
+ use winreg::enums::*;
+
+ let hkcu = RegKey::predef(HKEY_CURRENT_USER);
+ let env = hkcu
+ .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE)
+ .context("Failed to open Environment registry key")?;
+
+ let current_path: String = env.get_value("Path").unwrap_or_default();
+ let install_dir = get_install_dir();
+ let install_dir_str = install_dir.to_string_lossy();
+
+ if current_path
+ .to_lowercase()
+ .contains(&install_dir_str.to_lowercase())
+ {
+ // Remove the install directory from PATH
+ let new_path: String = current_path
+ .split(';')
+ .filter(|p| !p.eq_ignore_ascii_case(&install_dir_str))
+ .collect::>()
+ .join(";");
+
+ env.set_value("Path", &new_path)?;
+ println!("Removed {} from PATH", install_dir.display());
+ }
+
+ Ok(())
+}
diff --git a/crates/cli/src/constants.rs b/crates/cli/src/constants.rs
new file mode 100644
index 0000000..3d58167
--- /dev/null
+++ b/crates/cli/src/constants.rs
@@ -0,0 +1,22 @@
+use std::path::PathBuf;
+
+use crate::utils::app_data_dir;
+
+#[cfg(debug_assertions)]
+pub const DEFAULT_PORT: u16 = 3024;
+#[cfg(not(debug_assertions))]
+pub const DEFAULT_PORT: u16 = 7024;
+
+pub const DEFAULT_HOST: &str = "127.0.0.1";
+
+pub fn default_log_dir() -> PathBuf {
+ app_data_dir().join("logs")
+}
+
+pub fn default_pid_file() -> PathBuf {
+ app_data_dir().join("mds.pid")
+}
+
+pub fn default_editor_settings_file() -> PathBuf {
+ app_data_dir().join("editor-settings.json")
+}
diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs
new file mode 100644
index 0000000..4bb75c8
--- /dev/null
+++ b/crates/cli/src/main.rs
@@ -0,0 +1,212 @@
+mod commands;
+mod constants;
+mod utils;
+
+use anyhow::Result;
+use clap::{Parser, Subcommand};
+
+use commands::{
+ cmd_install, cmd_location, cmd_logs_clear, cmd_logs_view, cmd_start, cmd_status, cmd_stop,
+ cmd_uninstall,
+};
+use constants::DEFAULT_PORT;
+
+use crate::constants::DEFAULT_HOST;
+
+#[cfg(target_os = "windows")]
+use std::ffi::OsString;
+#[cfg(target_os = "windows")]
+use windows_service::define_windows_service;
+
+#[cfg(target_os = "windows")]
+define_windows_service!(ffi_service_main, my_service_main);
+
+#[cfg(target_os = "windows")]
+fn my_service_main(_arguments: Vec) {
+ use std::sync::mpsc;
+ use std::time::Duration;
+ use windows_service::service::{
+ ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus, ServiceType,
+ };
+ use windows_service::service_control_handler::{self, ServiceControlHandlerResult};
+
+ let (shutdown_tx, shutdown_rx) = mpsc::channel();
+
+ let event_handler = move |control_event| -> ServiceControlHandlerResult {
+ match control_event {
+ ServiceControl::Stop => {
+ let _ = shutdown_tx.send(());
+ ServiceControlHandlerResult::NoError
+ }
+ _ => ServiceControlHandlerResult::NotImplemented,
+ }
+ };
+
+ let status_handle =
+ service_control_handler::register("MarkdownEditorServer", event_handler).unwrap();
+
+ status_handle
+ .set_service_status(ServiceStatus {
+ service_type: ServiceType::OWN_PROCESS,
+ current_state: ServiceState::Running,
+ controls_accepted: ServiceControlAccept::STOP,
+ exit_code: ServiceExitCode::Win32(0),
+ checkpoint: 0,
+ wait_hint: Duration::default(),
+ process_id: None,
+ })
+ .unwrap();
+
+ // Run the server
+ // Compute paths in the service thread's context (they may differ from CLI user context)
+ // This ensures the service uses the same paths as intended
+ let log_dir = crate::constants::default_log_dir();
+ let editor_settings_file = crate::constants::default_editor_settings_file();
+
+ let config = server::ServerConfig {
+ host: DEFAULT_HOST.to_string(),
+ port: DEFAULT_PORT,
+ log_dir,
+ log_to_terminal: false,
+ editor_settings_file,
+ };
+
+ let rt = tokio::runtime::Runtime::new().unwrap();
+ let _server_future = rt.spawn(async move {
+ server::run_server(config).await.unwrap();
+ });
+
+ // Wait for shutdown signal
+ let _ = shutdown_rx.recv();
+
+ // Stop the server
+ rt.shutdown_timeout(Duration::from_secs(5));
+
+ status_handle
+ .set_service_status(ServiceStatus {
+ service_type: ServiceType::OWN_PROCESS,
+ current_state: ServiceState::Stopped,
+ controls_accepted: ServiceControlAccept::empty(),
+ exit_code: ServiceExitCode::Win32(0),
+ checkpoint: 0,
+ wait_hint: Duration::default(),
+ process_id: None,
+ })
+ .unwrap();
+}
+
+#[derive(Parser)]
+#[command(name = "md-server", version, about = "Markdown Editor Server CLI")]
+struct Cli {
+ /// Show the current executable location and app data location
+ #[arg(long, short = 'l')]
+ location: bool,
+
+ #[command(subcommand)]
+ command: Option,
+}
+
+#[derive(Subcommand)]
+enum Commands {
+ /// Start the server
+ Start {
+ /// Run as a background daemon
+ #[arg(long, short)]
+ daemon: bool,
+
+ /// Host to bind to
+ #[arg(long, default_value = DEFAULT_HOST)]
+ host: String,
+
+ /// Port to listen on
+ #[arg(long, default_value_t = DEFAULT_PORT)]
+ port: u16,
+ },
+
+ /// Stop a running daemon
+ Stop,
+
+ /// Check if the server is running
+ Status,
+
+ /// Install the server (copy binary, add to PATH, register autostart)
+ Install,
+
+ /// Uninstall the server (remove binary, PATH entry, and autostart)
+ Uninstall,
+
+ /// View or manage server logs
+ Logs {
+ #[command(subcommand)]
+ cmd: Option,
+
+ /// Show the last N lines
+ #[arg(long, short, default_value = "50")]
+ tail: usize,
+
+ /// Follow the log output (like tail -f)
+ #[arg(long, short)]
+ follow: bool,
+ },
+}
+
+#[derive(Subcommand)]
+enum LogsCmd {
+ /// Clear all log files
+ Clear,
+}
+
+fn main() -> Result<()> {
+ // On Windows, check if running as a service
+ #[cfg(target_os = "windows")]
+ {
+ use windows_service::service_dispatcher;
+
+ // If dispatched as service, run service_main
+ if let Err(_) = service_dispatcher::start("MarkdownEditorServer", ffi_service_main) {
+ // Not running as service, proceed with CLI
+ }
+ }
+
+ let cli = Cli::parse();
+
+ // Handle location flag first
+ if cli.location {
+ cmd_location()?;
+ return Ok(());
+ }
+
+ match cli.command {
+ None => {
+ // Install anyway if exists, just overwrite
+ cmd_install()?;
+
+ println!("Run 'mds -h' for more information.");
+ }
+ Some(Commands::Start { daemon, host, port }) => {
+ cmd_start(daemon, host, port)?;
+ }
+ Some(Commands::Stop) => {
+ cmd_stop()?;
+ }
+ Some(Commands::Status) => {
+ cmd_status()?;
+ }
+ Some(Commands::Install) => {
+ cmd_install()?;
+ }
+ Some(Commands::Uninstall) => {
+ cmd_uninstall()?;
+ }
+ Some(Commands::Logs { cmd, tail, follow }) => match cmd {
+ Some(LogsCmd::Clear) => {
+ cmd_logs_clear()?;
+ }
+ None => {
+ cmd_logs_view(tail, follow)?;
+ }
+ },
+ }
+
+ Ok(())
+}
diff --git a/crates/cli/src/utils/mod.rs b/crates/cli/src/utils/mod.rs
new file mode 100644
index 0000000..980c68a
--- /dev/null
+++ b/crates/cli/src/utils/mod.rs
@@ -0,0 +1,332 @@
+use std::fs;
+#[cfg(target_os = "macos")]
+use std::path::Path;
+use std::path::PathBuf;
+
+use sysinfo::System;
+
+pub mod system_commands;
+
+/// Get the stored home directory (for service runs)
+/// This is used by the Windows service to use the same home directory as the user who installed it
+#[cfg(target_os = "windows")]
+fn get_stored_home_dir() -> Option {
+ use winreg::RegKey;
+ use winreg::enums::*;
+
+ // Access HKEY_LOCAL_MACHINE for system-wide service access
+ let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
+ let mds_key = hklm.open_subkey("Software\\MarkdownEditorServer").ok()?;
+
+ let home_dir_str: String = mds_key.get_value("HomeDir").ok()?;
+ let path = PathBuf::from(home_dir_str);
+ if path.exists() { Some(path) } else { None }
+}
+
+/// Store the home directory for the service to use later
+#[cfg(target_os = "windows")]
+pub fn store_home_dir(home_dir: &PathBuf) -> std::io::Result<()> {
+ use winreg::RegKey;
+ use winreg::enums::*;
+
+ // Store in HKEY_LOCAL_MACHINE so the service (running as SYSTEM) can access it
+ let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
+ let (mds_key, _disp) = hklm
+ .create_subkey("Software\\MarkdownEditorServer")
+ .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
+
+ mds_key
+ .set_value("HomeDir", &home_dir.to_string_lossy().as_ref())
+ .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
+
+ Ok(())
+}
+
+/// Get the app data directory in user's home (for release builds)
+/// Falls back to "." if home directory cannot be determined
+pub fn app_data_dir() -> PathBuf {
+ #[cfg(not(debug_assertions))]
+ let data_path = PathBuf::from(".md-server");
+ #[cfg(debug_assertions)]
+ let data_path = PathBuf::from(".md-server-dev");
+
+ // On Windows, try to use the stored home directory first (for service runs)
+ #[cfg(target_os = "windows")]
+ {
+ if let Some(home) = get_stored_home_dir() {
+ return home.join(data_path);
+ }
+ }
+
+ dirs::home_dir()
+ .unwrap_or_else(|| PathBuf::from("."))
+ .join(data_path)
+}
+
+/// Read PID from file
+pub fn read_pid_file(path: &PathBuf) -> Option {
+ fs::read_to_string(path)
+ .ok()
+ .and_then(|s| s.trim().parse().ok())
+}
+
+/// Check if a process with the given PID is running
+pub fn is_process_running(pid: u32) -> bool {
+ let mut sys = System::new();
+ sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
+ sys.process(sysinfo::Pid::from_u32(pid)).is_some()
+}
+
+/// Remove the export line from a shell config file
+#[cfg(target_os = "macos")]
+pub fn remove_from_shell_config(
+ file_path: &Path,
+ _install_dir: &Path,
+) -> Result<(), anyhow::Error> {
+ use std::io::Write;
+ use std::io::{BufRead, BufReader};
+
+ if !file_path.exists() {
+ return Ok(());
+ }
+
+ let file = fs::File::open(file_path)?;
+ let reader = BufReader::new(file);
+ let mut lines: Vec = Vec::new();
+ let mut found = false;
+ let mut skip_next = false;
+
+ for line in reader.lines() {
+ let line = line?;
+
+ // Skip the comment and the export line
+ if line.contains("# Markdown Editor Server") {
+ found = true;
+ skip_next = true;
+ continue;
+ }
+
+ if skip_next && line.starts_with("export PATH=") && line.contains(".local/bin") {
+ skip_next = false;
+ continue;
+ }
+
+ skip_next = false;
+ lines.push(line);
+ }
+
+ if found {
+ let mut file = fs::File::create(file_path)?;
+ for line in lines {
+ writeln!(file, "{}", line)?;
+ }
+ println!("Removed PATH entry from {}", file_path.display());
+ }
+
+ Ok(())
+}
+
+#[cfg(target_os = "windows")]
+pub enum CheckAutoStartStatus {
+ Registered,
+ NotRegistered,
+ NotExist,
+ Error,
+}
+/// Check if the service is registered for autostart (Windows)
+#[cfg(target_os = "windows")]
+pub fn is_autostart_registered() -> Result {
+ use crate::utils::system_commands::query_windows_service_config;
+
+ let query_config = query_windows_service_config("MarkdownEditorServer");
+
+ let service_exists = query_config
+ .as_ref()
+ .map(|output| output.status.success())
+ .unwrap_or(false);
+
+ if service_exists {
+ // Check if it's already set to auto start
+ if let Ok(output) = &query_config {
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ if stdout.contains("START_TYPE : 2 AUTO_START") {
+ return Ok(CheckAutoStartStatus::Registered);
+ }
+ } else {
+ return Ok(CheckAutoStartStatus::Error);
+ }
+
+ return Ok(CheckAutoStartStatus::NotRegistered);
+ }
+
+ Ok(CheckAutoStartStatus::NotExist)
+}
+
+#[cfg(target_os = "windows")]
+/// Get the PID of the Windows service if it's running
+pub fn get_service_pid() -> Option {
+ use crate::utils::system_commands::query_windows_service_ex;
+
+ let output = query_windows_service_ex("MarkdownEditorServer").ok()?;
+
+ if !output.status.success() {
+ return None;
+ }
+
+ let stdout = String::from_utf8_lossy(&output.stdout);
+
+ // Check if the service is actually running
+ let is_running = stdout.contains("STATE : 4 RUNNING");
+ if !is_running {
+ return None;
+ }
+
+ // Parse the PID from the output
+ // The output format is like:
+ // SERVICE_NAME: MarkdownEditorServer
+ // TYPE : 10 WIN32_OWN_PROCESS
+ // STATE : 4 RUNNING
+ // WIN32_EXIT_CODE : 0 (0x0)
+ // SERVICE_EXIT_CODE : 0 (0x0)
+ // CHECKPOINT : 0x0
+ // WAIT_HINT : 0x0
+ // PID : 1234
+ // FLAGS :
+
+ for line in stdout.lines() {
+ if line.trim().starts_with("PID") {
+ if let Some(pid_str) = line.split(':').nth(1) {
+ if let Ok(pid) = pid_str.trim().parse::() {
+ if pid > 0 {
+ // PID 0 is invalid
+ return Some(pid);
+ }
+ }
+ }
+ }
+ }
+
+ None
+}
+
+/// Get the service PID using sc queryex and write it to the PID file
+#[cfg(target_os = "windows")]
+pub fn get_and_write_service_pid(pid_file: &PathBuf) -> Result<(), anyhow::Error> {
+ let pid = get_service_pid().unwrap_or(0);
+
+ if pid > 0 {
+ if let Some(parent) = pid_file.parent() {
+ fs::create_dir_all(parent)?;
+ } else {
+ println!(
+ "Warning: Could not determine parent directory for PID file: {}",
+ pid_file.display()
+ );
+ }
+ // Write PID to file
+ match fs::write(pid_file, pid.to_string()) {
+ Ok(_) => {
+ println!("Wrote service PID {} to file {}", pid, pid_file.display());
+ }
+ Err(e) => {
+ println!(
+ "Warning: Could not write PID to file {}: {}",
+ pid_file.display(),
+ e
+ );
+ }
+ }
+ }
+
+ Ok(())
+}
+
+/// Remove read-only attributes from a file on Windows using attrib command
+#[cfg(target_os = "windows")]
+pub fn remove_readonly_attributes(path: &PathBuf) -> Result<(), anyhow::Error> {
+ use crate::utils::system_commands::remove_readonly_attribute;
+ use std::os::windows::fs::MetadataExt;
+
+ // Check if file has read-only attribute
+ let metadata = match fs::metadata(path) {
+ Ok(m) => m,
+ Err(_) => return Ok(()), // If we can't read metadata, continue anyway
+ };
+
+ let attributes = metadata.file_attributes();
+ const FILE_ATTRIBUTE_READONLY: u32 = 0x1;
+
+ if (attributes & FILE_ATTRIBUTE_READONLY) != 0 {
+ // Try using attrib command to remove read-only flag
+ // Use the path as-is; Command handles proper escaping
+ let path_str = path.to_string_lossy().to_string();
+ let success = remove_readonly_attribute(&path_str).unwrap_or(false);
+
+ if success {
+ println!("Removed read-only attribute from {}", path.display());
+ } else {
+ println!(
+ "Warning: Could not remove read-only attribute from {} (attrib command failed)",
+ path.display()
+ );
+ }
+ }
+
+ Ok(())
+}
+
+pub fn remove_file_with_retry(path: &PathBuf) -> Result<(), anyhow::Error> {
+ use std::fs;
+
+ // Try to remove the file with retry logic (useful if file is temporarily locked)
+ let mut attempts = 3;
+ let mut last_error = None;
+
+ while attempts > 0 {
+ match fs::remove_file(path) {
+ Ok(_) => {
+ println!("Removed binary: {}", path.display());
+ last_error = None;
+ break;
+ }
+ Err(e) => {
+ last_error = Some(e);
+ attempts -= 1;
+ if attempts > 0 {
+ // Wait a bit before retrying (file might be locked by antivirus or system)
+ println!(
+ "Warning: Failed to remove binary (attempts remaining: {}). Error: {}",
+ attempts,
+ last_error.as_ref().unwrap()
+ );
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ }
+ }
+ }
+ }
+
+ // If all attempts failed, print detailed error
+ if let Some(e) = last_error {
+ eprintln!("\n❌ Failed to remove binary after multiple attempts:");
+ eprintln!(" Path: {}", path.display());
+ eprintln!(" Error: {}", e);
+ eprintln!(" Error kind: {:?}", e.kind());
+
+ #[cfg(target_os = "windows")]
+ {
+ eprintln!("\n💡 Suggestions for Windows:");
+ eprintln!(" 1. Make sure the server is fully stopped (wait a few seconds)");
+ eprintln!(" 2. Check if Windows Defender or antivirus is scanning the file");
+ eprintln!(" 3. Try running as Administrator");
+ eprintln!(" 4. Manually delete the file: {}", path.display());
+ eprintln!(" 5. Check if any process is using the file (use Process Explorer)");
+ }
+
+ return Err(anyhow::anyhow!(format!(
+ "Failed to remove binary: {}. See error details above.",
+ path.display()
+ )));
+ }
+
+ return Ok(());
+}
diff --git a/crates/cli/src/utils/system_commands.rs b/crates/cli/src/utils/system_commands.rs
new file mode 100644
index 0000000..d55c947
--- /dev/null
+++ b/crates/cli/src/utils/system_commands.rs
@@ -0,0 +1,136 @@
+use anyhow::Result;
+use std::process::Command;
+
+/// Query Windows service status
+#[cfg(target_os = "windows")]
+pub fn query_windows_service(service_name: &str) -> Result {
+ let query_status = Command::new("sc.exe")
+ .args(["query", service_name])
+ .status()?;
+
+ Ok(query_status.success())
+}
+
+/// Create a Windows service
+#[cfg(target_os = "windows")]
+pub fn create_windows_service(
+ service_name: &str,
+ exe_path: &str,
+ display_name: &str,
+ start_type: &str,
+) -> Result {
+ let create_status = Command::new("sc.exe")
+ .args([
+ "create",
+ service_name,
+ "binPath=",
+ &format!("\"{}\"", exe_path),
+ "start=",
+ start_type,
+ "DisplayName=",
+ display_name,
+ ])
+ .status()?;
+
+ Ok(create_status.success())
+}
+
+/// Start a Windows service
+#[cfg(target_os = "windows")]
+pub fn start_windows_service(service_name: &str) -> Result {
+ let status = Command::new("sc.exe")
+ .args(["start", service_name])
+ .status()?;
+
+ Ok(status)
+}
+
+/// Stop a Windows service
+#[cfg(target_os = "windows")]
+pub fn stop_windows_service(service_name: &str) -> Result {
+ let status = Command::new("sc.exe")
+ .args(["stop", service_name])
+ .status()?;
+
+ Ok(status)
+}
+
+/// Delete a Windows service
+#[cfg(target_os = "windows")]
+pub fn delete_windows_service(service_name: &str) -> Result {
+ let status = Command::new("sc.exe")
+ .args(["delete", service_name])
+ .status()?;
+
+ Ok(status)
+}
+
+/// Query Windows service configuration
+#[cfg(target_os = "windows")]
+pub fn query_windows_service_config(service_name: &str) -> Result {
+ let output = Command::new("sc.exe").args(["qc", service_name]).output()?;
+
+ Ok(output)
+}
+
+/// Query extended Windows service information (including PID)
+#[cfg(target_os = "windows")]
+pub fn query_windows_service_ex(service_name: &str) -> Result {
+ let output = Command::new("sc.exe")
+ .args(["queryex", service_name])
+ .output()?;
+
+ Ok(output)
+}
+
+/// Configure Windows service start type
+#[cfg(target_os = "windows")]
+pub fn config_windows_service_start_type(
+ service_name: &str,
+ start_type: &str,
+) -> Result {
+ let status = Command::new("sc.exe")
+ .args(["config", service_name, "start=", start_type])
+ .status()?;
+
+ Ok(status)
+}
+
+/// Load a macOS LaunchAgent
+#[cfg(target_os = "macos")]
+#[allow(dead_code)] // Used in install.rs on macOS
+pub fn load_launch_agent(plist_path: &str) -> Result {
+ let status = Command::new("launchctl")
+ .args(["load", plist_path])
+ .status()?;
+
+ Ok(status.success())
+}
+
+/// Unload a macOS LaunchAgent
+#[cfg(target_os = "macos")]
+pub fn unload_launch_agent(plist_path: &str) -> Result {
+ let status = Command::new("launchctl")
+ .args(["unload", plist_path])
+ .status()?;
+
+ Ok(status.success())
+}
+
+/// Kill a Windows process by PID
+#[cfg(target_os = "windows")]
+pub fn kill_windows_process(pid: u32) -> Result {
+ let output = Command::new("taskkill")
+ .args(["/PID", &pid.to_string(), "/F"])
+ .output()?;
+
+ Ok(output)
+}
+
+/// Remove read-only attribute from a Windows file
+#[cfg(target_os = "windows")]
+pub fn remove_readonly_attribute(path: &str) -> Result {
+ let status = Command::new("attrib").args(["-R", path]).status()?;
+
+ Ok(status.success())
+}
diff --git a/crates/package.json b/crates/package.json
new file mode 100644
index 0000000..b72763c
--- /dev/null
+++ b/crates/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "crates",
+ "version": "1.0.0",
+ "description": "Markdown Editor Server and CLI",
+ "author": "",
+ "private": true,
+ "license": "UNLICENSED",
+ "scripts": {
+ "build": "pnpm build-all",
+ "start": "cargo make start",
+ "start:daemon": "cargo make start-daemon",
+ "stop": "cargo make stop",
+ "status": "cargo make status",
+ "dev": "systemfd --no-pid -s http::3025 -- watchexec -r -- cargo run -p md-server -- start",
+ "dev-cli": "./target/debug/mds",
+ "prod-cli": "./target/release/mds",
+ "build-macos": "node build.js macos",
+ "build-windows": "node build.js windows",
+ "build-all": "node build.js all",
+ "build-macos:legacy": "./build-macos.sh",
+ "build-windows:legacy": "./build-windows.sh",
+ "test": "cargo make test",
+ "logs": "cargo make logs",
+ "logs:follow": "cargo make logs-follow",
+ "logs:clear": "cargo make logs-clear",
+ "link": "cargo make install",
+ "unlink": "cargo uninstall md-server"
+ }
+}
\ No newline at end of file
diff --git a/crates/rustfmt.toml b/crates/rustfmt.toml
new file mode 100644
index 0000000..9d8bb9f
--- /dev/null
+++ b/crates/rustfmt.toml
@@ -0,0 +1 @@
+tab_spaces = 2
diff --git a/crates/server/Cargo.toml b/crates/server/Cargo.toml
new file mode 100644
index 0000000..9597a77
--- /dev/null
+++ b/crates/server/Cargo.toml
@@ -0,0 +1,36 @@
+[package]
+name = "server"
+version = { workspace = true }
+edition = { workspace = true }
+
+[lib]
+name = "server"
+path = "src/lib.rs"
+
+[dependencies]
+anyhow = { workspace = true }
+axum = { workspace = true }
+git2 = { workspace = true }
+listenfd = { workspace = true }
+partial_struct = { workspace = true }
+percent-encoding = { workspace = true }
+regex = { workspace = true }
+serde = { workspace = true }
+serde_json = { workspace = true }
+struct-patch = { workspace = true }
+tokio = { workspace = true }
+tower = { workspace = true }
+tower-http = { workspace = true, features = ["cors"]}
+tracing = { workspace = true }
+tracing-appender = { workspace = true }
+tracing-subscriber = { workspace = true }
+sha2 = { workspace = true }
+dirs = { workspace = true }
+ignore = { workspace = true }
+grep-regex = { workspace = true }
+grep-searcher = { workspace = true }
+grep-matcher = { workspace = true }
+
+[dev-dependencies]
+tempfile = { workspace = true }
+
diff --git a/crates/server/src/constanst.rs b/crates/server/src/constanst.rs
new file mode 100644
index 0000000..5480c77
--- /dev/null
+++ b/crates/server/src/constanst.rs
@@ -0,0 +1,4 @@
+#[cfg(debug_assertions)]
+pub const CORS_ALLOWED_ORIGINS: &[&str] = &["http://localhost:4000"];
+#[cfg(not(debug_assertions))]
+pub const CORS_ALLOWED_ORIGINS: &[&str] = &["https://s-elo.github.io"];
diff --git a/crates/server/src/handlers/doc.rs b/crates/server/src/handlers/doc.rs
new file mode 100644
index 0000000..edffcd9
--- /dev/null
+++ b/crates/server/src/handlers/doc.rs
@@ -0,0 +1,152 @@
+use axum::extract::{Query, State};
+
+use crate::{
+ responses::app::{ApiRes, AppError, AppJson},
+ services::doc::{
+ CopyCutDocRequest, CreateDocRequest, CreateFolderRequest, DeleteDocRequest, DocService,
+ GetArticleQuery, UpdateArticleRequest, UpdateDocNameRequest, structs::GetDocSubTreeQueryPatch,
+ },
+ state::app::AppState,
+ utils::path_encoding::encode_path_string,
+};
+
+pub async fn get_sub_doc_items_handler(
+ State(state): State,
+ Query(params): Query,
+) -> Result>, AppError> {
+ let folder_doc_path = params.folder_doc_path.unwrap_or_default();
+ let home_root_dir = params.home_root_dir.unwrap_or(false);
+ tracing::info!("[DocHandler] getDocSubTree. {}.", folder_doc_path);
+ let doc_items = state
+ .services
+ .doc_service
+ .get_sub_doc_items(&folder_doc_path, home_root_dir)?;
+ Ok(ApiRes::success(doc_items))
+}
+
+pub async fn get_article_handler(
+ State(state): State,
+ Query(params): Query,
+) -> Result>, AppError> {
+ // Normalize the path to ensure it matches the format used in nor_docs
+ let normalized_path = encode_path_string(¶ms.file_path);
+ tracing::info!(
+ "[DocHandler] getArticle: {} (normalized: {})",
+ params.file_path,
+ normalized_path
+ );
+ let article = state.services.doc_service.get_article(&normalized_path)?;
+ Ok(ApiRes::success(article))
+}
+
+pub async fn create_doc_handler(
+ State(state): State,
+ AppJson(request): AppJson,
+) -> Result, AppError> {
+ // Normalize the path to ensure it matches the format used in nor_docs
+ let normalized_path = encode_path_string(&request.file_path);
+ tracing::info!(
+ "[DocHandler] create {}: {} (normalized: {})",
+ if request.is_file { "article" } else { "folder" },
+ request.file_path,
+ normalized_path
+ );
+ let doc = state
+ .services
+ .doc_service
+ .create_doc(&normalized_path, request.is_file)?;
+ Ok(ApiRes::success(doc))
+}
+
+pub async fn create_folder_handler(
+ AppJson(request): AppJson,
+) -> Result, AppError> {
+ tracing::info!("[DocHandler] create folder: {}", request.folder_path);
+ DocService::create_folder(&request.folder_path)?;
+ Ok(ApiRes::success(()))
+}
+
+pub async fn update_article_handler(
+ State(state): State,
+ AppJson(request): AppJson,
+) -> Result, AppError> {
+ // Normalize the path to ensure it matches the format used in nor_docs
+ let normalized_path = encode_path_string(&request.file_path);
+ tracing::info!(
+ "[DocHandler] updateArticle: {} (normalized: {})",
+ request.file_path,
+ normalized_path
+ );
+ state
+ .services
+ .doc_service
+ .update_article(&normalized_path, &request.content)?;
+ Ok(ApiRes::success(()))
+}
+
+pub async fn update_doc_name_handler(
+ State(state): State,
+ AppJson(request): AppJson,
+) -> Result, AppError> {
+ // Normalize the path to ensure it matches the format used in nor_docs
+ let normalized_path = encode_path_string(&request.file_path);
+ tracing::info!(
+ "[DocHandler] update {} name: {} (normalized: {})",
+ if request.is_file { "article" } else { "folder" },
+ request.file_path,
+ normalized_path
+ );
+ state
+ .services
+ .doc_service
+ .modify_name(&normalized_path, &request.name, request.is_file)?;
+ Ok(ApiRes::success(()))
+}
+
+pub async fn copy_cut_doc_handler(
+ State(state): State,
+ AppJson(requests): AppJson>,
+) -> Result, AppError> {
+ for request in requests {
+ // Normalize the paths to ensure they match the format used in nor_docs
+ let normalized_copy_cut_path = encode_path_string(&request.copy_cut_path);
+ let normalized_paste_path = encode_path_string(&request.paste_path);
+ tracing::info!(
+ "[DocHandler] {} {}: {} -> {} (normalized: {} -> {})",
+ if request.is_copy { "copy" } else { "cut" },
+ if request.is_file { "article" } else { "folder" },
+ request.copy_cut_path,
+ request.paste_path,
+ normalized_copy_cut_path,
+ normalized_paste_path
+ );
+ state.services.doc_service.copy_cut_doc(
+ &normalized_copy_cut_path,
+ &normalized_paste_path,
+ request.is_copy,
+ request.is_file,
+ )?;
+ }
+ Ok(ApiRes::success(()))
+}
+
+pub async fn delete_doc_handler(
+ State(state): State,
+ AppJson(requests): AppJson>,
+) -> Result, AppError> {
+ for request in requests {
+ // Normalize the path to ensure it matches the format used in nor_docs
+ let normalized_path = encode_path_string(&request.file_path);
+ tracing::info!(
+ "[DocHandler] delete {}: {} (normalized: {})",
+ if request.is_file { "article" } else { "folder" },
+ request.file_path,
+ normalized_path
+ );
+ state
+ .services
+ .doc_service
+ .delete_doc(&normalized_path, request.is_file)?;
+ }
+ Ok(ApiRes::success(()))
+}
diff --git a/crates/server/src/handlers/git.rs b/crates/server/src/handlers/git.rs
new file mode 100644
index 0000000..d06e464
--- /dev/null
+++ b/crates/server/src/handlers/git.rs
@@ -0,0 +1,97 @@
+use axum::extract::State;
+
+use crate::{
+ responses::app::{ApiRes, AppError, AppJson},
+ services::git::{Change, GitStatus},
+ state::app::AppState,
+};
+
+#[derive(serde::Deserialize)]
+pub struct AddRequest {
+ #[serde(rename = "changePaths")]
+ pub change_paths: Vec,
+}
+
+#[derive(serde::Deserialize)]
+pub struct CommitRequest {
+ pub title: String,
+ pub body: String,
+}
+
+#[derive(serde::Deserialize)]
+pub struct RestoreRequest {
+ pub staged: bool,
+ pub changes: Vec,
+}
+
+pub async fn get_status_handler(
+ State(state): State,
+) -> Result, AppError> {
+ tracing::info!("[GitHandler] getStatus");
+
+ if !state.services.git_service.is_repo() {
+ tracing::info!("[GitHandler] no repo");
+ return Ok(ApiRes::success(GitStatus {
+ workspace: Vec::new(),
+ staged: Vec::new(),
+ changes: false,
+ no_git: true,
+ remotes: Vec::new(),
+ }));
+ }
+
+ let status = state.services.git_service.get_status()?;
+ Ok(ApiRes::success(status))
+}
+
+pub async fn add_handler(
+ State(state): State,
+ AppJson(request): AppJson,
+) -> Result, AppError> {
+ tracing::info!("[GitHandler] add: {:?}", request.change_paths);
+ state.services.git_service.add(request.change_paths)?;
+ Ok(ApiRes::success(()))
+}
+
+pub async fn commit_handler(
+ State(state): State,
+ AppJson(request): AppJson,
+) -> Result, AppError> {
+ tracing::info!("[GitHandler] commit: {} - {}", request.title, request.body);
+ // state.services.git_service.commit(request.title, request.body)?;
+ let message = if request.body.is_empty() {
+ request.title
+ } else {
+ format!("{}\n\n{}", request.title, request.body)
+ };
+ state.services.git_service.exec_commit(message)?;
+ Ok(ApiRes::success(()))
+}
+
+pub async fn push_handler(State(state): State) -> Result, AppError> {
+ tracing::info!("[GitHandler] push");
+ state.services.git_service.exec_push()?;
+ Ok(ApiRes::success(()))
+}
+
+pub async fn pull_handler(State(state): State) -> Result, AppError> {
+ tracing::info!("[GitHandler] pull");
+ state.services.git_service.pull()?;
+ Ok(ApiRes::success(()))
+}
+
+pub async fn restore_handler(
+ State(state): State,
+ AppJson(request): AppJson,
+) -> Result, AppError> {
+ tracing::info!(
+ "[GitHandler] restore: staged={}, changes={:?}",
+ request.staged,
+ request.changes
+ );
+ state
+ .services
+ .git_service
+ .restore(request.staged, request.changes)?;
+ Ok(ApiRes::success(()))
+}
diff --git a/crates/server/src/handlers/img.rs b/crates/server/src/handlers/img.rs
new file mode 100644
index 0000000..2fc8eb4
--- /dev/null
+++ b/crates/server/src/handlers/img.rs
@@ -0,0 +1,86 @@
+use axum::{
+ body::Body,
+ extract::{Multipart, Path, State},
+ http::{HeaderValue, Response, StatusCode, header},
+};
+
+use serde::Deserialize;
+
+use crate::{
+ responses::app::{ApiRes, AppError, AppJson},
+ services::img::ImgItem,
+ state::app::AppState,
+};
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct DeleteImageRequest {
+ pub file_name: String,
+}
+
+pub async fn list_images_handler(
+ State(state): State,
+) -> Result>, AppError> {
+ tracing::info!("[ImgHandler] listing images");
+ let images = state.services.img_service.list_images()?;
+ Ok(ApiRes::success(images))
+}
+
+pub async fn delete_image_handler(
+ State(state): State,
+ AppJson(body): AppJson,
+) -> Result, AppError> {
+ tracing::info!("[ImgHandler] deleting image: {}", body.file_name);
+ state.services.img_service.delete_image(&body.file_name)?;
+ Ok(ApiRes::success("deleted".to_string()))
+}
+
+pub async fn get_image_handler(
+ State(state): State,
+ Path(img_path): Path,
+) -> Result, AppError> {
+ tracing::info!("[ImgHandler] get image: {}", img_path);
+
+ let (bytes, mime) = state.services.img_service.get_image(&img_path)?;
+
+ let response = Response::builder()
+ .status(StatusCode::OK)
+ .header(header::CONTENT_TYPE, HeaderValue::from_str(&mime).unwrap())
+ .header(header::CACHE_CONTROL, "public, max-age=3600")
+ .body(Body::from(bytes))
+ .unwrap();
+
+ Ok(response)
+}
+
+pub async fn upload_image_handler(
+ State(state): State,
+ mut multipart: Multipart,
+) -> Result, AppError> {
+ while let Some(field) = multipart
+ .next_field()
+ .await
+ .map_err(|e| anyhow::anyhow!("Failed to read multipart field: {}", e))?
+ {
+ let file_name = field
+ .file_name()
+ .map(|s| s.to_string())
+ .ok_or_else(|| anyhow::anyhow!("Missing file name"))?;
+
+ let data = field
+ .bytes()
+ .await
+ .map_err(|e| anyhow::anyhow!("Failed to read file data: {}", e))?;
+
+ tracing::info!(
+ "[ImgHandler] uploading image: {} ({} bytes)",
+ file_name,
+ data.len()
+ );
+
+ let url = state.services.img_service.upload_image(&file_name, &data)?;
+ return Ok(ApiRes::success(url));
+ }
+
+ Err(anyhow::anyhow!("No file provided in upload").into())
+}
diff --git a/crates/server/src/handlers/mod.rs b/crates/server/src/handlers/mod.rs
new file mode 100644
index 0000000..0c11dfb
--- /dev/null
+++ b/crates/server/src/handlers/mod.rs
@@ -0,0 +1,19 @@
+use serde::{Deserialize, Serialize};
+
+use crate::responses::app::{ApiRes, AppError};
+
+pub mod doc;
+pub mod git;
+pub mod img;
+pub mod search;
+pub mod settings;
+
+#[derive(Serialize, Deserialize)]
+pub struct CheckServerRes {
+ version: String,
+}
+pub async fn check_server_handler() -> Result, AppError> {
+ tracing::info!("[CheckServerHandler] checkServer.");
+ let version = env!("CARGO_PKG_VERSION").to_string();
+ Ok(ApiRes::success(CheckServerRes { version }))
+}
diff --git a/crates/server/src/handlers/search.rs b/crates/server/src/handlers/search.rs
new file mode 100644
index 0000000..b57ff16
--- /dev/null
+++ b/crates/server/src/handlers/search.rs
@@ -0,0 +1,71 @@
+use axum::extract::{Query, State};
+use serde::Deserialize;
+
+use crate::{
+ responses::app::{ApiRes, AppError},
+ services::search::{FileContentMatches, FileNameMatch},
+ state::app::AppState,
+};
+
+#[derive(Debug, Deserialize)]
+pub struct SearchFilesQuery {
+ pub q: String,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SearchContentQuery {
+ pub q: String,
+ pub case_sensitive: Option,
+ pub include_files: Option,
+ pub exclude_files: Option,
+}
+
+pub async fn search_files_handler(
+ State(state): State,
+ Query(params): Query,
+) -> Result>, AppError> {
+ tracing::info!("[SearchHandler] searchFiles: {}", params.q);
+ let results = state.services.search_service.search_file_names(¶ms.q)?;
+ Ok(ApiRes::success(results))
+}
+
+pub async fn search_content_handler(
+ State(state): State,
+ Query(params): Query,
+) -> Result>, AppError> {
+ let case_insensitive = !params.case_sensitive.unwrap_or(false);
+ let include_patterns: Vec = params
+ .include_files
+ .as_deref()
+ .map(|s| {
+ s.split(',')
+ .map(|p| p.trim().to_string())
+ .filter(|p| !p.is_empty())
+ .collect()
+ })
+ .unwrap_or_default();
+ let exclude_patterns: Vec = params
+ .exclude_files
+ .as_deref()
+ .map(|s| {
+ s.split(',')
+ .map(|p| p.trim().to_string())
+ .filter(|p| !p.is_empty())
+ .collect()
+ })
+ .unwrap_or_default();
+
+ tracing::info!(
+ "[SearchHandler] searchContent: {} (case_insensitive: {})",
+ params.q,
+ case_insensitive,
+ );
+ let results = state.services.search_service.search_content(
+ ¶ms.q,
+ case_insensitive,
+ &include_patterns,
+ &exclude_patterns,
+ )?;
+ Ok(ApiRes::success(results))
+}
diff --git a/crates/server/src/handlers/settings.rs b/crates/server/src/handlers/settings.rs
new file mode 100644
index 0000000..01697cb
--- /dev/null
+++ b/crates/server/src/handlers/settings.rs
@@ -0,0 +1,34 @@
+use axum::extract::State;
+
+use crate::{
+ responses::app::{ApiRes, AppError, AppJson},
+ services::settings::{Settings, SettingsPatch},
+ state::app::AppState,
+};
+
+pub async fn get_settings_handler(
+ State(state): State,
+) -> Result, AppError> {
+ Ok(ApiRes::success(
+ state.services.settings_service.get_settings(),
+ ))
+}
+
+pub async fn update_settings_handler(
+ State(state): State,
+ AppJson(new_settings): AppJson,
+) -> Result, AppError> {
+ let updated_settings = state
+ .services
+ .settings_service
+ .update_settings(new_settings)?;
+ // {
+ // Ok(updated_settings) => updated_settings,
+ // Err(e) => return Err(AppError::Unknown(e)),
+ // };
+
+ state.services.doc_service.sync_settings(&updated_settings);
+ state.services.git_service.sync_git(&updated_settings);
+
+ Ok(ApiRes::success(updated_settings))
+}
diff --git a/crates/server/src/lib.rs b/crates/server/src/lib.rs
new file mode 100644
index 0000000..816dffb
--- /dev/null
+++ b/crates/server/src/lib.rs
@@ -0,0 +1,94 @@
+pub mod constanst;
+pub mod handlers;
+pub mod middlewares;
+pub mod responses;
+pub mod routes;
+pub mod services;
+pub mod state;
+pub mod utils;
+
+use std::path::PathBuf;
+
+pub use routes::root::init_routes;
+use tracing_appender::{non_blocking, rolling};
+use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
+
+/// Server configuration
+#[derive(Debug, Clone)]
+pub struct ServerConfig {
+ pub host: String,
+ pub port: u16,
+ pub log_dir: PathBuf,
+ pub log_to_terminal: bool,
+ pub editor_settings_file: PathBuf,
+}
+
+impl Default for ServerConfig {
+ fn default() -> Self {
+ Self {
+ host: "127.0.0.1".to_string(),
+ port: 3024,
+ log_dir: PathBuf::from("logs"),
+ log_to_terminal: true,
+ editor_settings_file: PathBuf::from("editor-settings.json"),
+ }
+ }
+}
+
+/// Initialize tracing/logging for the server
+pub fn init_tracing(
+ config: &ServerConfig,
+) -> anyhow::Result {
+ // Create logs directory if it doesn't exist
+ std::fs::create_dir_all(&config.log_dir).unwrap_or_else(|e| {
+ eprintln!("Warning: Failed to create logs directory: {}", e);
+ });
+
+ // Create a file appender that rotates daily
+ let file_appender = rolling::daily(&config.log_dir, "server.log");
+ let (non_blocking_appender, guard) = non_blocking(file_appender);
+
+ let registry = tracing_subscriber::registry().with(
+ tracing_subscriber::EnvFilter::try_from_default_env()
+ .unwrap_or_else(|_| "server=debug,tower_http=debug".into()),
+ );
+
+ if config.log_to_terminal {
+ registry
+ // Terminal output
+ .with(tracing_subscriber::fmt::layer())
+ // File output
+ .with(
+ tracing_subscriber::fmt::layer()
+ .with_writer(non_blocking_appender)
+ .with_ansi(false),
+ )
+ .init();
+ } else {
+ registry
+ // File output only (daemon mode)
+ .with(
+ tracing_subscriber::fmt::layer()
+ .with_writer(non_blocking_appender)
+ .with_ansi(false),
+ )
+ .init();
+ }
+
+ Ok(guard)
+}
+
+/// Run the server with the given configuration
+pub async fn run_server(config: ServerConfig) -> anyhow::Result<()> {
+ let _guard = init_tracing(&config)?;
+
+ let app = init_routes(config.editor_settings_file);
+
+ let addr = format!("{}:{}", config.host, config.port);
+ let listener = tokio::net::TcpListener::bind(&addr).await?;
+ tracing::info!("Server listening on {}", listener.local_addr()?);
+
+ axum::serve(listener, app).await?;
+
+ Ok(())
+}
diff --git a/crates/server/src/middlewares/logs.rs b/crates/server/src/middlewares/logs.rs
new file mode 100644
index 0000000..e493743
--- /dev/null
+++ b/crates/server/src/middlewares/logs.rs
@@ -0,0 +1,14 @@
+use axum::{extract::Request, middleware::Next, response::Response};
+use std::sync::Arc;
+
+use crate::responses::app::AppError;
+
+// Our middleware is responsible for logging error details internally
+pub async fn log_app_errors(request: Request, next: Next) -> Response {
+ let response = next.run(request).await;
+ // If the response contains an AppError Extension, log it.
+ if let Some(err) = response.extensions().get::>() {
+ tracing::error!(?err, "an unexpected error occurred inside a handler");
+ }
+ response
+}
diff --git a/crates/server/src/middlewares/mod.rs b/crates/server/src/middlewares/mod.rs
new file mode 100644
index 0000000..af2c2c3
--- /dev/null
+++ b/crates/server/src/middlewares/mod.rs
@@ -0,0 +1 @@
+pub mod logs;
diff --git a/crates/server/src/responses/app.rs b/crates/server/src/responses/app.rs
new file mode 100644
index 0000000..b966b99
--- /dev/null
+++ b/crates/server/src/responses/app.rs
@@ -0,0 +1,100 @@
+use std::sync::Arc;
+
+use axum::{
+ extract::{FromRequest, rejection::JsonRejection},
+ http::StatusCode,
+ response::{IntoResponse, Response},
+};
+use serde::Serialize;
+
+// Create our own JSON extractor by wrapping `axum::Json`. This makes it easy to override the
+// rejection and provide our own which formats errors to match our application.
+//
+// `axum::Json` responds with plain text if the input is invalid.
+#[derive(FromRequest)]
+#[from_request(via(axum::Json), rejection(AppError))]
+pub struct AppJson(pub T);
+
+#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
+pub struct ApiRes {
+ data: Option,
+ code: u8,
+ message: Option,
+}
+
+impl ApiRes {
+ pub fn success(data: T) -> Self {
+ Self {
+ data: Some(data),
+ code: 0,
+ message: None,
+ }
+ }
+
+ pub fn error(message: T) -> Self {
+ Self {
+ data: None,
+ code: 1,
+ message: Some(message),
+ }
+ }
+}
+
+impl IntoResponse for ApiRes
+where
+ axum::Json>: IntoResponse,
+{
+ fn into_response(self) -> Response {
+ // wrap code feild
+ axum::Json(self).into_response()
+ }
+}
+
+// The kinds of errors we can hit in our application.
+#[derive(Debug)]
+pub enum AppError {
+ // The request body contained invalid JSON
+ JsonRejection(JsonRejection),
+ Unknown(anyhow::Error),
+}
+
+// Tell axum how `AppError` should be converted into a response.
+impl IntoResponse for AppError {
+ fn into_response(self) -> Response {
+ let (status, message, err) = match &self {
+ AppError::JsonRejection(rejection) => {
+ // This error is caused by bad user input so don't log it
+ (rejection.status(), rejection.body_text(), None)
+ }
+ AppError::Unknown(err) => (
+ StatusCode::INTERNAL_SERVER_ERROR,
+ err.to_string(),
+ Some(self),
+ ),
+ };
+
+ let mut response = (status, ApiRes::error(message)).into_response();
+ if let Some(err) = err {
+ // Insert our error into the response, our logging middleware will use this.
+ // By wrapping the error in an Arc we can use it as an Extension regardless of any inner types not deriving Clone.
+ response.extensions_mut().insert(Arc::new(err));
+ }
+
+ response
+ }
+}
+
+impl From for AppError {
+ fn from(rejection: JsonRejection) -> Self {
+ Self::JsonRejection(rejection)
+ }
+}
+
+// This enables using `?` on functions that return `Result<_, anyhow::Error>` to turn them into
+// `Result<_, AppError>`. That way you don't need to do that manually.
+// see the get_all_users_handler and get_all_users service
+impl From for AppError {
+ fn from(error: anyhow::Error) -> Self {
+ Self::Unknown(error)
+ }
+}
diff --git a/crates/server/src/responses/mod.rs b/crates/server/src/responses/mod.rs
new file mode 100644
index 0000000..309be62
--- /dev/null
+++ b/crates/server/src/responses/mod.rs
@@ -0,0 +1 @@
+pub mod app;
diff --git a/crates/server/src/routes/doc.rs b/crates/server/src/routes/doc.rs
new file mode 100644
index 0000000..9fa8740
--- /dev/null
+++ b/crates/server/src/routes/doc.rs
@@ -0,0 +1,25 @@
+use axum::{Router, routing};
+
+use crate::{
+ handlers::doc::{
+ copy_cut_doc_handler, create_doc_handler, create_folder_handler, delete_doc_handler,
+ get_article_handler, get_sub_doc_items_handler, update_article_handler,
+ update_doc_name_handler,
+ },
+ state::app::AppState,
+};
+
+pub fn doc_routes() -> Router {
+ Router::new().nest(
+ "/docs",
+ Router::new()
+ .route("/sub-items", routing::get(get_sub_doc_items_handler))
+ .route("/article", routing::get(get_article_handler))
+ .route("/create", routing::post(create_doc_handler))
+ .route("/create-folder", routing::post(create_folder_handler))
+ .route("/update", routing::patch(update_article_handler))
+ .route("/update-name", routing::patch(update_doc_name_handler))
+ .route("/copy-cut", routing::patch(copy_cut_doc_handler))
+ .route("/delete", routing::delete(delete_doc_handler)),
+ )
+}
diff --git a/crates/server/src/routes/git.rs b/crates/server/src/routes/git.rs
new file mode 100644
index 0000000..0a5672c
--- /dev/null
+++ b/crates/server/src/routes/git.rs
@@ -0,0 +1,21 @@
+use axum::{Router, routing};
+
+use crate::{
+ handlers::git::{
+ add_handler, commit_handler, get_status_handler, pull_handler, push_handler, restore_handler,
+ },
+ state::app::AppState,
+};
+
+pub fn git_routes() -> Router {
+ Router::new().nest(
+ "/git",
+ Router::new()
+ .route("/status", routing::get(get_status_handler))
+ .route("/add", routing::post(add_handler))
+ .route("/commit", routing::post(commit_handler))
+ .route("/push", routing::post(push_handler))
+ .route("/pull", routing::post(pull_handler))
+ .route("/restore", routing::post(restore_handler)),
+ )
+}
diff --git a/crates/server/src/routes/img.rs b/crates/server/src/routes/img.rs
new file mode 100644
index 0000000..df6e3ed
--- /dev/null
+++ b/crates/server/src/routes/img.rs
@@ -0,0 +1,19 @@
+use axum::{Router, routing};
+
+use crate::{
+ handlers::img::{
+ delete_image_handler, get_image_handler, list_images_handler, upload_image_handler,
+ },
+ state::app::AppState,
+};
+
+pub fn img_routes() -> Router {
+ Router::new().nest(
+ "/imgs",
+ Router::new()
+ .route("/list", routing::get(list_images_handler))
+ .route("/upload", routing::post(upload_image_handler))
+ .route("/delete", routing::delete(delete_image_handler))
+ .route("/{*path}", routing::get(get_image_handler)),
+ )
+}
diff --git a/crates/server/src/routes/mod.rs b/crates/server/src/routes/mod.rs
new file mode 100644
index 0000000..e042287
--- /dev/null
+++ b/crates/server/src/routes/mod.rs
@@ -0,0 +1,6 @@
+mod doc;
+mod git;
+mod img;
+pub mod root;
+mod search;
+mod settings;
diff --git a/crates/server/src/routes/root.rs b/crates/server/src/routes/root.rs
new file mode 100644
index 0000000..a5aa0c9
--- /dev/null
+++ b/crates/server/src/routes/root.rs
@@ -0,0 +1,104 @@
+use std::path::PathBuf;
+
+use axum::{
+ Router, ServiceExt,
+ extract::{MatchedPath, Request},
+ http::{HeaderName, HeaderValue},
+ middleware::from_fn,
+ routing,
+ routing::IntoMakeService,
+};
+
+use tower::{Layer, ServiceBuilder};
+use tower_http::{
+ cors::{AllowOrigin, Any, CorsLayer},
+ normalize_path::{NormalizePath, NormalizePathLayer},
+ request_id::{MakeRequestUuid, PropagateRequestIdLayer, SetRequestIdLayer},
+ trace::TraceLayer,
+};
+
+use crate::{
+ constanst::CORS_ALLOWED_ORIGINS,
+ handlers::check_server_handler,
+ middlewares::logs::log_app_errors,
+ routes::{
+ doc::doc_routes, git::git_routes, img::img_routes, search::search_routes,
+ settings::settings_routes,
+ },
+ state::app::AppState,
+};
+
+const REQUEST_ID_HEADER: &str = "x-request-id";
+
+pub fn init_routes(editor_settings_file: PathBuf) -> IntoMakeService> {
+ let x_request_id = HeaderName::from_static(REQUEST_ID_HEADER);
+
+ let cors_layer = CorsLayer::new()
+ .allow_origin(AllowOrigin::list(
+ CORS_ALLOWED_ORIGINS
+ .iter()
+ .map(|s| HeaderValue::from_static(s)),
+ )) // Open access to selected route
+ .allow_methods(Any)
+ .allow_headers(Any);
+
+ let middleware = ServiceBuilder::new()
+ .layer(SetRequestIdLayer::new(
+ x_request_id.clone(),
+ MakeRequestUuid,
+ ))
+ .layer(
+ TraceLayer::new_for_http()
+ // Create our own span for the request and include the matched path. The matched
+ // path is useful for figuring out which handler the request was routed to.
+ .make_span_with(|req: &Request| {
+ let method = req.method();
+ let uri = req.uri();
+ let request_id = req.headers().get(REQUEST_ID_HEADER);
+
+ // axum automatically adds this extension.
+ let matched_path = req
+ .extensions()
+ .get::()
+ .map(|matched_path| matched_path.as_str());
+
+ match request_id {
+ Some(req_id) => {
+ tracing::debug_span!("|", %method, %uri, matched_path, request_id = ?req_id)
+ }
+ None => {
+ tracing::error!("could not extract request_id");
+ tracing::debug_span!("|", %method, %uri, matched_path)
+ }
+ }
+ })
+ // By default `TraceLayer` will log 5xx responses but we're doing our specific
+ // logging of errors so disable that
+ .on_failure(()),
+ )
+ // send headers from request to response headers
+ .layer(PropagateRequestIdLayer::new(x_request_id))
+ .layer(from_fn(log_app_errors));
+
+ let app_state = AppState::new(editor_settings_file);
+
+ let app = Router::new().nest(
+ "/api",
+ Router::new()
+ .route("/check", routing::get(check_server_handler))
+ .with_state(app_state.clone())
+ .merge(settings_routes().with_state(app_state.clone()))
+ .merge(doc_routes().with_state(app_state.clone()))
+ .merge(git_routes().with_state(app_state.clone()))
+ .merge(img_routes().with_state(app_state.clone()))
+ .merge(search_routes().with_state(app_state.clone()))
+ .layer(cors_layer)
+ .layer(middleware),
+ );
+
+ let app = NormalizePathLayer::trim_trailing_slash().layer(app);
+
+ // https://github.com/tokio-rs/axum/discussions/2377
+ ServiceExt::::into_make_service(app)
+ // as ServiceExt>::into_make_service(app)
+}
diff --git a/crates/server/src/routes/search.rs b/crates/server/src/routes/search.rs
new file mode 100644
index 0000000..24fd8ee
--- /dev/null
+++ b/crates/server/src/routes/search.rs
@@ -0,0 +1,15 @@
+use axum::{Router, routing};
+
+use crate::{
+ handlers::search::{search_content_handler, search_files_handler},
+ state::app::AppState,
+};
+
+pub fn search_routes() -> Router {
+ Router::new().nest(
+ "/search",
+ Router::new()
+ .route("/files", routing::get(search_files_handler))
+ .route("/content", routing::get(search_content_handler)),
+ )
+}
diff --git a/crates/server/src/routes/settings.rs b/crates/server/src/routes/settings.rs
new file mode 100644
index 0000000..28cfde8
--- /dev/null
+++ b/crates/server/src/routes/settings.rs
@@ -0,0 +1,16 @@
+use axum::{Router, routing::get};
+
+use crate::{
+ handlers::settings::{get_settings_handler, update_settings_handler},
+ state::app::AppState,
+};
+
+pub fn settings_routes() -> Router {
+ Router::new().nest(
+ "/settings",
+ Router::new().route(
+ "/",
+ get(get_settings_handler).patch(update_settings_handler),
+ ),
+ )
+}
diff --git a/crates/server/src/services/doc/helpers.rs b/crates/server/src/services/doc/helpers.rs
new file mode 100644
index 0000000..d456730
--- /dev/null
+++ b/crates/server/src/services/doc/helpers.rs
@@ -0,0 +1,146 @@
+use percent_encoding::{AsciiSet, CONTROLS};
+use std::fs;
+use std::path::Path;
+
+/// ASCII set that matches JavaScript's `encodeURIComponent` behavior.
+/// Encodes everything except unreserved characters: A-Z, a-z, 0-9, -, ., _, ~
+/// This is equivalent to JavaScript's encodeURIComponent.
+pub const ENCODE_URI_COMPONENT: &AsciiSet = &CONTROLS
+ .add(b' ')
+ .add(b'!')
+ .add(b'"')
+ .add(b'#')
+ .add(b'$')
+ .add(b'%')
+ .add(b'&')
+ .add(b'\'')
+ .add(b'(')
+ .add(b')')
+ .add(b'*')
+ .add(b'+')
+ .add(b',')
+ .add(b'/')
+ .add(b':')
+ .add(b';')
+ .add(b'<')
+ .add(b'=')
+ .add(b'>')
+ .add(b'?')
+ .add(b'@')
+ .add(b'[')
+ .add(b'\\')
+ .add(b']')
+ .add(b'^')
+ .add(b'`')
+ .add(b'{')
+ .add(b'|')
+ .add(b'}');
+
+/// Converts a path array to a percent-encoded normalized path string.
+/// Uses the same encoding as JavaScript's `encodeURIComponent`.
+///
+/// # Example
+/// ```
+/// use server::services::doc::normalize_path;
+///
+/// let path = vec!["js".to_string(), "basic".to_string(), "array".to_string()];
+/// assert_eq!(normalize_path(&path), "js%2Fbasic%2Farray");
+/// ```
+pub fn normalize_path(path_arr: &[String]) -> String {
+ percent_encoding::utf8_percent_encode(&path_arr.join("/"), ENCODE_URI_COMPONENT).to_string()
+}
+
+/// Decodes a percent-encoded normalized path string into a path array.
+///
+/// # Example
+/// ```
+/// use server::services::doc::denormalize_path;
+///
+/// let normalized = "js%2Fbasic%2Farray";
+/// let path = denormalize_path(normalized);
+/// assert_eq!(path, vec!["js", "basic", "array"]);
+/// ```
+pub fn denormalize_path(path_str: &str) -> Vec {
+ percent_encoding::percent_decode_str(path_str)
+ .decode_utf8_lossy()
+ .split('/')
+ .map(|s| s.to_string())
+ .collect()
+}
+
+/// Recursively copies a directory and all its contents.
+pub fn copy_dir_all(src: &Path, dst: &Path) -> Result<(), anyhow::Error> {
+ fs::create_dir_all(dst)?;
+ for entry in fs::read_dir(src)? {
+ let entry = entry?;
+ let path = entry.path();
+ let name = entry.file_name();
+ let dst_path = dst.join(name);
+
+ if path.is_dir() {
+ copy_dir_all(&path, &dst_path)?;
+ } else {
+ fs::copy(&path, &dst_path)?;
+ }
+ }
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_normalize_path_encodes_slash() {
+ let path = vec!["js".to_string(), "basic".to_string(), "array".to_string()];
+ let result = normalize_path(&path);
+ // Slash should be encoded as %2F
+ assert_eq!(result, "js%2Fbasic%2Farray");
+ }
+
+ #[test]
+ fn test_normalize_path_preserves_unreserved_chars() {
+ // Unreserved characters: A-Z, a-z, 0-9, -, ., _, ~
+ let path = vec!["test-file_123".to_string(), "dir.name~".to_string()];
+ let result = normalize_path(&path);
+ // These characters should NOT be encoded
+ assert!(result.contains("test-file_123"));
+ assert!(result.contains("dir.name~"));
+ // But slash should be encoded
+ assert!(result.contains("%2F"));
+ }
+
+ #[test]
+ fn test_normalize_path_encodes_special_chars() {
+ // Characters that should be encoded
+ let path = vec!["file with spaces".to_string(), "file#name".to_string()];
+ let result = normalize_path(&path);
+ // Space should be encoded as %20
+ assert!(result.contains("%20"));
+ // # should be encoded as %23
+ assert!(result.contains("%23"));
+ }
+
+ #[test]
+ fn test_normalize_denormalize_roundtrip() {
+ let original = vec!["js".to_string(), "basic".to_string(), "array".to_string()];
+ let normalized = normalize_path(&original);
+ let denormalized = denormalize_path(&normalized);
+ assert_eq!(original, denormalized);
+ }
+
+ #[test]
+ fn test_normalize_path_matches_encodeuricomponent_behavior() {
+ // Test cases that verify we match JavaScript's encodeURIComponent
+ // These are the unreserved characters that should NOT be encoded
+ let unreserved = vec!["ABC".to_string(), "xyz".to_string(), "123".to_string()];
+ let result = normalize_path(&unreserved);
+ assert_eq!(result, "ABC%2Fxyz%2F123"); // Only / is encoded
+
+ // Test with all unreserved chars in one segment
+ let path = vec!["A-Z_a-z0-9.-~".to_string()];
+ let result = normalize_path(&path);
+ // All characters should remain unencoded (except / which isn't in this path)
+ assert_eq!(result, "A-Z_a-z0-9.-~");
+ }
+}
diff --git a/crates/server/src/services/doc/mod.rs b/crates/server/src/services/doc/mod.rs
new file mode 100644
index 0000000..9338ae4
--- /dev/null
+++ b/crates/server/src/services/doc/mod.rs
@@ -0,0 +1,510 @@
+pub mod helpers;
+pub mod structs;
+mod test;
+
+pub use helpers::{copy_dir_all, denormalize_path, normalize_path};
+
+// Re-export all public types from structs
+pub use structs::{
+ Article, CopyCutDocRequest, CreateDocRequest, CreateFolderRequest, DeleteDocRequest, DocItem,
+ GetArticleQuery, GetDocSubTreeQuery, UpdateArticleRequest, UpdateDocNameRequest,
+};
+
+use crate::services::settings::SettingsService;
+use std::{
+ fs,
+ path::PathBuf,
+ sync::{Arc, Mutex},
+};
+
+pub struct DocService {
+ ignore_dirs: Arc>>,
+ doc_root_path: Arc>,
+ doc_root_path_depth: Arc>,
+ settings_service: Arc,
+}
+
+const INTERNAL_IGNORE_DIRS: &[&str] = &["_assets"];
+
+impl DocService {
+ /// Creates a new `DocService` instance and initializes it with current settings.
+ pub fn new(settings_service: Arc) -> Self {
+ let service = Self {
+ ignore_dirs: Arc::new(Mutex::new(Vec::new())),
+ doc_root_path: Arc::new(Mutex::new(PathBuf::new())),
+ doc_root_path_depth: Arc::new(Mutex::new(0)),
+ settings_service,
+ };
+
+ // Initialize with current settings
+ let settings = service.settings_service.get_settings();
+ service.sync_settings(&settings);
+
+ tracing::info!("[DocService] Docs initialized.");
+ service
+ }
+
+ pub fn get_sub_doc_items(
+ &self,
+ folder_doc_path: &str,
+ home_root_dir: bool,
+ ) -> Result, anyhow::Error> {
+ tracing::info!(
+ "get_sub_doc_items: {:?}, {:?}",
+ folder_doc_path,
+ home_root_dir
+ );
+
+ let doc_path = self.path_convertor(folder_doc_path, false)?;
+ let root_dir = Self::get_root_dir();
+
+ #[cfg(target_os = "windows")]
+ {
+ // If folder_doc_path is empty and home_root_dir is true, return all available disks
+ if folder_doc_path.is_empty() && home_root_dir {
+ return Ok(Self::get_disks_folders(&root_dir));
+ }
+ }
+
+ let ab_doc_path = if !home_root_dir {
+ self.doc_root_path.lock().unwrap().join(doc_path)
+ } else {
+ // recover back to folder_doc_path, since path_convertor will add doc_root_path prefix, we need to remove it and add home dir prefix instead
+ let doc_root = self.doc_root_path.lock().unwrap().clone();
+ if doc_path.starts_with(&doc_root) {
+ root_dir.join(doc_path.strip_prefix(doc_root).unwrap())
+ } else {
+ // for windows folder_doc_path like "C:/" will be converted to "C:" by path_convertor
+ // and will not add the doc_root_path prefix, so just take folder_doc_path directly
+ root_dir.join(folder_doc_path)
+ }
+ };
+ if !ab_doc_path.exists() {
+ tracing::error!("The folder doc path {} does not exist.", folder_doc_path);
+ return Err(anyhow::anyhow!(
+ "The folder doc path {} does not exist.",
+ folder_doc_path
+ ));
+ }
+
+ tracing::info!("ab_doc_path: {:?}", ab_doc_path,);
+
+ let entries = fs::read_dir(&ab_doc_path)?;
+ let mut docs = Vec::new();
+ for entry in entries {
+ let entry = entry?;
+ let path = entry.path();
+ let name = entry.file_name().to_string_lossy().to_string();
+ // ignore hidden files/folders
+ if name.starts_with('.') {
+ continue;
+ }
+
+ let is_file = path.is_file();
+ let is_valid_dir = !INTERNAL_IGNORE_DIRS.contains(&name.as_str())
+ && !self.ignore_dirs.lock().unwrap().contains(&name);
+
+ if is_file {
+ if self.is_markdown(&name) {
+ let mut file_path_parts = denormalize_path(folder_doc_path)
+ .into_iter()
+ .filter(|p| !p.is_empty())
+ .collect::>();
+ let file_name = name.strip_suffix(".md").unwrap_or(&name).to_string();
+ file_path_parts.push(file_name.clone());
+
+ // add home dir prefix to display for UI
+ if home_root_dir {
+ file_path_parts.insert(0, root_dir.to_string_lossy().to_string());
+ }
+
+ let doc = DocItem {
+ id: format!("{}-{}", file_name, file_path_parts.join("-")),
+ name: file_name,
+ is_file: true,
+ path: file_path_parts,
+ };
+
+ docs.push(doc);
+ }
+ } else if is_valid_dir {
+ let mut dir_path_parts = denormalize_path(folder_doc_path)
+ .into_iter()
+ .filter(|p| !p.is_empty())
+ .collect::>();
+ dir_path_parts.push(name.clone());
+
+ // add home dir prefix to display for UI
+ if home_root_dir {
+ dir_path_parts.insert(0, root_dir.to_string_lossy().to_string());
+ }
+
+ let doc = DocItem {
+ id: format!("{}-{}", name, dir_path_parts.join("-")),
+ name: name.clone(),
+ is_file: false,
+ path: dir_path_parts,
+ };
+
+ docs.push(doc);
+ }
+ }
+
+ // Sort: directories first, then files, both alphabetically
+ Self::sort_doc_items(&mut docs);
+
+ Ok(docs)
+ }
+
+ /// Retrieves article content for a file. Returns `None` if the file doesn't exist.
+ ///
+ /// # Arguments
+ /// * `file_path` - Normalized path string (percent-encoded), e.g., `"js%2Fbasic%2Farray"`
+ ///
+ /// # Example
+ /// ```ignore
+ /// // Get article at "js/basic/array.md"
+ /// let article = doc_service.get_article("js%2Fbasic%2Farray")?;
+ /// ```
+ pub fn get_article(&self, file_path: &str) -> Result, anyhow::Error> {
+ let doc_path = self.path_convertor(file_path, true).unwrap();
+ println!("{:?}", doc_path);
+ if !doc_path.exists() {
+ tracing::error!("The file path {} does not exist.", file_path);
+ return Err(anyhow::anyhow!(
+ "The file path {} does not exist.",
+ file_path
+ ));
+ }
+
+ let content = fs::read_to_string(&doc_path)?;
+
+ Ok(Some(Article {
+ content,
+ file_path: file_path.to_string(),
+ headings: Vec::new(),
+ keywords: Vec::new(),
+ }))
+ }
+
+ /// Updates the content of an article file. Creates parent directories if needed.
+ ///
+ /// # Arguments
+ /// * `update_path` - Normalized path string (percent-encoded), e.g., `"js%2Fbasic%2Farray"`
+ /// * `content` - New content for the file
+ ///
+ /// # Example
+ /// ```ignore
+ /// // Update article at "js/basic/array.md"
+ /// doc_service.update_article("js%2Fbasic%2Farray", "# New Content")?;
+ /// ```
+ pub fn update_article(&self, update_path: &str, content: &str) -> Result<(), anyhow::Error> {
+ let converted_path = self.path_convertor(update_path, true)?;
+
+ // Ensure parent directory exists
+ if let Some(parent) = converted_path.parent() {
+ fs::create_dir_all(parent)?;
+ }
+
+ fs::write(&converted_path, content)?;
+
+ Ok(())
+ }
+
+ /// Creates a new document or directory at the specified path.
+ ///
+ /// # Arguments
+ /// * `doc_path` - Normalized path string (percent-encoded), e.g., `"js%2Fbasic%2Fnew-doc"`
+ /// * `is_file` - `true` for markdown files, `false` for directories
+ ///
+ /// # Example
+ /// ```ignore
+ /// // Create a new file at "js/basic/new-doc.md"
+ /// let doc = doc_service.create_doc("js%2Fbasic%2Fnew-doc", true)?;
+ ///
+ /// // Create a new directory at "js/basic/new-folder"
+ /// let dir = doc_service.create_doc("js%2Fbasic%2Fnew-folder", false)?;
+ /// ```
+ pub fn create_doc(&self, doc_path: &str, is_file: bool) -> Result {
+ let created_path = self.path_convertor(doc_path, is_file)?;
+
+ tracing::info!("create_doc: {:?}, {:?}", doc_path, created_path);
+
+ if is_file {
+ if let Some(parent) = created_path.parent() {
+ fs::create_dir_all(parent)?;
+ }
+ fs::File::create(&created_path)?;
+ } else {
+ fs::create_dir_all(&created_path)?;
+ }
+
+ let path_parts = denormalize_path(doc_path);
+ let name = path_parts.last().unwrap().to_string();
+
+ Ok(DocItem {
+ id: format!("{}-{}", name, path_parts.join("-")),
+ name,
+ is_file,
+ path: path_parts,
+ })
+ }
+
+ pub fn create_folder(ab_path: &str) -> Result<(), anyhow::Error> {
+ tracing::info!("create_folder: {:?}", ab_path);
+
+ let path = PathBuf::from(ab_path);
+ if !path.exists() {
+ fs::create_dir_all(&path)?;
+ }
+ Ok(())
+ }
+
+ /// Deletes a document or directory.
+ ///
+ /// # Arguments
+ /// * `doc_path` - Normalized path string (percent-encoded), e.g., `"js%2Fbasic%2Fold-doc"`
+ /// * `is_file` - `true` for markdown files, `false` for directories
+ ///
+ /// # Example
+ /// ```ignore
+ /// // Delete a file at "js/basic/old-doc.md"
+ /// doc_service.delete_doc("js%2Fbasic%2Fold-doc", true)?;
+ ///
+ /// // Delete a directory at "js/basic/old-folder"
+ /// doc_service.delete_doc("js%2Fbasic%2Fold-folder", false)?;
+ /// ```
+ pub fn delete_doc(&self, doc_path: &str, is_file: bool) -> Result<(), anyhow::Error> {
+ let delete_path = self.path_convertor(doc_path, is_file)?;
+
+ fs::remove_dir_all(&delete_path).or_else(|_| fs::remove_file(&delete_path))?;
+
+ Ok(())
+ }
+
+ /// Copies or moves a document/directory from `copy_cut_path` to `paste_path`.
+ /// If `is_copy` is true, performs a copy; otherwise, moves the item.
+ ///
+ /// # Arguments
+ /// * `copy_cut_path` - Source normalized path, e.g., `"js%2Fbasic%2Fsource"`
+ /// * `paste_path` - Destination normalized path, e.g., `"js%2Fadvanced%2Fdestination"`
+ /// * `is_copy` - `true` to copy, `false` to move
+ /// * `is_file` - `true` for markdown files, `false` for directories
+ ///
+ /// # Example
+ /// ```ignore
+ /// // Copy a file from "js/basic/source.md" to "js/advanced/destination.md"
+ /// doc_service.copy_cut_doc("js%2Fbasic%2Fsource", "js%2Fadvanced%2Fdestination", true, true)?;
+ ///
+ /// // Move a directory from "js/basic/folder" to "js/advanced/folder"
+ /// doc_service.copy_cut_doc("js%2Fbasic%2Ffolder", "js%2Fadvanced%2Ffolder", false, false)?;
+ /// ```
+ pub fn copy_cut_doc(
+ &self,
+ copy_cut_path: &str,
+ paste_path: &str,
+ is_copy: bool,
+ is_file: bool,
+ ) -> Result<(), anyhow::Error> {
+ let paste_parent_path = {
+ let mut path_parts = denormalize_path(paste_path);
+ path_parts.pop();
+ let parent_path = if path_parts.is_empty() {
+ String::new()
+ } else {
+ normalize_path(&path_parts)
+ };
+ self.path_convertor(&parent_path, false)?
+ };
+
+ if !paste_parent_path.exists() {
+ return Err(anyhow::anyhow!(
+ "The parent path {:?} of the paste path {} does not exist.",
+ paste_parent_path,
+ paste_path
+ ));
+ }
+
+ let source_path = self.path_convertor(copy_cut_path, is_file)?;
+ let dest_path = self.path_convertor(paste_path, is_file)?;
+
+ if is_copy {
+ if is_file {
+ fs::copy(&source_path, &dest_path)?;
+ } else {
+ copy_dir_all(&source_path, &dest_path)?;
+ }
+ } else {
+ fs::rename(&source_path, &dest_path)?;
+ }
+
+ Ok(())
+ }
+
+ /// Renames a document or directory.
+ ///
+ /// # Arguments
+ /// * `modify_path` - Current normalized path, e.g., `"js%2Fbasic%2Fold-name"`
+ /// * `name` - New name (without path), e.g., `"new-name"`
+ /// * `is_file` - `true` for markdown files, `false` for directories
+ ///
+ /// # Example
+ /// ```ignore
+ /// // Rename file "js/basic/old-name.md" to "js/basic/new-name.md"
+ /// doc_service.modify_name("js%2Fbasic%2Fold-name", "new-name", true)?;
+ ///
+ /// // Rename directory "js/basic/old-folder" to "js/basic/new-folder"
+ /// doc_service.modify_name("js%2Fbasic%2Fold-folder", "new-folder", false)?;
+ /// ```
+ pub fn modify_name(
+ &self,
+ modify_path: &str,
+ name: &str,
+ is_file: bool,
+ ) -> Result<(), anyhow::Error> {
+ let cur_path = self.path_convertor(modify_path, is_file)?;
+
+ if !cur_path.exists() {
+ return Ok(());
+ }
+
+ let new_path = self.path_convertor_with_name(modify_path, is_file, Some(name))?;
+ if new_path.to_str() == cur_path.to_str() {
+ tracing::info!(
+ "The new path is the same as the current path, so skip the rename. {}",
+ cur_path.display()
+ );
+ return Ok(());
+ }
+ println!("{:?}, {:?}", cur_path, new_path);
+ fs::rename(&cur_path, &new_path)?;
+
+ Ok(())
+ }
+
+ /// Synchronizes service state with settings.
+ pub fn sync_settings(&self, settings: &crate::services::settings::Settings) {
+ *self.ignore_dirs.lock().unwrap() = settings.ignore_dirs.clone();
+ *self.doc_root_path.lock().unwrap() = settings.doc_root_path.clone();
+ *self.doc_root_path_depth.lock().unwrap() = settings.doc_root_path.components().count();
+
+ if !self.doc_root_path.lock().unwrap().exists() {
+ tracing::warn!(
+ "[DocService] Doc root path: {} does not exist, should let user to provide correct path in settings.",
+ self.doc_root_path.lock().unwrap().display()
+ );
+ return;
+ }
+ }
+
+ /// Checks if a file name has a markdown extension.
+ fn is_markdown(&self, file_name: &str) -> bool {
+ file_name.ends_with(".md")
+ }
+
+ /// Converts a normalized path string to a filesystem path.
+ ///
+ /// # Arguments
+ /// * `str_path` - Normalized path (percent-encoded), e.g., `"js%2Fbasic%2Farray"`
+ /// * `is_file` - `true` to append `.md` extension, `false` for directories
+ ///
+ /// # Example
+ /// ```ignore
+ /// // Converts "js%2Fbasic%2Farray" to "js/basic/array.md" (if is_file=true)
+ /// // or "js/basic/array" (if is_file=false)
+ /// let path = doc_service.path_convertor("js%2Fbasic%2Farray", true)?;
+ /// ```
+ fn path_convertor(&self, str_path: &str, is_file: bool) -> Result {
+ self.path_convertor_with_name(str_path, is_file, None)
+ }
+
+ /// Converts a normalized path to a filesystem path, optionally replacing the last component with `name`.
+ fn path_convertor_with_name(
+ &self,
+ str_path: &str,
+ is_file: bool,
+ name: Option<&str>,
+ ) -> Result {
+ let mut path_parts = denormalize_path(str_path);
+
+ if let Some(new_name) = name {
+ if let Some(last) = path_parts.last_mut() {
+ *last = new_name.to_string();
+ }
+ }
+
+ let doc_root = self.doc_root_path.lock().unwrap().clone();
+
+ let mut full_path = doc_root;
+ for part in path_parts {
+ full_path.push(part);
+ }
+
+ if is_file {
+ full_path.set_extension("md");
+ }
+
+ Ok(full_path)
+ }
+
+ fn sort_doc_items(doc_items: &mut Vec) {
+ doc_items.sort_by(|a, b| match (a.is_file, b.is_file) {
+ (true, false) => std::cmp::Ordering::Greater,
+ (false, true) => std::cmp::Ordering::Less,
+ _ => {
+ if a.is_file {
+ a.id.to_lowercase().cmp(&b.id.to_lowercase())
+ } else {
+ a.name.to_lowercase().cmp(&b.name.to_lowercase())
+ }
+ }
+ });
+ }
+
+ fn get_root_dir() -> PathBuf {
+ #[cfg(target_os = "windows")]
+ {
+ // For Windows service, return "" for UI
+ return PathBuf::from("");
+ }
+
+ #[cfg(target_os = "macos")]
+ {
+ return PathBuf::from("/");
+ }
+ }
+
+ /// Lists all available disk roots on Windows.
+ /// Returns paths like ["C:\\", "D:\\", "E:\\"] for available drives.
+ #[cfg(target_os = "windows")]
+ fn list_available_disks() -> Vec {
+ let mut disks = Vec::new();
+ for drive_letter in b'A'..=b'Z' {
+ let drive_path = PathBuf::from(format!("{}:/", drive_letter as char));
+ if drive_path.exists() {
+ disks.push(drive_path);
+ }
+ }
+ disks
+ }
+
+ #[cfg(target_os = "windows")]
+ fn get_disks_folders(root_dir: &PathBuf) -> Vec {
+ return Self::list_available_disks()
+ .into_iter()
+ .map(|disk_path| {
+ let disk_name = disk_path
+ .to_string_lossy()
+ // .trim_end_matches('\\')
+ .to_string();
+ DocItem {
+ id: disk_name.clone(),
+ name: disk_name.clone(),
+ is_file: false,
+ // add a virtual root_dir prefix to display for UI
+ path: vec![root_dir.to_string_lossy().to_string(), disk_name],
+ }
+ })
+ .collect();
+ }
+}
diff --git a/crates/server/src/services/doc/structs.rs b/crates/server/src/services/doc/structs.rs
new file mode 100644
index 0000000..599ac5e
--- /dev/null
+++ b/crates/server/src/services/doc/structs.rs
@@ -0,0 +1,109 @@
+use std::collections::HashMap;
+
+use serde::{Deserialize, Serialize};
+use struct_patch::Patch;
+
+#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Doc {
+ pub name: String,
+ pub id: String,
+ pub is_file: bool,
+ pub children: Vec,
+ pub path: Vec,
+ pub headings: Vec,
+ pub keywords: Vec,
+}
+
+#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct DocItem {
+ pub name: String,
+ pub id: String,
+ pub is_file: bool,
+ pub path: Vec,
+}
+
+#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct NormalizedDoc {
+ pub name: String,
+ pub id: String,
+ pub is_file: bool,
+ /// children keys in the normalized docs HashMap
+ pub children_keys: Vec,
+ pub path: Vec,
+ pub headings: Vec,
+ pub keywords: Vec,
+ /// Parent key in the normalized docs HashMap. None means it's a root doc.
+ pub parent_key: Option,
+}
+
+pub type NormalizedDocMap = HashMap;
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Article {
+ pub content: String,
+ pub file_path: String,
+ pub headings: Vec,
+ pub keywords: Vec,
+}
+
+#[derive(Patch, Debug, Deserialize)]
+#[patch(attribute(derive(Deserialize, Debug)))]
+#[patch(attribute(serde(rename_all = "camelCase")))]
+pub struct GetDocSubTreeQuery {
+ pub folder_doc_path: String,
+ pub home_root_dir: bool,
+}
+
+#[derive(Debug, Deserialize)]
+pub struct GetArticleQuery {
+ #[serde(rename = "filePath")]
+ pub file_path: String,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateDocRequest {
+ pub file_path: String,
+ pub is_file: bool,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateFolderRequest {
+ pub folder_path: String,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct UpdateArticleRequest {
+ pub file_path: String,
+ pub content: String,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct UpdateDocNameRequest {
+ pub file_path: String,
+ pub name: String,
+ pub is_file: bool,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CopyCutDocRequest {
+ pub copy_cut_path: String,
+ pub paste_path: String,
+ pub is_copy: bool,
+ pub is_file: bool,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct DeleteDocRequest {
+ pub file_path: String,
+ pub is_file: bool,
+}
diff --git a/crates/server/src/services/doc/test.rs b/crates/server/src/services/doc/test.rs
new file mode 100644
index 0000000..6ef362a
--- /dev/null
+++ b/crates/server/src/services/doc/test.rs
@@ -0,0 +1,528 @@
+#[cfg(test)]
+mod tests {
+ use crate::services::doc::{DocService, denormalize_path, normalize_path};
+ use crate::services::settings::{Settings, SettingsService};
+ use std::{
+ fs,
+ sync::{Arc, Mutex},
+ };
+
+ /// Creates a temporary directory and returns a DocService instance configured to use it.
+ fn setup_test_service() -> (DocService, tempfile::TempDir) {
+ let temp_dir = tempfile::tempdir().expect("Failed to create temp directory");
+ let settings = Settings {
+ doc_root_path: temp_dir.path().to_path_buf(),
+ ignore_dirs: vec![".git".to_string(), "node_modules".to_string()],
+ };
+
+ let settings_service = SettingsService {
+ settings: Arc::new(Mutex::new(settings)),
+ editor_settings_file: temp_dir.path().join("editor-settings.json"),
+ };
+
+ let doc_service = DocService::new(Arc::new(settings_service));
+ (doc_service, temp_dir)
+ }
+
+ mod init_and_create {
+ use super::*;
+
+ #[test]
+ fn test_get_sub_doc_items() {
+ let (service, _temp_dir) = setup_test_service();
+ let docs = service.get_sub_doc_items("", false).unwrap();
+ assert!(docs.is_empty());
+ }
+
+ #[test]
+ fn test_create_file() {
+ let (service, _temp_dir) = setup_test_service();
+ let doc_path = "test-file";
+
+ let doc = service.create_doc(doc_path, true).unwrap();
+ assert_eq!(doc.name, "test-file");
+ assert!(doc.is_file);
+ assert_eq!(doc.path, vec!["test-file"]);
+
+ // Verify file exists on filesystem
+ let fs_path = service.path_convertor(doc_path, true).unwrap();
+ assert!(fs_path.exists());
+ assert!(fs_path.is_file());
+ assert_eq!(fs_path.extension().unwrap(), "md");
+ }
+
+ #[test]
+ fn test_create_directory() {
+ let (service, _temp_dir) = setup_test_service();
+ let doc_path = "test-dir";
+
+ let doc = service.create_doc(doc_path, false).unwrap();
+ assert_eq!(doc.name, "test-dir");
+ assert!(!doc.is_file);
+ assert_eq!(doc.path, vec!["test-dir"]);
+
+ // Verify directory exists on filesystem
+ let fs_path = service.path_convertor(doc_path, false).unwrap();
+ assert!(fs_path.exists());
+ assert!(fs_path.is_dir());
+ }
+
+ #[test]
+ fn test_create_nested_file() {
+ let (service, _temp_dir) = setup_test_service();
+ // Create parent directories first
+ service.create_doc("parent", false).unwrap();
+ service.create_doc("parent%2Fchild", false).unwrap();
+
+ let doc_path = "parent%2Fchild%2Ffile";
+ let doc = service.create_doc(doc_path, true).unwrap();
+ assert_eq!(doc.name, "file");
+ assert_eq!(doc.path, vec!["parent", "child", "file"]);
+
+ // Verify nested structure exists
+ let fs_path = service.path_convertor(doc_path, true).unwrap();
+ assert!(fs_path.exists());
+ assert!(fs_path.parent().unwrap().exists());
+ }
+ }
+
+ mod delete {
+ use super::*;
+
+ #[test]
+ fn test_delete_file() {
+ let (service, _temp_dir) = setup_test_service();
+ let doc_path = "test-file";
+
+ // Create file first
+ service.create_doc(doc_path, true).unwrap();
+ let fs_path = service.path_convertor(doc_path, true).unwrap();
+ assert!(fs_path.exists());
+
+ // Delete file
+ service.delete_doc(doc_path, true).unwrap();
+ assert!(!fs_path.exists());
+ }
+
+ #[test]
+ fn test_delete_directory() {
+ let (service, _temp_dir) = setup_test_service();
+ let doc_path = "test-dir";
+
+ // Create directory with a file
+ service.create_doc(doc_path, false).unwrap();
+ service.create_doc("test-dir%2Ffile", true).unwrap();
+
+ let fs_path = service.path_convertor(doc_path, false).unwrap();
+ assert!(fs_path.exists());
+
+ // Delete directory
+ service.delete_doc(doc_path, false).unwrap();
+ assert!(!fs_path.exists());
+ }
+ }
+
+ mod article {
+ use super::*;
+
+ #[test]
+ fn test_update_article() {
+ let (service, _temp_dir) = setup_test_service();
+ let doc_path = "test-article";
+ let content = "# Hello World\n\nThis is a test article.";
+
+ // Create file first
+ service.create_doc(doc_path, true).unwrap();
+
+ // Update article
+ service.update_article(doc_path, content).unwrap();
+
+ // Verify content
+ let article = service.get_article(doc_path).unwrap().unwrap();
+ assert_eq!(article.content, content);
+ assert_eq!(article.file_path, doc_path);
+ }
+
+ #[test]
+ fn test_update_article_creates_parent_dirs() {
+ let (service, _temp_dir) = setup_test_service();
+ let doc_path = normalize_path(&vec![
+ "nested".to_string(),
+ "deep".to_string(),
+ "article".to_string(),
+ ]);
+ let content = "# Nested Article";
+
+ // Update article without creating parent dirs first
+ service.update_article(&doc_path, content).unwrap();
+
+ // Verify file and parent directories exist
+ let fs_path = service.path_convertor(&doc_path, true).unwrap();
+ assert!(fs_path.exists());
+ assert!(fs_path.parent().unwrap().exists());
+ }
+
+ #[test]
+ fn test_get_article_nonexistent() {
+ let (service, _temp_dir) = setup_test_service();
+ let result = service.get_article("nonexistent");
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn test_get_article_existing() {
+ let (service, _temp_dir) = setup_test_service();
+ let doc_path = "test-article";
+ let content = "# Test\n\nContent here.";
+
+ service.create_doc(doc_path, true).unwrap();
+ service.update_article(doc_path, content).unwrap();
+
+ let article = service.get_article(doc_path).unwrap().unwrap();
+ assert_eq!(article.content, content);
+ }
+ }
+
+ mod copy_cut {
+ use super::*;
+
+ #[test]
+ fn test_copy_file() {
+ let (service, _temp_dir) = setup_test_service();
+ let source_path = "source-file";
+ let dest_path = "dest-file";
+ let content = "# Source Content";
+
+ // Create source file
+ service.create_doc(source_path, true).unwrap();
+ service.update_article(source_path, content).unwrap();
+
+ // Copy file
+ service
+ .copy_cut_doc(source_path, dest_path, true, true)
+ .unwrap();
+
+ // Verify both files exist
+ let source_fs = service.path_convertor(source_path, true).unwrap();
+ let dest_fs = service.path_convertor(dest_path, true).unwrap();
+ assert!(source_fs.exists());
+ assert!(dest_fs.exists());
+
+ // Verify content is copied
+ let dest_article = service.get_article(dest_path).unwrap().unwrap();
+ assert_eq!(dest_article.content, content);
+ }
+
+ #[test]
+ fn test_move_file() {
+ let (service, _temp_dir) = setup_test_service();
+ let source_path = "source-file";
+ let dest_path = "dest-file";
+ let content = "# Source Content";
+
+ // Create source file
+ service.create_doc(source_path, true).unwrap();
+ service.update_article(source_path, content).unwrap();
+
+ // Move file
+ service
+ .copy_cut_doc(source_path, dest_path, false, true)
+ .unwrap();
+
+ // Verify source is gone and dest exists
+ let source_fs = service.path_convertor(source_path, true).unwrap();
+ let dest_fs = service.path_convertor(dest_path, true).unwrap();
+ assert!(!source_fs.exists());
+ assert!(dest_fs.exists());
+
+ // Verify content is moved
+ let dest_article = service.get_article(dest_path).unwrap().unwrap();
+ assert_eq!(dest_article.content, content);
+ }
+
+ #[test]
+ fn test_copy_directory() {
+ let (service, _temp_dir) = setup_test_service();
+ let source_path = "source-dir";
+ let dest_path = "dest-dir";
+ let file_path = normalize_path(&vec!["source-dir".to_string(), "file".to_string()]);
+ let content = "# File Content";
+
+ // Create source directory with file
+ service.create_doc(source_path, false).unwrap();
+ service.create_doc(&file_path, true).unwrap();
+ service.update_article(&file_path, content).unwrap();
+
+ // Copy directory
+ service
+ .copy_cut_doc(source_path, dest_path, true, false)
+ .unwrap();
+
+ // Verify both directories exist
+ let source_fs = service.path_convertor(source_path, false).unwrap();
+ let dest_fs = service.path_convertor(dest_path, false).unwrap();
+ assert!(source_fs.exists());
+ assert!(dest_fs.exists());
+
+ // Verify file in copied directory
+ let dest_file_path = normalize_path(&vec!["dest-dir".to_string(), "file".to_string()]);
+ let dest_article = service.get_article(&dest_file_path).unwrap().unwrap();
+ assert_eq!(dest_article.content, content);
+ }
+
+ #[test]
+ fn test_copy_cut_invalid_parent_path() {
+ let (service, _temp_dir) = setup_test_service();
+ let source_path = "source-file";
+ let invalid_dest = normalize_path(&vec![
+ "nonexistent".to_string(),
+ "parent".to_string(),
+ "dest".to_string(),
+ ]);
+
+ service.create_doc(source_path, true).unwrap();
+
+ // Should fail because parent doesn't exist
+ let result = service.copy_cut_doc(source_path, &invalid_dest, true, true);
+ assert!(result.is_err());
+ }
+ }
+
+ mod rename {
+ use super::*;
+
+ #[test]
+ fn test_rename_file() {
+ let (service, _temp_dir) = setup_test_service();
+ let old_path = "old-file";
+ let new_name = "new-file";
+ let content = "# Content";
+
+ // Create file on filesystem
+ let fs_path = service.path_convertor(old_path, true).unwrap();
+ if let Some(parent) = fs_path.parent() {
+ fs::create_dir_all(parent).unwrap();
+ }
+ fs::File::create(&fs_path).unwrap();
+ service.update_article(old_path, content).unwrap();
+
+ let docs = service.get_sub_doc_items("", false).unwrap();
+ assert_eq!(docs.len(), 1);
+ let actual_old_path = normalize_path(&docs[0].path);
+
+ // Rename file using the actual normalized path from cache
+ service
+ .modify_name(&actual_old_path, new_name, true)
+ .unwrap();
+
+ // Verify old path doesn't exist, new path exists
+ let old_fs = service.path_convertor(&actual_old_path, true).unwrap();
+ let new_path = normalize_path(&vec![new_name.to_string()]);
+ let new_fs = service.path_convertor(&new_path, true).unwrap();
+ assert!(!old_fs.exists());
+ assert!(new_fs.exists());
+
+ // Verify content is preserved
+ let article = service.get_article(&new_path).unwrap().unwrap();
+ assert_eq!(article.content, content);
+ }
+
+ #[test]
+ fn test_rename_directory() {
+ let (service, _temp_dir) = setup_test_service();
+ let old_path = "old-dir";
+ let new_name = "new-dir";
+ let file_path = normalize_path(&vec!["old-dir".to_string(), "file".to_string()]);
+ let content = "# File Content";
+
+ // Create directory with file on filesystem
+ service.create_doc(old_path, false).unwrap();
+ service.create_doc(&file_path, true).unwrap();
+ service.update_article(&file_path, content).unwrap();
+
+ let docs = service.get_sub_doc_items("", false).unwrap();
+ assert_eq!(docs.len(), 1);
+
+ // Rename directory using the actual normalized path from cache
+ service.modify_name(old_path, new_name, false).unwrap();
+
+ // Verify old path doesn't exist, new path exists
+ let old_fs = service.path_convertor(old_path, false).unwrap();
+ let new_fs = service.path_convertor(new_name, false).unwrap();
+ assert!(!old_fs.exists());
+ assert!(new_fs.exists());
+
+ // Verify file in renamed directory
+ let new_file_path = normalize_path(&vec!["new-dir".to_string(), "file".to_string()]);
+ let article = service.get_article(&new_file_path).unwrap().unwrap();
+ assert_eq!(article.content, content);
+ }
+
+ #[test]
+ fn test_rename_same_name_no_op() {
+ let (service, _temp_dir) = setup_test_service();
+ let doc_path = "test-file";
+ let content = "# Content";
+
+ service.create_doc(doc_path, true).unwrap();
+ service.update_article(doc_path, content).unwrap();
+
+ // Rename to same name should be a no-op
+ service.modify_name(doc_path, "test-file", true).unwrap();
+
+ // Verify file still exists with same content
+ let article = service.get_article(doc_path).unwrap().unwrap();
+ assert_eq!(article.content, content);
+ }
+
+ #[test]
+ fn test_modify_name_nonexistent_file() {
+ let (service, _temp_dir) = setup_test_service();
+ // Should not error, just return Ok(())
+ let result = service.modify_name("nonexistent", "new-name", true);
+ assert!(result.is_ok());
+ }
+ }
+
+ #[test]
+ fn test_get_docs_with_cache() {
+ let (service, _temp_dir) = setup_test_service();
+ let doc_path = "test-file";
+
+ // First call should scan filesystem
+ service.create_doc(doc_path, true).unwrap();
+ let docs1 = service.get_sub_doc_items("", false).unwrap();
+ assert_eq!(docs1.len(), 1);
+
+ // Second call should use cache
+ let docs2 = service.get_sub_doc_items("", false).unwrap();
+ assert_eq!(docs2.len(), 1);
+ assert_eq!(docs1[0].id, docs2[0].id);
+ }
+
+ #[test]
+ fn test_get_docs_force_refresh() {
+ let (service, _temp_dir) = setup_test_service();
+ let doc_path = "test-file";
+
+ service.create_doc(doc_path, true).unwrap();
+ let docs1 = service.get_sub_doc_items("", false).unwrap();
+ assert_eq!(docs1.len(), 1);
+
+ // Create another file directly on filesystem
+ let another_path = service.path_convertor("another-file", true).unwrap();
+ fs::write(&another_path, "").unwrap();
+
+ // Force refresh should pick up new file
+ let docs2 = service.get_sub_doc_items("", false).unwrap();
+ assert_eq!(docs2.len(), 2);
+ }
+
+ #[test]
+ fn test_refresh_doc() {
+ let (service, _temp_dir) = setup_test_service();
+ let doc_path = "test-file";
+
+ service.create_doc(doc_path, true).unwrap();
+
+ // Create another file directly on filesystem
+ let another_path = service.path_convertor("another-file", true).unwrap();
+ fs::write(&another_path, "").unwrap();
+
+ let docs = service.get_sub_doc_items("", false).unwrap();
+ assert_eq!(docs.len(), 2);
+ }
+
+ #[test]
+ fn test_ignore_directories() {
+ let (service, _temp_dir) = setup_test_service();
+ let temp_dir_path = service.doc_root_path.lock().unwrap().clone();
+
+ // Create ignored directory
+ let ignored_dir = temp_dir_path.join(".git");
+ fs::create_dir_all(&ignored_dir).unwrap();
+ fs::write(ignored_dir.join("file.md"), "").unwrap();
+
+ // Create non-ignored directory
+ service.create_doc("visible-dir", false).unwrap();
+ service.create_doc("visible-dir%2Ffile", true).unwrap();
+
+ let docs = service.get_sub_doc_items("", false).unwrap();
+ // Should only see visible-dir, not .git
+ assert_eq!(docs.len(), 1);
+ assert_eq!(docs[0].name, "visible-dir");
+ }
+
+ #[test]
+ fn test_nested_structure() {
+ let (service, _temp_dir) = setup_test_service();
+ let dir_path = "parent";
+ let child_path = "parent%2Fchild";
+ let file_path = "parent%2Fchild%2Ffile";
+
+ service.create_doc(dir_path, false).unwrap();
+ service.create_doc(child_path, false).unwrap();
+ service.create_doc(file_path, true).unwrap();
+
+ let docs = service.get_sub_doc_items("", false).unwrap();
+ assert_eq!(docs.len(), 1);
+ assert_eq!(docs[0].name, "parent");
+ let sub_doc_items = service.get_sub_doc_items(dir_path, false).unwrap();
+ assert_eq!(sub_doc_items.len(), 1);
+ assert_eq!(sub_doc_items[0].name, "child");
+ let sub_doc_items = service.get_sub_doc_items(child_path, false).unwrap();
+ assert_eq!(sub_doc_items.len(), 1);
+ assert_eq!(sub_doc_items[0].name, "file");
+ }
+
+ #[test]
+ fn test_sorting_directories_before_files() {
+ let (service, _temp_dir) = setup_test_service();
+ service.create_doc("z-file", true).unwrap();
+ service.create_doc("a-dir", false).unwrap();
+ service.create_doc("m-file", true).unwrap();
+ service.create_doc("b-dir", false).unwrap();
+
+ let docs = service.get_sub_doc_items("", false).unwrap();
+ assert_eq!(docs.len(), 4);
+ // Directories should come first
+ assert_eq!(docs[0].name, "a-dir");
+ assert!(!docs[0].is_file);
+ assert_eq!(docs[1].name, "b-dir");
+ assert!(!docs[1].is_file);
+ // Then files
+ assert_eq!(docs[2].name, "m-file");
+ assert!(docs[2].is_file);
+ assert_eq!(docs[3].name, "z-file");
+ assert!(docs[3].is_file);
+ }
+
+ #[test]
+ fn test_path_normalization() {
+ let path = vec!["js".to_string(), "basic".to_string(), "array".to_string()];
+ let normalized = normalize_path(&path);
+ assert_eq!(normalized, "js%2Fbasic%2Farray");
+
+ let denormalized = denormalize_path(&normalized);
+ assert_eq!(denormalized, path);
+ }
+
+ #[test]
+ fn test_sync_settings() {
+ let (service, temp_dir) = setup_test_service();
+ let new_settings = Settings {
+ doc_root_path: temp_dir.path().join("new-docs"),
+ ignore_dirs: vec!["custom-ignore".to_string()],
+ };
+
+ fs::create_dir_all(&new_settings.doc_root_path).unwrap();
+ fs::write(new_settings.doc_root_path.join("file.md"), "").unwrap();
+
+ service.sync_settings(&new_settings);
+
+ // Verify settings are updated
+ let ignore_dirs = service.ignore_dirs.lock().unwrap();
+ assert_eq!(ignore_dirs.len(), 1);
+ assert_eq!(ignore_dirs[0], "custom-ignore");
+ }
+}
diff --git a/crates/server/src/services/git.rs b/crates/server/src/services/git.rs
new file mode 100644
index 0000000..2d6f111
--- /dev/null
+++ b/crates/server/src/services/git.rs
@@ -0,0 +1,406 @@
+use std::sync::{Arc, Mutex};
+
+use git2::{Repository, Status, StatusOptions};
+
+use crate::services::{settings::Settings, settings::SettingsService};
+
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
+pub enum StatusType {
+ Added,
+ Modified,
+ Deleted,
+ Untracked,
+ Rename,
+}
+
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Change {
+ pub change_path: String,
+ pub status: StatusType,
+}
+
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GitStatus {
+ pub workspace: Vec,
+ pub staged: Vec,
+ pub changes: bool,
+ pub no_git: bool,
+ pub remotes: Vec,
+}
+
+/// Remote info (e.g. origin URL). Use `Repository::find_remote("origin")` and `remote.url()`.
+#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RemoteInfo {
+ pub name: String,
+ pub url: Option,
+ /// Browser-friendly URL (e.g. https://github.com/user/repo) derived from the remote URL.
+ pub web_url: Option,
+}
+
+#[derive(Clone)]
+pub struct GitService {
+ repo: Arc>>,
+ settings_service: Arc,
+}
+
+impl GitService {
+ pub fn new(settings_service: Arc) -> Self {
+ let service = Self {
+ repo: Arc::new(Mutex::new(None)),
+ settings_service,
+ };
+
+ // Initialize with current settings
+ let settings = service.settings_service.get_settings().clone();
+ service.sync_git(&settings);
+
+ // TODO: Initialize git pull?
+ // service.init_git_pull();
+
+ service
+ }
+
+ fn _init_git_pull(&self) {
+ let repo_clone = self.repo.clone();
+ let git_service_clone = self.clone();
+ tokio::spawn(async move {
+ if let Some(_) = repo_clone.lock().unwrap().as_ref() {
+ tracing::info!("[GitService] git initially pulling...");
+ if let Err(e) = git_service_clone.exec_pull() {
+ tracing::info!("[GitService] failed to pull doc: {}", e);
+ } else {
+ tracing::info!("[GitService] doc is updated!");
+ }
+ }
+ });
+ }
+
+ pub fn get_status(&self) -> Result {
+ let repo_guard = self.repo.lock().unwrap();
+ let repo = repo_guard
+ .as_ref()
+ .ok_or_else(|| anyhow::anyhow!("No git repository"))?;
+
+ let mut opts = StatusOptions::new();
+ opts.include_untracked(true);
+ opts.include_ignored(false);
+ opts.recurse_untracked_dirs(true);
+
+ let statuses = repo.statuses(Some(&mut opts))?;
+
+ let mut workspace: Vec = Vec::new();
+ let mut staged: Vec = Vec::new();
+
+ for entry in statuses.iter() {
+ let status = entry.status();
+ let path = entry.path().unwrap_or("").to_string();
+
+ // Check for untracked files (WT_NEW but not INDEX_NEW means untracked)
+ let is_untracked = status.contains(Status::WT_NEW) && !status.contains(Status::INDEX_NEW);
+ if is_untracked {
+ workspace.push(Change {
+ change_path: path.clone(),
+ status: StatusType::Untracked,
+ });
+ continue;
+ }
+
+ // Working directory changes (only if not already staged)
+ if status.contains(Status::WT_MODIFIED) && !status.contains(Status::INDEX_MODIFIED) {
+ workspace.push(Change {
+ change_path: path.clone(),
+ status: StatusType::Modified,
+ });
+ }
+ if status.contains(Status::WT_DELETED) && !status.contains(Status::INDEX_DELETED) {
+ workspace.push(Change {
+ change_path: path.clone(),
+ status: StatusType::Deleted,
+ });
+ }
+ if status.contains(Status::WT_RENAMED) && !status.contains(Status::INDEX_RENAMED) {
+ workspace.push(Change {
+ change_path: path.clone(),
+ status: StatusType::Rename,
+ });
+ }
+ if status.contains(Status::WT_TYPECHANGE) {
+ workspace.push(Change {
+ change_path: path.clone(),
+ status: StatusType::Modified,
+ });
+ }
+
+ // Staged changes
+ if status.contains(Status::INDEX_NEW) {
+ staged.push(Change {
+ change_path: path.clone(),
+ status: StatusType::Added,
+ });
+ }
+ if status.contains(Status::INDEX_MODIFIED) {
+ staged.push(Change {
+ change_path: path.clone(),
+ status: StatusType::Modified,
+ });
+ }
+ if status.contains(Status::INDEX_DELETED) {
+ staged.push(Change {
+ change_path: path.clone(),
+ status: StatusType::Deleted,
+ });
+ }
+ if status.contains(Status::INDEX_RENAMED) {
+ staged.push(Change {
+ change_path: path.clone(),
+ status: StatusType::Rename,
+ });
+ }
+ if status.contains(Status::INDEX_TYPECHANGE) {
+ staged.push(Change {
+ change_path: path.clone(),
+ status: StatusType::Modified,
+ });
+ }
+ }
+
+ Ok(GitStatus {
+ workspace,
+ staged,
+ changes: !statuses.is_empty(),
+ no_git: false,
+ remotes: Self::get_remote_info(&repo)?,
+ })
+ }
+
+ /// Get remote info (e.g. origin URL). Returns all configured remotes.
+ pub fn get_remote_info(repo: &Repository) -> Result, anyhow::Error> {
+ tracing::info!("[GitService] Getting remote info");
+
+ let names = repo.remotes()?;
+ tracing::info!(
+ "[GitService] Remotes: {:?}",
+ names.iter().flatten().collect::>()
+ );
+
+ let mut out = Vec::with_capacity(names.len());
+ for name in names.iter().flatten() {
+ let remote = repo.find_remote(name)?;
+ let url = remote.url().map(String::from);
+ out.push(RemoteInfo {
+ name: name.to_string(),
+ web_url: url.as_deref().and_then(Self::remote_url_to_web_url),
+ url,
+ });
+ }
+ Ok(out)
+ }
+
+ /// Converts a git remote URL to a browser-friendly web URL.
+ /// e.g. `https://github.com/user/repo.git` or `git@github.com:user/repo.git` → `https://github.com/user/repo`
+ fn remote_url_to_web_url(url: &str) -> Option {
+ let url = url.trim();
+ let without_dot_git = url.strip_suffix(".git").unwrap_or(url);
+ if let Some(rest) = without_dot_git.strip_prefix("https://") {
+ return Some(format!("https://{}", rest));
+ }
+ if let Some(rest) = without_dot_git.strip_prefix("http://") {
+ return Some(format!("http://{}", rest));
+ }
+ // SSH: git@host:path/repo → https://host/path/repo
+ if let Some(rest) = without_dot_git.strip_prefix("git@") {
+ if let Some(colon) = rest.find(':') {
+ let host = &rest[..colon];
+ let path = &rest[colon + 1..];
+ return Some(format!("https://{}/{}", host, path));
+ }
+ }
+ None
+ }
+
+ pub fn add(&self, change_paths: Vec) -> Result<(), anyhow::Error> {
+ let repo_guard = self.repo.lock().unwrap();
+ let repo = repo_guard
+ .as_ref()
+ .ok_or_else(|| anyhow::anyhow!("No git repository"))?;
+
+ let mut index = repo.index()?;
+
+ // Paths from frontend are relative to doc_root_path (which should be the git repo root)
+ // So we can add them directly
+ for path in change_paths {
+ let path_obj = std::path::Path::new(&path);
+ index.add_path(path_obj)?;
+ }
+
+ index.write()?;
+ Ok(())
+ }
+
+ /// Helper method to execute git commands with common validation and error handling
+ fn exec_git_command(
+ &self,
+ command: &str,
+ error_prefix: &str,
+ build_args: F,
+ ) -> Result<(), anyhow::Error>
+ where
+ F: FnOnce(&mut std::process::Command) -> &mut std::process::Command,
+ {
+ if !self.is_repo() {
+ return Err(anyhow::anyhow!("No git repository"));
+ }
+
+ let settings = self.settings_service.get_settings();
+ let git_root = &settings.doc_root_path;
+
+ if !git_root.exists() {
+ return Err(anyhow::anyhow!(
+ "Git root path does not exist: {:?}",
+ git_root
+ ));
+ }
+
+ let mut cmd = std::process::Command::new("git");
+ cmd.arg(command).current_dir(git_root);
+ build_args(&mut cmd);
+
+ let output = cmd.output()?;
+
+ if !output.status.success() {
+ let stderr = String::from_utf8_lossy(&output.stderr);
+ return Err(anyhow::anyhow!("{}: {}", error_prefix, stderr.trim()));
+ }
+
+ Ok(())
+ }
+
+ pub fn exec_commit(&self, message: String) -> Result<(), anyhow::Error> {
+ self.exec_git_command("commit", "Git commit failed", |cmd| {
+ cmd.arg("-m").arg(&message)
+ })?;
+ tracing::info!("[GitService] Executed commit with message: {}", message);
+ Ok(())
+ }
+
+ pub fn exec_push(&self) -> Result<(), anyhow::Error> {
+ self.exec_git_command("push", "Git push failed", |cmd| cmd)?;
+ tracing::info!("[GitService] Executed push");
+ Ok(())
+ }
+
+ pub fn pull(&self) -> Result<(), anyhow::Error> {
+ self.exec_pull()?;
+
+ Ok(())
+ }
+
+ fn exec_pull(&self) -> Result<(), anyhow::Error> {
+ self.exec_git_command("pull", "Git pull failed", |cmd| cmd)?;
+ tracing::info!("[GitService] Executed pull");
+ Ok(())
+ }
+
+ pub fn restore(&self, staged: bool, changes: Vec) -> Result<(), anyhow::Error> {
+ let settings = self.settings_service.get_settings();
+
+ if staged {
+ // Restore staged changes: git restore --staged
+ let paths: Vec<&str> = changes.iter().map(|c| c.change_path.as_str()).collect();
+ self.exec_git_command("restore", "Git restore --staged failed", |cmd| {
+ cmd.arg("--staged").args(&paths)
+ })?;
+ } else {
+ // Restore working directory changes
+ let untracked: Vec<_> = changes
+ .iter()
+ .filter(|c| matches!(c.status, StatusType::Untracked))
+ .collect();
+
+ // Delete untracked files
+ for change in &untracked {
+ let full_path = settings.doc_root_path.join(&change.change_path);
+ if full_path.exists() {
+ if full_path.is_file() {
+ std::fs::remove_file(&full_path)?;
+ } else {
+ std::fs::remove_dir_all(&full_path)?;
+ }
+ }
+ }
+
+ // Restore tracked files: git restore
+ let tracked: Vec<_> = changes
+ .iter()
+ .filter(|c| !matches!(c.status, StatusType::Untracked))
+ .collect();
+
+ if !tracked.is_empty() {
+ let paths: Vec<&str> = tracked.iter().map(|c| c.change_path.as_str()).collect();
+ self.exec_git_command("restore", "Git restore failed", |cmd| cmd.args(&paths))?;
+ }
+ }
+
+ Ok(())
+ }
+
+ pub fn sync_git(&self, settings: &Settings) {
+ let doc_root_path = &settings.doc_root_path;
+
+ // Log current user (for Windows service debugging)
+ #[cfg(target_os = "windows")]
+ {
+ if let Ok(username) = std::env::var("USERNAME") {
+ let userdomain = std::env::var("USERDOMAIN").unwrap_or_else(|_| "Unknown".to_string());
+ tracing::info!("[GitService] Running as user: {}\\{}", userdomain, username);
+ }
+
+ // Configure git to trust this directory (for LocalSystem and other service accounts)
+ if let Ok(mut config) = git2::Config::open_default() {
+ let repo_path_str = doc_root_path.to_string_lossy().replace("\\", "/");
+ match config.set_str("safe.directory", &repo_path_str) {
+ Ok(_) => {
+ tracing::info!(
+ "[GitService] Configured safe.directory for: {}",
+ repo_path_str
+ );
+ }
+ Err(e) => {
+ tracing::warn!("[GitService] Failed to configure safe.directory: {}", e);
+ }
+ }
+ }
+ }
+
+ tracing::info!(
+ "[GitService] Syncing git with doc root path: {:?}",
+ doc_root_path
+ );
+
+ let repo = if doc_root_path.exists() {
+ match Repository::open(doc_root_path) {
+ Ok(repo) => Some(repo),
+ Err(e) => {
+ tracing::info!(
+ "[GitService] Failed to open git repository at {:?}: {}",
+ doc_root_path,
+ e
+ );
+ None
+ }
+ }
+ } else {
+ None
+ };
+
+ *self.repo.lock().unwrap() = repo;
+ }
+
+ pub fn is_repo(&self) -> bool {
+ self.repo.lock().unwrap().is_some()
+ }
+}
diff --git a/crates/server/src/services/img.rs b/crates/server/src/services/img.rs
new file mode 100644
index 0000000..8b91718
--- /dev/null
+++ b/crates/server/src/services/img.rs
@@ -0,0 +1,267 @@
+use std::{
+ fs,
+ path::{Path, PathBuf},
+ sync::Arc,
+ time::UNIX_EPOCH,
+};
+
+use serde::Serialize;
+use sha2::{Digest, Sha256};
+
+use crate::services::settings::SettingsService;
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ImgItem {
+ pub file_name: String,
+ pub url: String,
+ pub created_time: u64,
+}
+
+const ASSETS_DIR: &str = "_assets";
+
+pub struct ImgService {
+ settings_service: Arc,
+}
+
+impl ImgService {
+ pub fn new(settings_service: Arc) -> Self {
+ Self { settings_service }
+ }
+
+ /// Reads an image from the doc workspace by relative path.
+ /// Returns (bytes, mime_type).
+ pub fn get_image(&self, img_path: &str) -> Result<(Vec, String), anyhow::Error> {
+ let settings = self.settings_service.get_settings();
+ let full_path = settings.doc_root_path.join(img_path);
+
+ if !full_path.exists() {
+ return Err(anyhow::anyhow!("Image not found: {}", img_path));
+ }
+
+ // Prevent directory traversal
+ if !full_path.starts_with(&settings.doc_root_path) {
+ return Err(anyhow::anyhow!("Invalid image path"));
+ }
+
+ let bytes = fs::read(&full_path)?;
+ let mime = Self::infer_mime(&full_path);
+
+ Ok((bytes, mime))
+ }
+
+ /// Saves an uploaded image to `{doc_root}/_assets/{hash}.{ext}`.
+ /// Uses SHA-256 content hash (first 16 hex chars) for deduplication:
+ /// - Same content already exists: returns existing path without writing.
+ /// - Hash collision with different content: appends `_1`, `_2`, etc.
+ pub fn upload_image(&self, file_name: &str, data: &[u8]) -> Result {
+ let settings = self.settings_service.get_settings();
+ let assets_dir = settings.doc_root_path.join(ASSETS_DIR);
+
+ fs::create_dir_all(&assets_dir)?;
+
+ let ext = Self::infer_extension(file_name, data);
+ let hash = Self::content_hash(data);
+ let dest_name = Self::resolve_hash_name(&assets_dir, &hash, ext, data)?;
+ let rel_url = format!("/{}/{}", ASSETS_DIR, dest_name);
+
+ Ok(rel_url)
+ }
+
+ pub fn list_images(&self) -> Result, anyhow::Error> {
+ let settings = self.settings_service.get_settings();
+ let assets_dir = settings.doc_root_path.join(ASSETS_DIR);
+
+ if !assets_dir.exists() {
+ tracing::info!(
+ "[ImgService] assets directory not found: {}",
+ assets_dir.display()
+ );
+ return Ok(Vec::new());
+ }
+
+ let mut images = Vec::new();
+ for entry in fs::read_dir(&assets_dir)? {
+ let entry = entry?;
+ let path = entry.path();
+ if !path.is_file() {
+ continue;
+ }
+
+ let ext = path
+ .extension()
+ .and_then(|e| e.to_str())
+ .unwrap_or("")
+ .to_lowercase();
+
+ if !matches!(
+ ext.as_str(),
+ "png" | "jpg" | "jpeg" | "gif" | "webp" | "svg" | "ico" | "bmp" | "avif"
+ ) {
+ continue;
+ }
+
+ let file_name = entry.file_name().to_string_lossy().to_string();
+ let url = format!("/{}/{}", ASSETS_DIR, file_name);
+ let created_time = entry
+ .metadata()
+ .and_then(|m| m.created())
+ .map(|t| t.duration_since(UNIX_EPOCH).unwrap_or_default().as_secs())
+ .unwrap_or(0);
+
+ images.push(ImgItem {
+ file_name,
+ url,
+ created_time,
+ });
+ }
+
+ images.sort_by(|a, b| b.created_time.cmp(&a.created_time));
+ Ok(images)
+ }
+
+ /// Deletes an image from the `_assets` directory by file name.
+ /// Only allows deleting files directly inside `_assets` to prevent traversal.
+ pub fn delete_image(&self, file_name: &str) -> Result<(), anyhow::Error> {
+ let settings = self.settings_service.get_settings();
+ let assets_dir = settings.doc_root_path.join(ASSETS_DIR);
+ let file_path = assets_dir.join(file_name);
+
+ if !file_path.starts_with(&assets_dir) {
+ return Err(anyhow::anyhow!("Invalid file name"));
+ }
+
+ if !file_path.exists() {
+ return Err(anyhow::anyhow!("Image not found: {}", file_name));
+ }
+
+ fs::remove_file(&file_path)?;
+ tracing::info!("[ImgService] Deleted image: {}", file_path.display());
+ Ok(())
+ }
+
+ fn content_hash(data: &[u8]) -> String {
+ let digest = Sha256::digest(data);
+ digest[..8].iter().map(|b| format!("{:02x}", b)).collect()
+ }
+
+ /// Finds the correct filename for the given hash:
+ /// - If no file with this hash exists, writes `{hash}.{ext}` and returns it.
+ /// - If a file exists with identical content, returns the existing name (dedup).
+ /// - If a file exists with different content (hash collision), tries `{hash}_1`, `{hash}_2`, etc.
+ fn resolve_hash_name(
+ assets_dir: &Path,
+ hash: &str,
+ ext: &str,
+ data: &[u8],
+ ) -> Result {
+ let base_name = format!("{}.{}", hash, ext);
+ let base_path = assets_dir.join(&base_name);
+
+ if !base_path.exists() {
+ fs::write(&base_path, data)?;
+ tracing::info!("[ImgService] Saved image: {}", base_path.display());
+ return Ok(base_name);
+ }
+
+ if Self::file_content_matches(&base_path, data)? {
+ tracing::info!("[ImgService] Dedup hit: {}", base_path.display());
+ return Ok(base_name);
+ }
+
+ for i in 1u32.. {
+ let candidate_name = format!("{}_{}.{}", hash, i, ext);
+ let candidate_path = assets_dir.join(&candidate_name);
+
+ if !candidate_path.exists() {
+ fs::write(&candidate_path, data)?;
+ tracing::info!(
+ "[ImgService] Saved image (collision): {}",
+ candidate_path.display()
+ );
+ return Ok(candidate_name);
+ }
+
+ if Self::file_content_matches(&candidate_path, data)? {
+ tracing::info!("[ImgService] Dedup hit: {}", candidate_path.display());
+ return Ok(candidate_name);
+ }
+ }
+
+ unreachable!()
+ }
+
+ fn file_content_matches(path: &PathBuf, data: &[u8]) -> Result {
+ let existing = fs::read(path)?;
+ Ok(existing == data)
+ }
+
+ /// Infers file extension from client filename if valid, else from content magic bytes.
+ fn infer_extension(file_name: &str, data: &[u8]) -> &'static str {
+ let path = Path::new(file_name);
+ if let Some(ext) = path.extension() {
+ let ext = ext.to_string_lossy().to_lowercase();
+ if matches!(
+ ext.as_str(),
+ "png" | "jpg" | "jpeg" | "gif" | "webp" | "svg" | "ico" | "bmp" | "avif"
+ ) {
+ return match ext.as_str() {
+ "jpg" | "jpeg" => "jpg",
+ "png" => "png",
+ "gif" => "gif",
+ "webp" => "webp",
+ "svg" => "svg",
+ "ico" => "ico",
+ "bmp" => "bmp",
+ "avif" => "avif",
+ _ => Self::infer_extension_from_magic_bytes(data),
+ };
+ }
+ }
+ Self::infer_extension_from_magic_bytes(data)
+ }
+
+ /// Infers extension from image magic bytes (for pasted images with no extension).
+ fn infer_extension_from_magic_bytes(data: &[u8]) -> &'static str {
+ if data.len() < 12 {
+ return "png"; // fallback
+ }
+ if data[0..8] == [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A] {
+ return "png";
+ }
+ if data.len() >= 3 && data[0..3] == [0xFF, 0xD8, 0xFF] {
+ return "jpg";
+ }
+ if data.len() >= 6 && (data.starts_with(b"GIF87a") || data.starts_with(b"GIF89a")) {
+ return "gif";
+ }
+ if data.len() >= 12 && &data[0..4] == b"RIFF" && &data[8..12] == b"WEBP" {
+ return "webp";
+ }
+ if data.len() >= 5 && data[0] == b'<' && (&data[1..4] == b"svg" || &data[1..5] == b"?xml") {
+ return "svg";
+ }
+ "png" // fallback
+ }
+
+ fn infer_mime(path: &Path) -> String {
+ let ext = path
+ .extension()
+ .and_then(|e| e.to_str())
+ .unwrap_or("")
+ .to_lowercase();
+
+ match ext.as_str() {
+ "png" => "image/png",
+ "jpg" | "jpeg" => "image/jpeg",
+ "gif" => "image/gif",
+ "webp" => "image/webp",
+ "svg" => "image/svg+xml",
+ "ico" => "image/x-icon",
+ "bmp" => "image/bmp",
+ "avif" => "image/avif",
+ _ => "application/octet-stream",
+ }
+ .to_string()
+ }
+}
diff --git a/crates/server/src/services/mod.rs b/crates/server/src/services/mod.rs
new file mode 100644
index 0000000..90afe1a
--- /dev/null
+++ b/crates/server/src/services/mod.rs
@@ -0,0 +1,5 @@
+pub mod doc;
+pub mod git;
+pub mod img;
+pub mod search;
+pub mod settings;
diff --git a/crates/server/src/services/search.rs b/crates/server/src/services/search.rs
new file mode 100644
index 0000000..8f39ca2
--- /dev/null
+++ b/crates/server/src/services/search.rs
@@ -0,0 +1,237 @@
+use std::{
+ path::{Path, PathBuf},
+ sync::{Arc, Mutex},
+};
+
+use grep_regex::RegexMatcherBuilder;
+use grep_searcher::{Searcher, sinks::UTF8};
+use ignore::WalkBuilder;
+use serde::Serialize;
+
+use crate::services::settings::SettingsService;
+
+const INTERNAL_IGNORE_DIRS: &[&str] = &["_assets"];
+
+#[derive(Debug, Clone, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct FileNameMatch {
+ pub name: String,
+ pub path: Vec,
+}
+
+#[derive(Debug, Clone, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct LineMatch {
+ pub line_number: u64,
+ pub line_content: String,
+}
+
+#[derive(Debug, Clone, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct FileContentMatches {
+ pub name: String,
+ pub path: Vec,
+ pub matches: Vec,
+}
+
+pub struct SearchService {
+ ignore_dirs: Arc